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