xref: /lighttpd1.4/src/t/test_request.c (revision 70b1af06)
1 #include "first.h"
2 
3 #undef NDEBUG
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 
9 #include "request.c"
10 
11 static void test_request_reset(request_st * const r)
12 {
13     r->http_method = HTTP_METHOD_UNSET;
14     r->http_version = HTTP_VERSION_UNSET;
15     r->http_host = NULL;
16     r->rqst_htags = 0;
17     r->reqbody_length = 0;
18     buffer_clear(&r->target_orig);
19     buffer_clear(&r->target);
20     array_reset_data_strings(&r->rqst_headers);
21 }
22 
23 static void run_http_request_parse(request_st * const r, int line, int status, const char *desc, const char *req, size_t reqlen)
24 {
25     unsigned short hloffsets[32];
26     char hdrs[1024];
27     test_request_reset(r);
28     assert(reqlen < sizeof(hdrs));
29     memcpy(hdrs, req, reqlen);
30     hloffsets[0] = 1;
31     hloffsets[1] = 0;
32     for (const char *n=req, *end=req+reqlen; (n=memchr(n,'\n',end-n)); ++n) {
33         if (++hloffsets[0] >= sizeof(hloffsets)/sizeof(*hloffsets)) break;
34         hloffsets[hloffsets[0]] = n - req + 1;
35     }
36     --hloffsets[0]; /*(ignore final blank line "\r\n" ending headers)*/
37     const int proto_default_port = 80;
38     int http_status = http_request_parse(r,hdrs,hloffsets,proto_default_port);
39     if (http_status != status) {
40         fprintf(stderr,
41                 "%s.%d: %s() failed: expected '%d', got '%d' for test %s\n",
42                 __FILE__, line, "http_request_parse", status, http_status,
43                 desc);
44         fflush(stderr);
45         abort();
46     }
47 }
48 
49 static void test_request_http_request_parse(request_st * const r)
50 {
51     data_string *ds;
52 
53     run_http_request_parse(r, __LINE__, 400,
54       "invalid request-line: space",
55       CONST_STR_LEN(" \r\n"
56                     "Host: www.example.org\r\n"
57                     "\r\n"));
58 
59     run_http_request_parse(r, __LINE__, 400,
60       "invalid request-line: space, char",
61       CONST_STR_LEN(" a\r\n"
62                     "Host: www.example.org\r\n"
63                     "\r\n"));
64 
65     run_http_request_parse(r, __LINE__, 400,
66       "invalid request-line: dot",
67       CONST_STR_LEN(".\r\n"
68                     "Host: www.example.org\r\n"
69                     "\r\n"));
70 
71     run_http_request_parse(r, __LINE__, 400,
72       "invalid request-line: single char",
73       CONST_STR_LEN("a\r\n"
74                     "Host: www.example.org\r\n"
75                     "\r\n"));
76 
77     run_http_request_parse(r, __LINE__, 400,
78       "invalid request-line: char, space",
79       CONST_STR_LEN("a \r\n"
80                     "Host: www.example.org\r\n"
81                     "\r\n"));
82 
83     run_http_request_parse(r, __LINE__, 400,
84       "invalid request-line: method only",
85       CONST_STR_LEN("GET\r\n"
86                     "Host: www.example.org\r\n"
87                     "\r\n"));
88 
89     run_http_request_parse(r, __LINE__, 400,
90       "invalid request-line: method space",
91       CONST_STR_LEN("GET \r\n"
92                     "Host: www.example.org\r\n"
93                     "\r\n"));
94 
95     run_http_request_parse(r, __LINE__, 400,
96       "invalid request-line: method space space",
97       CONST_STR_LEN("GET  \r\n"
98                     "Host: www.example.org\r\n"
99                     "\r\n"));
100 
101     run_http_request_parse(r, __LINE__, 400,
102       "invalid request-line: method space proto",
103       CONST_STR_LEN("GET HTTP/1.0\r\n"
104                     "Host: www.example.org\r\n"
105                     "\r\n"));
106 
107     run_http_request_parse(r, __LINE__, 400,
108       "invalid request-line: method space space proto",
109       CONST_STR_LEN("GET  HTTP/1.0\r\n"
110                     "Host: www.example.org\r\n"
111                     "\r\n"));
112 
113     run_http_request_parse(r, __LINE__, 400,
114       "invalid request-line: method space space space proto",
115       CONST_STR_LEN("GET   HTTP/1.0\r\n"
116                     "Host: www.example.org\r\n"
117                     "\r\n"));
118 
119     run_http_request_parse(r, __LINE__, 400,
120       "invalid request-line: method slash proto, no spaces",
121       CONST_STR_LEN("GET/HTTP/1.0\r\n"
122                     "Host: www.example.org\r\n"
123                     "\r\n"));
124 
125     run_http_request_parse(r, __LINE__, 400,
126       "invalid request-line: method space slash proto",
127       CONST_STR_LEN("GET /HTTP/1.0\r\n"
128                     "Host: www.example.org\r\n"
129                     "\r\n"));
130 
131     run_http_request_parse(r, __LINE__, 400,
132       "invalid request-line: method space space slash proto",
133       CONST_STR_LEN("GET  /HTTP/1.0\r\n"
134                     "Host: www.example.org\r\n"
135                     "\r\n"));
136 
137     run_http_request_parse(r, __LINE__, 501,
138       "invalid request-line: method slash space proto",
139       CONST_STR_LEN("GET/ HTTP/1.0\r\n"
140                     "Host: www.example.org\r\n"
141                     "\r\n"));
142 
143     run_http_request_parse(r, __LINE__, 501,
144       "invalid request-line: method slash space space proto",
145       CONST_STR_LEN("GET/  HTTP/1.0\r\n"
146                     "Host: www.example.org\r\n"
147                     "\r\n"));
148 
149     run_http_request_parse(r, __LINE__, 0,
150       "hostname",
151       CONST_STR_LEN("GET / HTTP/1.0\r\n"
152                     "Host: www.example.org\r\n"
153                     "\r\n"));
154     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("www.example.org")));
155 
156     run_http_request_parse(r, __LINE__, 0,
157       "IPv4 address",
158       CONST_STR_LEN("GET / HTTP/1.0\r\n"
159                     "Host: 127.0.0.1\r\n"
160                     "\r\n"));
161     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("127.0.0.1")));
162 
163     run_http_request_parse(r, __LINE__, 0,
164       "IPv6 address",
165       CONST_STR_LEN("GET / HTTP/1.0\r\n"
166                     "Host: [::1]\r\n"
167                     "\r\n"));
168     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("[::1]")));
169 
170     run_http_request_parse(r, __LINE__, 0,
171       "hostname + port",
172       CONST_STR_LEN("GET / HTTP/1.0\r\n"
173                     "Host: www.example.org:80\r\n"
174                     "\r\n"));
175     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("www.example.org")));
176 
177     run_http_request_parse(r, __LINE__, 0,
178       "IPv4 address + port",
179       CONST_STR_LEN("GET / HTTP/1.0\r\n"
180                     "Host: 127.0.0.1:80\r\n"
181                     "\r\n"));
182     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("127.0.0.1")));
183 
184     run_http_request_parse(r, __LINE__, 0,
185       "IPv6 address + port",
186       CONST_STR_LEN("GET / HTTP/1.0\r\n"
187                     "Host: [::1]:80\r\n"
188                     "\r\n"));
189     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("[::1]")));
190 
191     run_http_request_parse(r, __LINE__, 400,
192       "directory traversal",
193       CONST_STR_LEN("GET / HTTP/1.0\r\n"
194                     "Host: ../123.org\r\n"
195                     "\r\n"));
196 
197     run_http_request_parse(r, __LINE__, 400,
198       "leading and trailing dot",
199       CONST_STR_LEN("GET / HTTP/1.0\r\n"
200                     "Host: .jsdh.sfdg.sdfg.\r\n"
201                     "\r\n"));
202 
203     run_http_request_parse(r, __LINE__, 0,
204       "trailing dot is ok",
205       CONST_STR_LEN("GET / HTTP/1.0\r\n"
206                     "Host: jsdh.sfdg.sdfg.\r\n"
207                     "\r\n"));
208     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("jsdh.sfdg.sdfg")));
209 
210     run_http_request_parse(r, __LINE__, 400,
211       "leading dot",
212       CONST_STR_LEN("GET / HTTP/1.0\r\n"
213                     "Host: .jsdh.sfdg.sdfg\r\n"
214                     "\r\n"));
215 
216     run_http_request_parse(r, __LINE__, 400,
217       "two dots",
218       CONST_STR_LEN("GET / HTTP/1.0\r\n"
219                     "Host: jsdh..sfdg.sdfg\r\n"
220                     "\r\n"));
221 
222     run_http_request_parse(r, __LINE__, 400,
223       "broken port-number",
224       CONST_STR_LEN("GET / HTTP/1.0\r\n"
225                     "Host: jsdh.sfdg.sdfg:asd\r\n"
226                     "\r\n"));
227 
228     run_http_request_parse(r, __LINE__, 400,
229       "negative port-number",
230       CONST_STR_LEN("GET / HTTP/1.0\r\n"
231                     "Host: jsdh.sfdg.sdfg:-1\r\n"
232                     "\r\n"));
233 
234     run_http_request_parse(r, __LINE__, 400,
235       "port given but host missing",
236       CONST_STR_LEN("GET / HTTP/1.0\r\n"
237                     "Host: :80\r\n"
238                     "\r\n"));
239 
240     run_http_request_parse(r, __LINE__, 400,
241       "port and host are broken",
242       CONST_STR_LEN("GET / HTTP/1.0\r\n"
243                     "Host: .jsdh.sfdg.:sdfg.\r\n"
244                     "\r\n"));
245 
246     run_http_request_parse(r, __LINE__, 0,
247       "allowed characters in host-name",
248       CONST_STR_LEN("GET / HTTP/1.0\r\n"
249                     "Host: a.b-c.d123\r\n"
250                     "\r\n"));
251     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("a.b-c.d123")));
252 
253     run_http_request_parse(r, __LINE__, 400,
254       "leading dash",
255       CONST_STR_LEN("GET / HTTP/1.0\r\n"
256                     "Host: -a.c\r\n"
257                     "\r\n"));
258 
259     run_http_request_parse(r, __LINE__, 400,
260       "dot only",
261       CONST_STR_LEN("GET / HTTP/1.0\r\n"
262                     "Host: .\r\n"
263                     "\r\n"));
264 
265     run_http_request_parse(r, __LINE__, 400,
266       "broken IPv4 address - non-digit",
267       CONST_STR_LEN("GET / HTTP/1.0\r\n"
268                     "Host: a192.168.2.10:1234\r\n"
269                     "\r\n"));
270 
271     run_http_request_parse(r, __LINE__, 400,
272       "broken IPv4 address - too short",
273       CONST_STR_LEN("GET / HTTP/1.0\r\n"
274                     "Host: 192.168.2:1234\r\n"
275                     "\r\n"));
276 
277     run_http_request_parse(r, __LINE__, 400,
278       "IPv6 address + SQL injection",
279       CONST_STR_LEN("GET / HTTP/1.0\r\n"
280                     "Host: [::1]' UNION SELECT '/\r\n"
281                     "\r\n"));
282 
283     run_http_request_parse(r, __LINE__, 400,
284       "IPv6 address + path traversal",
285       CONST_STR_LEN("GET / HTTP/1.0\r\n"
286                     "Host: [::1]/../../../\r\n"
287                     "\r\n"));
288 
289     run_http_request_parse(r, __LINE__, 400,
290       "negative Content-Length",
291       CONST_STR_LEN("POST /12345.txt HTTP/1.0\r\n"
292                     "Content-Length: -2\r\n"
293                     "\r\n"));
294 
295     run_http_request_parse(r, __LINE__, 411,
296       "Content-Length is empty",
297       CONST_STR_LEN("POST /12345.txt HTTP/1.0\r\n"
298                     "Host: 123.example.org\r\n"
299                     "Content-Length:\r\n"
300                     "\r\n"));
301 
302     run_http_request_parse(r, __LINE__, 400,
303       "Host missing",
304       CONST_STR_LEN("GET / HTTP/1.1\r\n"
305                     "\r\n"));
306 
307     run_http_request_parse(r, __LINE__, 400,
308       "empty request-URI",
309       CONST_STR_LEN("GET  HTTP/1.0\r\n"
310                     "\r\n"));
311 
312     run_http_request_parse(r, __LINE__, 0,
313       "#1232 - duplicate headers with line-wrapping",
314       CONST_STR_LEN("GET / HTTP/1.0\r\n"
315                     "Location: foo\r\n"
316                     "Location: foobar\r\n"
317                     "  baz\r\n"
318                     "\r\n"));
319     ds = (data_string *)
320       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Location"));
321     assert(ds
322            && buffer_eq_slen(&ds->value, CONST_STR_LEN("foo, foobar    baz")));
323 
324     run_http_request_parse(r, __LINE__, 0,
325       "#1232 - duplicate headers with line-wrapping - test 2",
326       CONST_STR_LEN("GET / HTTP/1.0\r\n"
327                     "Location: \r\n"
328                     "Location: foobar\r\n"
329                     "  baz\r\n"
330                     "\r\n"));
331     ds = (data_string *)
332       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Location"));
333     assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foobar    baz")));
334 
335     run_http_request_parse(r, __LINE__, 0,
336       "#1232 - duplicate headers with line-wrapping - test 3",
337       CONST_STR_LEN("GET / HTTP/1.0\r\n"
338                     "A: \r\n"
339                     "Location: foobar\r\n"
340                     "  baz\r\n"
341                     "\r\n"));
342     ds = (data_string *)
343       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Location"));
344     assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foobar    baz")));
345 
346     run_http_request_parse(r, __LINE__, 400,
347       "missing protocol",
348       CONST_STR_LEN("GET /\r\n"
349                     "\r\n"));
350 
351     run_http_request_parse(r, __LINE__, 505,
352       "zeros in protocol version",
353       CONST_STR_LEN("GET / HTTP/01.01\r\n"
354                     "Host: foo\r\n"
355                     "\r\n"));
356 
357     run_http_request_parse(r, __LINE__, 505,
358       "missing major version",
359       CONST_STR_LEN("GET / HTTP/.01\r\n"
360                     "\r\n"));
361 
362     run_http_request_parse(r, __LINE__, 505,
363       "missing minor version",
364       CONST_STR_LEN("GET / HTTP/01.\r\n"
365                     "\r\n"));
366 
367     run_http_request_parse(r, __LINE__, 505,
368       "strings as version",
369       CONST_STR_LEN("GET / HTTP/a.b\r\n"
370                     "\r\n"));
371 
372     run_http_request_parse(r, __LINE__, 400,
373       "missing protocol + unknown method",
374       CONST_STR_LEN("BC /\r\n"
375                     "\r\n"));
376 
377     run_http_request_parse(r, __LINE__, 400,
378       "missing protocol + unknown method + missing URI",
379       CONST_STR_LEN("ABC\r\n"
380                     "\r\n"));
381 
382     run_http_request_parse(r, __LINE__, 501,
383       "unknown method",
384       CONST_STR_LEN("ABC / HTTP/1.0\r\n"
385                     "\r\n"));
386 
387     run_http_request_parse(r, __LINE__, 505,
388       "unknown protocol",
389       CONST_STR_LEN("GET / HTTP/1.3\r\n"
390                     "\r\n"));
391 
392     run_http_request_parse(r, __LINE__, 0,
393       "absolute URI",
394       CONST_STR_LEN("GET http://www.example.org/ HTTP/1.0\r\n"
395                     "\r\n"));
396     assert(buffer_eq_slen(r->http_host, CONST_STR_LEN("www.example.org")));
397     assert(buffer_eq_slen(&r->target, CONST_STR_LEN("/")));
398 
399     run_http_request_parse(r, __LINE__, 400,
400       "whitespace after key",
401       CONST_STR_LEN("GET / HTTP/1.0\r\n"
402                     "ABC : foo\r\n"
403                     "\r\n"));
404 
405     run_http_request_parse(r, __LINE__, 400,
406       "whitespace within key",
407       CONST_STR_LEN("GET / HTTP/1.0\r\n"
408                     "ABC a: foo\r\n"
409                     "\r\n"));
410 
411     run_http_request_parse(r, __LINE__, 0,
412       "no whitespace",
413       CONST_STR_LEN("GET / HTTP/1.0\r\n"
414                     "ABC:foo\r\n"
415                     "\r\n"));
416     ds = (data_string *)
417       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("ABC"));
418     assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foo")));
419 
420     run_http_request_parse(r, __LINE__, 0,
421       "line-folding",
422       CONST_STR_LEN("GET / HTTP/1.0\r\n"
423                     "ABC:foo\r\n"
424                     "  bc\r\n"
425                     "\r\n"));
426     ds = (data_string *)
427       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("ABC"));
428     assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("foo    bc")));
429 
430     run_http_request_parse(r, __LINE__, 411,
431       "POST request, no Content-Length",
432       CONST_STR_LEN("POST / HTTP/1.0\r\n"
433                     "\r\n"));
434 
435     run_http_request_parse(r, __LINE__, 400,
436       "Duplicate Host headers, Bug #25",
437       CONST_STR_LEN("GET / HTTP/1.0\r\n"
438                     "Host: www.example.org\r\n"
439                     "Host: 123.example.org\r\n"
440                     "\r\n"));
441 
442     run_http_request_parse(r, __LINE__, 400,
443       "Duplicate Content-Length headers",
444       CONST_STR_LEN("GET / HTTP/1.0\r\n"
445                     "Content-Length: 5\r\n"
446                     "Content-Length: 4\r\n"
447                     "\r\n"));
448 
449     run_http_request_parse(r, __LINE__, 400,
450       "Duplicate Content-Type headers",
451       CONST_STR_LEN("GET / HTTP/1.0\r\n"
452                     "Content-Type: 5\r\n"
453                     "Content-Type: 4\r\n"
454                     "\r\n"));
455 
456     /* (not actually testing Range here anymore; parsing deferred until use) */
457 
458     run_http_request_parse(r, __LINE__, 0,
459       "Duplicate Range headers (get appended)",
460       CONST_STR_LEN("GET / HTTP/1.0\r\n"
461                     "Range: bytes=5-6\r\n"
462                     "Range: bytes=5-9\r\n"
463                     "\r\n"));
464 
465     run_http_request_parse(r, __LINE__, 0,
466       "Duplicate Range headers with invalid range (a)",
467       CONST_STR_LEN("GET / HTTP/1.0\r\n"
468                     "Range: bytes=0\r\n"
469                     "Range: bytes=5-9\r\n"
470                     "\r\n"));
471     run_http_request_parse(r, __LINE__, 0,
472       "Duplicate Range headers with invalid range (b)",
473       CONST_STR_LEN("GET / HTTP/1.0\r\n"
474                     "Range: bytes=5-9\r\n"
475                     "Range: bytes=0\r\n"
476                     "\r\n"));
477     run_http_request_parse(r, __LINE__, 0,
478       "Duplicate Range headers with invalid range (c)",
479       CONST_STR_LEN("GET / HTTP/1.0\r\n"
480                     "Range: 0\r\n"
481                     "Range: bytes=5-9\r\n"
482                     "\r\n"));
483     run_http_request_parse(r, __LINE__, 0,
484       "Duplicate Range headers with invalid range (d)",
485       CONST_STR_LEN("GET / HTTP/1.0\r\n"
486                     "Range: bytes=5-9\r\n"
487                     "Range: 0\r\n"
488                     "\r\n"));
489 
490     run_http_request_parse(r, __LINE__, 0,
491       "Duplicate If-None-Match headers",
492       CONST_STR_LEN("GET / HTTP/1.0\r\n"
493                     "If-None-Match: 5\r\n"
494                     "If-None-Match: 4\r\n"
495                     "\r\n"));
496 
497     run_http_request_parse(r, __LINE__, 400,
498       "Duplicate If-Modified-Since headers",
499       CONST_STR_LEN("GET / HTTP/1.0\r\n"
500                     "If-Modified-Since: 5\r\n"
501                     "If-Modified-Since: 4\r\n"
502                     "\r\n"));
503 
504     run_http_request_parse(r, __LINE__, 400,
505       "GET with Content-Length",
506       CONST_STR_LEN("GET / HTTP/1.0\r\n"
507                     "Content-Length: 4\r\n"
508                     "\r\n"
509                     "1234"));
510 
511     run_http_request_parse(r, __LINE__, 400,
512       "HEAD with Content-Length",
513       CONST_STR_LEN("HEAD / HTTP/1.0\r\n"
514                     "Content-Length: 4\r\n"
515                     "\r\n"
516                     "1234"));
517 
518     run_http_request_parse(r, __LINE__, 400,
519       "invalid chars in Header values (bug #1286)",
520       CONST_STR_LEN("GET / HTTP/1.0\r\n"
521                     "If-Modified-Since: \0\r\n"
522                     "\r\n"));
523 
524     run_http_request_parse(r, __LINE__, 0,
525       "absolute-uri in request-line (without Host)",
526       CONST_STR_LEN("GET http://zzz.example.org/ HTTP/1.1\r\n"
527                     "Connection: close\r\n"
528                     "\r\n"));
529     ds = (data_string *)
530       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Host"));
531     assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("zzz.example.org")));
532 
533     run_http_request_parse(r, __LINE__, 0,
534       "absolute-uri in request-line (with Host match)",
535       CONST_STR_LEN("GET http://zzz.example.org/ HTTP/1.1\r\n"
536                     "Host: zzz.example.org\r\n"
537                     "Connection: close\r\n"
538                     "\r\n"));
539     ds = (data_string *)
540       array_get_element_klen(&r->rqst_headers, CONST_STR_LEN("Host"));
541     assert(ds && buffer_eq_slen(&ds->value, CONST_STR_LEN("zzz.example.org")));
542 
543     run_http_request_parse(r, __LINE__, 400,
544       "absolute-uri in request-line (with Host mismatch)",
545       CONST_STR_LEN("GET http://zzz.example.org/ HTTP/1.1\r\n"
546                     "Host: aaa.example.org\r\n"
547                     "Connection: close\r\n"
548                     "\r\n"));
549 
550     /* (quick check that none of above tests were left in a state
551      *  which resulted in subsequent tests returning 400 for other
552      *  reasons) */
553     run_http_request_parse(r, __LINE__, 0,
554       "valid",
555       CONST_STR_LEN("GET / HTTP/1.0\r\n"
556                     "Host: www.example.org\r\n"
557                     "\r\n"));
558 }
559 
560 #include "base.h"
561 #include "burl.h"
562 #include "log.h"
563 
564 int main (void)
565 {
566     request_st r;
567 
568     memset(&r, 0, sizeof(request_st));
569     r.conf.errh              = log_error_st_init();
570     r.conf.errh->errorlog_fd = -1; /* (disable) */
571     r.conf.allow_http11      = 1;
572     r.conf.http_parseopts    = HTTP_PARSEOPT_HEADER_STRICT
573                              | HTTP_PARSEOPT_HOST_STRICT
574                              | HTTP_PARSEOPT_HOST_NORMALIZE;
575 
576     test_request_http_request_parse(&r);
577 
578     free(r.target_orig.ptr);
579     free(r.target.ptr);
580     array_free_data(&r.rqst_headers);
581 
582     log_error_st_free(r.conf.errh);
583 
584     return 0;
585 }
586