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