xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/spdy/core/spdy_alt_svc_wire_format.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/spdy/core/spdy_alt_svc_wire_format.h"
6 
7 #include <algorithm>
8 #include <cctype>
9 #include <cstdint>
10 #include <limits>
11 #include <string>
12 #include <utility>
13 
14 #include "absl/strings/str_cat.h"
15 #include "absl/strings/string_view.h"
16 #include "quiche/common/platform/api/quiche_logging.h"
17 
18 namespace spdy {
19 
20 namespace {
21 
22 template <class T>
ParsePositiveIntegerImpl(absl::string_view::const_iterator c,absl::string_view::const_iterator end,T * value)23 bool ParsePositiveIntegerImpl(absl::string_view::const_iterator c,
24                               absl::string_view::const_iterator end, T* value) {
25   *value = 0;
26   for (; c != end && std::isdigit(*c); ++c) {
27     if (*value > std::numeric_limits<T>::max() / 10) {
28       return false;
29     }
30     *value *= 10;
31     if (*value > std::numeric_limits<T>::max() - (*c - '0')) {
32       return false;
33     }
34     *value += *c - '0';
35   }
36   return (c == end && *value > 0);
37 }
38 
39 }  // namespace
40 
41 SpdyAltSvcWireFormat::AlternativeService::AlternativeService() = default;
42 
AlternativeService(const std::string & protocol_id,const std::string & host,uint16_t port,uint32_t max_age_seconds,VersionVector version)43 SpdyAltSvcWireFormat::AlternativeService::AlternativeService(
44     const std::string& protocol_id, const std::string& host, uint16_t port,
45     uint32_t max_age_seconds, VersionVector version)
46     : protocol_id(protocol_id),
47       host(host),
48       port(port),
49       max_age_seconds(max_age_seconds),
50       version(std::move(version)) {}
51 
52 SpdyAltSvcWireFormat::AlternativeService::~AlternativeService() = default;
53 
54 SpdyAltSvcWireFormat::AlternativeService::AlternativeService(
55     const AlternativeService& other) = default;
56 
57 // static
ParseHeaderFieldValue(absl::string_view value,AlternativeServiceVector * altsvc_vector)58 bool SpdyAltSvcWireFormat::ParseHeaderFieldValue(
59     absl::string_view value, AlternativeServiceVector* altsvc_vector) {
60   // Empty value is invalid according to the specification.
61   if (value.empty()) {
62     return false;
63   }
64   altsvc_vector->clear();
65   if (value == absl::string_view("clear")) {
66     return true;
67   }
68   absl::string_view::const_iterator c = value.begin();
69   while (c != value.end()) {
70     // Parse protocol-id.
71     absl::string_view::const_iterator percent_encoded_protocol_id_end =
72         std::find(c, value.end(), '=');
73     std::string protocol_id;
74     if (percent_encoded_protocol_id_end == c ||
75         !PercentDecode(c, percent_encoded_protocol_id_end, &protocol_id)) {
76       return false;
77     }
78     // Check for IETF format for advertising QUIC:
79     // hq=":443";quic=51303338;quic=51303334
80     const bool is_ietf_format_quic = (protocol_id == "hq");
81     c = percent_encoded_protocol_id_end;
82     if (c == value.end()) {
83       return false;
84     }
85     // Parse alt-authority.
86     QUICHE_DCHECK_EQ('=', *c);
87     ++c;
88     if (c == value.end() || *c != '"') {
89       return false;
90     }
91     ++c;
92     absl::string_view::const_iterator alt_authority_begin = c;
93     for (; c != value.end() && *c != '"'; ++c) {
94       // Decode backslash encoding.
95       if (*c != '\\') {
96         continue;
97       }
98       ++c;
99       if (c == value.end()) {
100         return false;
101       }
102     }
103     if (c == alt_authority_begin || c == value.end()) {
104       return false;
105     }
106     QUICHE_DCHECK_EQ('"', *c);
107     std::string host;
108     uint16_t port;
109     if (!ParseAltAuthority(alt_authority_begin, c, &host, &port)) {
110       return false;
111     }
112     ++c;
113     // Parse parameters.
114     uint32_t max_age_seconds = 86400;
115     VersionVector version;
116     absl::string_view::const_iterator parameters_end =
117         std::find(c, value.end(), ',');
118     while (c != parameters_end) {
119       SkipWhiteSpace(&c, parameters_end);
120       if (c == parameters_end) {
121         break;
122       }
123       if (*c != ';') {
124         return false;
125       }
126       ++c;
127       SkipWhiteSpace(&c, parameters_end);
128       if (c == parameters_end) {
129         break;
130       }
131       std::string parameter_name;
132       for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) {
133         parameter_name.push_back(tolower(*c));
134       }
135       SkipWhiteSpace(&c, parameters_end);
136       if (c == parameters_end || *c != '=') {
137         return false;
138       }
139       ++c;
140       SkipWhiteSpace(&c, parameters_end);
141       absl::string_view::const_iterator parameter_value_begin = c;
142       for (; c != parameters_end && *c != ';' && *c != ' ' && *c != '\t'; ++c) {
143       }
144       if (c == parameter_value_begin) {
145         return false;
146       }
147       if (parameter_name == "ma") {
148         if (!ParsePositiveInteger32(parameter_value_begin, c,
149                                     &max_age_seconds)) {
150           return false;
151         }
152       } else if (!is_ietf_format_quic && parameter_name == "v") {
153         // Version is a comma separated list of positive integers enclosed in
154         // quotation marks.  Since it can contain commas, which are not
155         // delineating alternative service entries, |parameters_end| and |c| can
156         // be invalid.
157         if (*parameter_value_begin != '"') {
158           return false;
159         }
160         c = std::find(parameter_value_begin + 1, value.end(), '"');
161         if (c == value.end()) {
162           return false;
163         }
164         ++c;
165         parameters_end = std::find(c, value.end(), ',');
166         absl::string_view::const_iterator v_begin = parameter_value_begin + 1;
167         while (v_begin < c) {
168           absl::string_view::const_iterator v_end = v_begin;
169           while (v_end < c - 1 && *v_end != ',') {
170             ++v_end;
171           }
172           uint16_t v;
173           if (!ParsePositiveInteger16(v_begin, v_end, &v)) {
174             return false;
175           }
176           version.push_back(v);
177           v_begin = v_end + 1;
178           if (v_begin == c - 1) {
179             // List ends in comma.
180             return false;
181           }
182         }
183       } else if (is_ietf_format_quic && parameter_name == "quic") {
184         // IETF format for advertising QUIC. Version is hex encoding of QUIC
185         // version tag. Hex-encoded string should not include leading "0x" or
186         // leading zeros.
187         // Example for advertising QUIC versions "Q038" and "Q034":
188         // hq=":443";quic=51303338;quic=51303334
189         if (*parameter_value_begin == '0') {
190           return false;
191         }
192         // Versions will be stored as the uint32_t hex decoding of the param
193         // value string. Example: QUIC version "Q038", which is advertised as:
194         // hq=":443";quic=51303338
195         // ... will be stored in |versions| as 0x51303338.
196         uint32_t quic_version;
197         if (!HexDecodeToUInt32(absl::string_view(&*parameter_value_begin,
198                                                  c - parameter_value_begin),
199                                &quic_version) ||
200             quic_version == 0) {
201           return false;
202         }
203         version.push_back(quic_version);
204       }
205     }
206     altsvc_vector->emplace_back(protocol_id, host, port, max_age_seconds,
207                                 version);
208     for (; c != value.end() && (*c == ' ' || *c == '\t' || *c == ','); ++c) {
209     }
210   }
211   return true;
212 }
213 
214 // static
SerializeHeaderFieldValue(const AlternativeServiceVector & altsvc_vector)215 std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue(
216     const AlternativeServiceVector& altsvc_vector) {
217   if (altsvc_vector.empty()) {
218     return std::string("clear");
219   }
220   const char kNibbleToHex[] = "0123456789ABCDEF";
221   std::string value;
222   for (const AlternativeService& altsvc : altsvc_vector) {
223     if (!value.empty()) {
224       value.push_back(',');
225     }
226     // Check for IETF format for advertising QUIC.
227     const bool is_ietf_format_quic = (altsvc.protocol_id == "hq");
228     // Percent escape protocol id according to
229     // http://tools.ietf.org/html/rfc7230#section-3.2.6.
230     for (char c : altsvc.protocol_id) {
231       if (isalnum(c)) {
232         value.push_back(c);
233         continue;
234       }
235       switch (c) {
236         case '!':
237         case '#':
238         case '$':
239         case '&':
240         case '\'':
241         case '*':
242         case '+':
243         case '-':
244         case '.':
245         case '^':
246         case '_':
247         case '`':
248         case '|':
249         case '~':
250           value.push_back(c);
251           break;
252         default:
253           value.push_back('%');
254           // Network byte order is big-endian.
255           value.push_back(kNibbleToHex[c >> 4]);
256           value.push_back(kNibbleToHex[c & 0x0f]);
257           break;
258       }
259     }
260     value.push_back('=');
261     value.push_back('"');
262     for (char c : altsvc.host) {
263       if (c == '"' || c == '\\') {
264         value.push_back('\\');
265       }
266       value.push_back(c);
267     }
268     absl::StrAppend(&value, ":", altsvc.port, "\"");
269     if (altsvc.max_age_seconds != 86400) {
270       absl::StrAppend(&value, "; ma=", altsvc.max_age_seconds);
271     }
272     if (!altsvc.version.empty()) {
273       if (is_ietf_format_quic) {
274         for (uint32_t quic_version : altsvc.version) {
275           absl::StrAppend(&value, "; quic=", absl::Hex(quic_version));
276         }
277       } else {
278         value.append("; v=\"");
279         for (auto it = altsvc.version.begin(); it != altsvc.version.end();
280              ++it) {
281           if (it != altsvc.version.begin()) {
282             value.append(",");
283           }
284           absl::StrAppend(&value, *it);
285         }
286         value.append("\"");
287       }
288     }
289   }
290   return value;
291 }
292 
293 // static
SkipWhiteSpace(absl::string_view::const_iterator * c,absl::string_view::const_iterator end)294 void SpdyAltSvcWireFormat::SkipWhiteSpace(
295     absl::string_view::const_iterator* c,
296     absl::string_view::const_iterator end) {
297   for (; *c != end && (**c == ' ' || **c == '\t'); ++*c) {
298   }
299 }
300 
301 // static
PercentDecode(absl::string_view::const_iterator c,absl::string_view::const_iterator end,std::string * output)302 bool SpdyAltSvcWireFormat::PercentDecode(absl::string_view::const_iterator c,
303                                          absl::string_view::const_iterator end,
304                                          std::string* output) {
305   output->clear();
306   for (; c != end; ++c) {
307     if (*c != '%') {
308       output->push_back(*c);
309       continue;
310     }
311     QUICHE_DCHECK_EQ('%', *c);
312     ++c;
313     if (c == end || !std::isxdigit(*c)) {
314       return false;
315     }
316     // Network byte order is big-endian.
317     char decoded = HexDigitToInt(*c) << 4;
318     ++c;
319     if (c == end || !std::isxdigit(*c)) {
320       return false;
321     }
322     decoded += HexDigitToInt(*c);
323     output->push_back(decoded);
324   }
325   return true;
326 }
327 
328 // static
ParseAltAuthority(absl::string_view::const_iterator c,absl::string_view::const_iterator end,std::string * host,uint16_t * port)329 bool SpdyAltSvcWireFormat::ParseAltAuthority(
330     absl::string_view::const_iterator c, absl::string_view::const_iterator end,
331     std::string* host, uint16_t* port) {
332   host->clear();
333   if (c == end) {
334     return false;
335   }
336   if (*c == '[') {
337     for (; c != end && *c != ']'; ++c) {
338       if (*c == '"') {
339         // Port is mandatory.
340         return false;
341       }
342       host->push_back(*c);
343     }
344     if (c == end) {
345       return false;
346     }
347     QUICHE_DCHECK_EQ(']', *c);
348     host->push_back(*c);
349     ++c;
350   } else {
351     for (; c != end && *c != ':'; ++c) {
352       if (*c == '"') {
353         // Port is mandatory.
354         return false;
355       }
356       if (*c == '\\') {
357         ++c;
358         if (c == end) {
359           return false;
360         }
361       }
362       host->push_back(*c);
363     }
364   }
365   if (c == end || *c != ':') {
366     return false;
367   }
368   QUICHE_DCHECK_EQ(':', *c);
369   ++c;
370   return ParsePositiveInteger16(c, end, port);
371 }
372 
373 // static
ParsePositiveInteger16(absl::string_view::const_iterator c,absl::string_view::const_iterator end,uint16_t * value)374 bool SpdyAltSvcWireFormat::ParsePositiveInteger16(
375     absl::string_view::const_iterator c, absl::string_view::const_iterator end,
376     uint16_t* value) {
377   return ParsePositiveIntegerImpl<uint16_t>(c, end, value);
378 }
379 
380 // static
ParsePositiveInteger32(absl::string_view::const_iterator c,absl::string_view::const_iterator end,uint32_t * value)381 bool SpdyAltSvcWireFormat::ParsePositiveInteger32(
382     absl::string_view::const_iterator c, absl::string_view::const_iterator end,
383     uint32_t* value) {
384   return ParsePositiveIntegerImpl<uint32_t>(c, end, value);
385 }
386 
387 // static
HexDigitToInt(char c)388 char SpdyAltSvcWireFormat::HexDigitToInt(char c) {
389   QUICHE_DCHECK(std::isxdigit(c));
390 
391   if (std::isdigit(c)) {
392     return c - '0';
393   }
394   if (c >= 'A' && c <= 'F') {
395     return c - 'A' + 10;
396   }
397   if (c >= 'a' && c <= 'f') {
398     return c - 'a' + 10;
399   }
400 
401   return 0;
402 }
403 
404 // static
HexDecodeToUInt32(absl::string_view data,uint32_t * value)405 bool SpdyAltSvcWireFormat::HexDecodeToUInt32(absl::string_view data,
406                                              uint32_t* value) {
407   if (data.empty() || data.length() > 8u) {
408     return false;
409   }
410 
411   *value = 0;
412   for (char c : data) {
413     if (!std::isxdigit(c)) {
414       return false;
415     }
416 
417     *value <<= 4;
418     *value += HexDigitToInt(c);
419   }
420 
421   return true;
422 }
423 
424 }  // namespace spdy
425