1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2015 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "nghttp2_http.h"
26
27 #include <string.h>
28 #include <assert.h>
29 #include <stdio.h>
30
31 #include "nghttp2_hd.h"
32 #include "nghttp2_helper.h"
33
downcase(uint8_t c)34 static uint8_t downcase(uint8_t c) {
35 return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
36 }
37
memieq(const void * a,const void * b,size_t n)38 static int memieq(const void *a, const void *b, size_t n) {
39 size_t i;
40 const uint8_t *aa = a, *bb = b;
41
42 for (i = 0; i < n; ++i) {
43 if (downcase(aa[i]) != downcase(bb[i])) {
44 return 0;
45 }
46 }
47 return 1;
48 }
49
50 #define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
51
parse_uint(const uint8_t * s,size_t len)52 static int64_t parse_uint(const uint8_t *s, size_t len) {
53 int64_t n = 0;
54 size_t i;
55 if (len == 0) {
56 return -1;
57 }
58 for (i = 0; i < len; ++i) {
59 if ('0' <= s[i] && s[i] <= '9') {
60 if (n > INT64_MAX / 10) {
61 return -1;
62 }
63 n *= 10;
64 if (n > INT64_MAX - (s[i] - '0')) {
65 return -1;
66 }
67 n += s[i] - '0';
68 continue;
69 }
70 return -1;
71 }
72 return n;
73 }
74
lws(const uint8_t * s,size_t n)75 static int lws(const uint8_t *s, size_t n) {
76 size_t i;
77 for (i = 0; i < n; ++i) {
78 if (s[i] != ' ' && s[i] != '\t') {
79 return 0;
80 }
81 }
82 return 1;
83 }
84
check_pseudo_header(nghttp2_stream * stream,const nghttp2_hd_nv * nv,int flag)85 static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
86 int flag) {
87 if (stream->http_flags & flag) {
88 return 0;
89 }
90 if (lws(nv->value->base, nv->value->len)) {
91 return 0;
92 }
93 stream->http_flags = (uint16_t)(stream->http_flags | flag);
94 return 1;
95 }
96
expect_response_body(nghttp2_stream * stream)97 static int expect_response_body(nghttp2_stream *stream) {
98 return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
99 stream->status_code / 100 != 1 && stream->status_code != 304 &&
100 stream->status_code != 204;
101 }
102
103 /* For "http" or "https" URIs, OPTIONS request may have "*" in :path
104 header field to represent system-wide OPTIONS request. Otherwise,
105 :path header field value must start with "/". This function must
106 be called after ":method" header field was received. This function
107 returns nonzero if path is valid.*/
check_path(nghttp2_stream * stream)108 static int check_path(nghttp2_stream *stream) {
109 return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
110 ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
111 ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
112 (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
113 }
114
http_request_on_header(nghttp2_stream * stream,nghttp2_hd_nv * nv,int trailer,int connect_protocol)115 static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
116 int trailer, int connect_protocol) {
117 if (nv->name->base[0] == ':') {
118 if (trailer ||
119 (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
120 return NGHTTP2_ERR_HTTP_HEADER;
121 }
122 }
123
124 switch (nv->token) {
125 case NGHTTP2_TOKEN__AUTHORITY:
126 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
127 return NGHTTP2_ERR_HTTP_HEADER;
128 }
129 break;
130 case NGHTTP2_TOKEN__METHOD:
131 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
132 return NGHTTP2_ERR_HTTP_HEADER;
133 }
134 switch (nv->value->len) {
135 case 4:
136 if (lstreq("HEAD", nv->value->base, nv->value->len)) {
137 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
138 }
139 break;
140 case 7:
141 switch (nv->value->base[6]) {
142 case 'T':
143 if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
144 if (stream->stream_id % 2 == 0) {
145 /* we won't allow CONNECT for push */
146 return NGHTTP2_ERR_HTTP_HEADER;
147 }
148 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
149 }
150 break;
151 case 'S':
152 if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
153 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
154 }
155 break;
156 }
157 break;
158 }
159 break;
160 case NGHTTP2_TOKEN__PATH:
161 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
162 return NGHTTP2_ERR_HTTP_HEADER;
163 }
164 if (nv->value->base[0] == '/') {
165 stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
166 } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
167 stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
168 }
169 break;
170 case NGHTTP2_TOKEN__SCHEME:
171 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
172 return NGHTTP2_ERR_HTTP_HEADER;
173 }
174 if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
175 (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
176 stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
177 }
178 break;
179 case NGHTTP2_TOKEN__PROTOCOL:
180 if (!connect_protocol) {
181 return NGHTTP2_ERR_HTTP_HEADER;
182 }
183
184 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
185 return NGHTTP2_ERR_HTTP_HEADER;
186 }
187 break;
188 case NGHTTP2_TOKEN_HOST:
189 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
190 return NGHTTP2_ERR_HTTP_HEADER;
191 }
192 break;
193 case NGHTTP2_TOKEN_CONTENT_LENGTH: {
194 if (stream->content_length != -1) {
195 return NGHTTP2_ERR_HTTP_HEADER;
196 }
197 stream->content_length = parse_uint(nv->value->base, nv->value->len);
198 if (stream->content_length == -1) {
199 return NGHTTP2_ERR_HTTP_HEADER;
200 }
201 break;
202 }
203 /* disallowed header fields */
204 case NGHTTP2_TOKEN_CONNECTION:
205 case NGHTTP2_TOKEN_KEEP_ALIVE:
206 case NGHTTP2_TOKEN_PROXY_CONNECTION:
207 case NGHTTP2_TOKEN_TRANSFER_ENCODING:
208 case NGHTTP2_TOKEN_UPGRADE:
209 return NGHTTP2_ERR_HTTP_HEADER;
210 case NGHTTP2_TOKEN_TE:
211 if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
212 return NGHTTP2_ERR_HTTP_HEADER;
213 }
214 break;
215 default:
216 if (nv->name->base[0] == ':') {
217 return NGHTTP2_ERR_HTTP_HEADER;
218 }
219 }
220
221 if (nv->name->base[0] != ':') {
222 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
223 }
224
225 return 0;
226 }
227
http_response_on_header(nghttp2_stream * stream,nghttp2_hd_nv * nv,int trailer)228 static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
229 int trailer) {
230 if (nv->name->base[0] == ':') {
231 if (trailer ||
232 (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
233 return NGHTTP2_ERR_HTTP_HEADER;
234 }
235 }
236
237 switch (nv->token) {
238 case NGHTTP2_TOKEN__STATUS: {
239 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
240 return NGHTTP2_ERR_HTTP_HEADER;
241 }
242 if (nv->value->len != 3) {
243 return NGHTTP2_ERR_HTTP_HEADER;
244 }
245 stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
246 if (stream->status_code == -1 || stream->status_code == 101) {
247 return NGHTTP2_ERR_HTTP_HEADER;
248 }
249 break;
250 }
251 case NGHTTP2_TOKEN_CONTENT_LENGTH: {
252 if (stream->status_code == 204) {
253 /* content-length header field in 204 response is prohibited by
254 RFC 7230. But some widely used servers send content-length:
255 0. Until they get fixed, we ignore it. */
256 if (stream->content_length != -1) {
257 /* Found multiple content-length field */
258 return NGHTTP2_ERR_HTTP_HEADER;
259 }
260 if (!lstrieq("0", nv->value->base, nv->value->len)) {
261 return NGHTTP2_ERR_HTTP_HEADER;
262 }
263 stream->content_length = 0;
264 return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
265 }
266 if (stream->status_code / 100 == 1) {
267 return NGHTTP2_ERR_HTTP_HEADER;
268 }
269 /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
270 if (stream->status_code / 100 == 2 &&
271 (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
272 return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273 }
274 if (stream->content_length != -1) {
275 return NGHTTP2_ERR_HTTP_HEADER;
276 }
277 stream->content_length = parse_uint(nv->value->base, nv->value->len);
278 if (stream->content_length == -1) {
279 return NGHTTP2_ERR_HTTP_HEADER;
280 }
281 break;
282 }
283 /* disallowed header fields */
284 case NGHTTP2_TOKEN_CONNECTION:
285 case NGHTTP2_TOKEN_KEEP_ALIVE:
286 case NGHTTP2_TOKEN_PROXY_CONNECTION:
287 case NGHTTP2_TOKEN_TRANSFER_ENCODING:
288 case NGHTTP2_TOKEN_UPGRADE:
289 return NGHTTP2_ERR_HTTP_HEADER;
290 case NGHTTP2_TOKEN_TE:
291 if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
292 return NGHTTP2_ERR_HTTP_HEADER;
293 }
294 break;
295 default:
296 if (nv->name->base[0] == ':') {
297 return NGHTTP2_ERR_HTTP_HEADER;
298 }
299 }
300
301 if (nv->name->base[0] != ':') {
302 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
303 }
304
305 return 0;
306 }
307
check_scheme(const uint8_t * value,size_t len)308 static int check_scheme(const uint8_t *value, size_t len) {
309 const uint8_t *last;
310 if (len == 0) {
311 return 0;
312 }
313
314 if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
315 return 0;
316 }
317
318 last = value + len;
319 ++value;
320
321 for (; value != last; ++value) {
322 if (!(('A' <= *value && *value <= 'Z') ||
323 ('a' <= *value && *value <= 'z') ||
324 ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
325 *value == '.')) {
326 return 0;
327 }
328 }
329 return 1;
330 }
331
nghttp2_http_on_header(nghttp2_session * session,nghttp2_stream * stream,nghttp2_frame * frame,nghttp2_hd_nv * nv,int trailer)332 int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
333 nghttp2_frame *frame, nghttp2_hd_nv *nv,
334 int trailer) {
335 int rv;
336
337 /* We are strict for pseudo header field. One bad character should
338 lead to fail. OTOH, we should be a bit forgiving for regular
339 headers, since existing public internet has so much illegal
340 headers floating around and if we kill the stream because of
341 this, we may disrupt many web sites and/or libraries. So we
342 become conservative here, and just ignore those illegal regular
343 headers. */
344 if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
345 size_t i;
346 if (nv->name->len > 0 && nv->name->base[0] == ':') {
347 return NGHTTP2_ERR_HTTP_HEADER;
348 }
349 /* header field name must be lower-cased without exception */
350 for (i = 0; i < nv->name->len; ++i) {
351 uint8_t c = nv->name->base[i];
352 if ('A' <= c && c <= 'Z') {
353 return NGHTTP2_ERR_HTTP_HEADER;
354 }
355 }
356 /* When ignoring regular headers, we set this flag so that we
357 still enforce header field ordering rule for pseudo header
358 fields. */
359 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
360 return NGHTTP2_ERR_IGN_HTTP_HEADER;
361 }
362
363 if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
364 nv->token == NGHTTP2_TOKEN_HOST) {
365 rv = nghttp2_check_authority(nv->value->base, nv->value->len);
366 } else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
367 rv = check_scheme(nv->value->base, nv->value->len);
368 } else {
369 rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
370 }
371
372 if (rv == 0) {
373 assert(nv->name->len > 0);
374 if (nv->name->base[0] == ':') {
375 return NGHTTP2_ERR_HTTP_HEADER;
376 }
377 /* When ignoring regular headers, we set this flag so that we
378 still enforce header field ordering rule for pseudo header
379 fields. */
380 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
381 return NGHTTP2_ERR_IGN_HTTP_HEADER;
382 }
383
384 if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
385 return http_request_on_header(stream, nv, trailer,
386 session->server &&
387 session->pending_enable_connect_protocol);
388 }
389
390 return http_response_on_header(stream, nv, trailer);
391 }
392
nghttp2_http_on_request_headers(nghttp2_stream * stream,nghttp2_frame * frame)393 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
394 nghttp2_frame *frame) {
395 if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
396 (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
397 if ((stream->http_flags &
398 (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
399 (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
400 return -1;
401 }
402 stream->content_length = -1;
403 } else {
404 if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
405 NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
406 (stream->http_flags &
407 (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
408 return -1;
409 }
410 if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
411 ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
412 (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
413 return -1;
414 }
415 if (!check_path(stream)) {
416 return -1;
417 }
418 }
419
420 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
421 /* we are going to reuse data fields for upcoming response. Clear
422 them now, except for method flags. */
423 stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
424 stream->content_length = -1;
425 }
426
427 return 0;
428 }
429
nghttp2_http_on_response_headers(nghttp2_stream * stream)430 int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
431 if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
432 return -1;
433 }
434
435 if (stream->status_code / 100 == 1) {
436 /* non-final response */
437 stream->http_flags =
438 (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
439 NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
440 stream->content_length = -1;
441 stream->status_code = -1;
442 return 0;
443 }
444
445 stream->http_flags =
446 (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
447
448 if (!expect_response_body(stream)) {
449 stream->content_length = 0;
450 } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
451 NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
452 stream->content_length = -1;
453 }
454
455 return 0;
456 }
457
nghttp2_http_on_trailer_headers(nghttp2_stream * stream,nghttp2_frame * frame)458 int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
459 nghttp2_frame *frame) {
460 (void)stream;
461
462 if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
463 return -1;
464 }
465
466 return 0;
467 }
468
nghttp2_http_on_remote_end_stream(nghttp2_stream * stream)469 int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
470 if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
471 return -1;
472 }
473
474 if (stream->content_length != -1 &&
475 stream->content_length != stream->recv_content_length) {
476 return -1;
477 }
478
479 return 0;
480 }
481
nghttp2_http_on_data_chunk(nghttp2_stream * stream,size_t n)482 int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
483 stream->recv_content_length += (int64_t)n;
484
485 if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
486 (stream->content_length != -1 &&
487 stream->recv_content_length > stream->content_length)) {
488 return -1;
489 }
490
491 return 0;
492 }
493
nghttp2_http_record_request_method(nghttp2_stream * stream,nghttp2_frame * frame)494 void nghttp2_http_record_request_method(nghttp2_stream *stream,
495 nghttp2_frame *frame) {
496 const nghttp2_nv *nva;
497 size_t nvlen;
498 size_t i;
499
500 switch (frame->hd.type) {
501 case NGHTTP2_HEADERS:
502 nva = frame->headers.nva;
503 nvlen = frame->headers.nvlen;
504 break;
505 case NGHTTP2_PUSH_PROMISE:
506 nva = frame->push_promise.nva;
507 nvlen = frame->push_promise.nvlen;
508 break;
509 default:
510 return;
511 }
512
513 /* TODO we should do this strictly. */
514 for (i = 0; i < nvlen; ++i) {
515 const nghttp2_nv *nv = &nva[i];
516 if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
517 memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
518 continue;
519 }
520 if (lstreq("CONNECT", nv->value, nv->valuelen)) {
521 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
522 return;
523 }
524 if (lstreq("HEAD", nv->value, nv->valuelen)) {
525 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
526 return;
527 }
528 return;
529 }
530 }
531