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