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