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