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