1 From: =?UTF-8?Q?Stefan_B=c3=bchler?= <stbuehler@web.de>
2 To: lighttpd-announce@lists.lighttpd.net
3 Message-ID: <8cbf0c87-1037-8f70-aa24-b2f14c95170d@web.de>
4 Date: Sun, 26 Aug 2018 19:06:30 +0200
5 Subject: use-after-free bug in header folding
9 Or Peles reported some use-after-free scenarios related to header
10 folding (continuing a header value on a whitespace indented line).
12 The fix required some preparation and therefor consists of a series of 4
15 https://git.lighttpd.net/lighttpd/lighttpd1.4.git/log/?qt=range&q=a9e131..df8e4f&showmsg=1
17 There are currently no plans for a new release.
22 diff --git a/src/request.c b/src/request.c
23 index 213a87e1..e94d8591 100644
26 @@ -408,26 +408,179 @@ static int request_uri_is_valid_char(unsigned char c) {
30 -static int http_request_missing_CR_before_LF(server *srv, connection *con) {
31 +static void http_request_missing_CR_before_LF(server *srv, connection *con) {
32 if (srv->srvconf.log_request_header_on_error) {
33 log_error_write(srv, __FILE__, __LINE__, "s", "missing CR before LF in header -> 400");
34 log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request);
38 - con->http_status = 400;
39 - con->keep_alive = 0;
40 - con->response.keep_alive = 0;
41 +enum keep_alive_set {
42 + HTTP_CONNECTION_UNSET,
43 + HTTP_CONNECTION_KEEPALIVE,
44 + HTTP_CONNECTION_CLOSE,
48 + enum keep_alive_set keep_alive_set;
49 + char con_length_set;
51 + int reqline_hostlen;
52 +} parse_header_state;
54 +static void init_parse_header_state(parse_header_state* state) {
55 + state->keep_alive_set = HTTP_CONNECTION_UNSET;
56 + state->con_length_set = 0;
57 + state->reqline_host = NULL;
58 + state->reqline_hostlen = 0;
61 +/* add a header to the list of headers; certain headers are also parsed in this state.
63 + * Also might drop a header if deemed unnecessary/broken.
65 + * returns 0 on error
67 +static int parse_single_header(server *srv, connection *con, parse_header_state *state, data_string *ds) {
70 + /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
71 + if (buffer_string_is_empty(ds->value)) {
78 + * the list of options is sorted to simplify the search
81 + if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
87 + vals = srv->split_vals;
91 + http_request_split_value(vals, ds->value);
93 + for (vi = 0; vi < vals->used; vi++) {
94 + data_string *dsv = (data_string *)vals->data[vi];
96 + if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
97 + state->keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
100 + } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
101 + state->keep_alive_set = HTTP_CONNECTION_CLOSE;
107 + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
111 + if (state->con_length_set) {
112 + if (srv->srvconf.log_request_header_on_error) {
113 + log_error_write(srv, __FILE__, __LINE__, "s",
114 + "duplicate Content-Length-header -> 400");
115 + log_error_write(srv, __FILE__, __LINE__, "Sb",
116 + "request-header:\n",
117 + con->request.request);
119 + goto invalid_header;
122 + r = strtoll(ds->value->ptr, &err, 10);
124 + if (*err == '\0' && r >= 0) {
125 + state->con_length_set = 1;
126 + con->request.content_length = r;
128 + log_error_write(srv, __FILE__, __LINE__, "sbs",
129 + "content-length broken:", ds->value, "-> 400");
130 + goto invalid_header;
132 + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
133 + /* if dup, only the first one will survive */
134 + if (!con->request.http_content_type) {
135 + con->request.http_content_type = ds->value->ptr;
137 + if (srv->srvconf.log_request_header_on_error) {
138 + log_error_write(srv, __FILE__, __LINE__, "s",
139 + "duplicate Content-Type-header -> 400");
140 + log_error_write(srv, __FILE__, __LINE__, "Sb",
141 + "request-header:\n",
142 + con->request.request);
144 + goto invalid_header;
146 + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
147 + if (state->reqline_host) {
148 + /* ignore all host: headers as we got the host in the request line */
150 + } else if (!con->request.http_host) {
151 + con->request.http_host = ds->value;
153 + if (srv->srvconf.log_request_header_on_error) {
154 + log_error_write(srv, __FILE__, __LINE__, "s",
155 + "duplicate Host-header -> 400");
156 + log_error_write(srv, __FILE__, __LINE__, "Sb",
157 + "request-header:\n",
158 + con->request.request);
160 + goto invalid_header;
162 + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
163 + /* Proxies sometimes send dup headers
164 + * if they are the same we ignore the second
165 + * if not, we raise an error */
166 + if (!con->request.http_if_modified_since) {
167 + con->request.http_if_modified_since = ds->value->ptr;
168 + } else if (0 == strcasecmp(con->request.http_if_modified_since, ds->value->ptr)) {
169 + /* ignore it if they are the same */
172 + if (srv->srvconf.log_request_header_on_error) {
173 + log_error_write(srv, __FILE__, __LINE__, "s",
174 + "duplicate If-Modified-Since header -> 400");
175 + log_error_write(srv, __FILE__, __LINE__, "Sb",
176 + "request-header:\n",
177 + con->request.request);
179 + goto invalid_header;
181 + } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
182 + /* if dup, only the first one will survive */
183 + if (!con->request.http_if_none_match) {
184 + con->request.http_if_none_match = ds->value->ptr;
190 + array_insert_unique(con->request.headers, (data_unset *)ds);
194 + ds->free((data_unset *)ds);
198 + ds->free((data_unset *)ds);
202 int http_request_parse(server *srv, connection *con) {
203 - char *uri = NULL, *proto = NULL, *method = NULL, con_length_set;
204 + char *uri = NULL, *proto = NULL, *method = NULL;
205 int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding;
206 char *value = NULL, *key = NULL;
207 - char *reqline_host = NULL;
208 - int reqline_hostlen = 0;
210 - enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET;
211 + data_string *current_header = NULL;
215 @@ -437,6 +590,9 @@ int http_request_parse(server *srv, connection *con) {
217 const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT);
219 + parse_header_state state;
220 + init_parse_header_state(&state);
223 * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
224 * Option : "^([-a-zA-Z]+): (.+)$"
225 @@ -457,9 +613,7 @@ int http_request_parse(server *srv, connection *con) {
228 if (buffer_string_length(con->request.request) < 2) {
229 - con->keep_alive = 0;
230 - con->http_status = 400;
235 /* coverity[overflow_sink : FALSE] */
236 @@ -467,12 +621,13 @@ int http_request_parse(server *srv, connection *con) {
237 } else if (con->request_count > 0 &&
238 con->request.request->ptr[1] == '\n') {
239 /* we are in keep-alive and might get \n after a previous POST request.*/
240 - if (http_header_strict) return http_request_missing_CR_before_LF(srv, con);
241 + if (http_header_strict) {
242 + http_request_missing_CR_before_LF(srv, con);
246 if (buffer_string_length(con->request.request) < 1) {
247 - con->keep_alive = 0;
248 - con->http_status = 400;
253 /* coverity[overflow_sink : FALSE] */
254 @@ -482,9 +637,6 @@ int http_request_parse(server *srv, connection *con) {
255 buffer_copy_buffer(con->parse_request, con->request.request);
258 - keep_alive_set = 0;
259 - con_length_set = 0;
261 /* parse the first line of the request
264 @@ -510,22 +662,19 @@ int http_request_parse(server *srv, connection *con) {
265 con->parse_request->ptr[i] = '\0';
267 } else if (http_header_strict) { /* '\n' */
268 - return http_request_missing_CR_before_LF(srv, con);
269 + http_request_missing_CR_before_LF(srv, con);
272 con->parse_request->ptr[i] = '\0';
274 if (request_line_stage != 2) {
275 - con->http_status = 400;
276 - con->response.keep_alive = 0;
277 - con->keep_alive = 0;
279 if (srv->srvconf.log_request_header_on_error) {
280 log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400");
281 log_error_write(srv, __FILE__, __LINE__, "Sb",
283 con->request.request);
289 proto = con->parse_request->ptr + first;
290 @@ -536,8 +685,6 @@ int http_request_parse(server *srv, connection *con) {
291 /* we got the first one :) */
292 if (HTTP_METHOD_UNSET == (r = get_http_method_key(method))) {
293 con->http_status = 501;
294 - con->response.keep_alive = 0;
295 - con->keep_alive = 0;
297 if (srv->srvconf.log_request_header_on_error) {
298 log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501");
299 @@ -546,7 +693,7 @@ int http_request_parse(server *srv, connection *con) {
300 con->request.request);
307 con->request.http_method = r;
308 @@ -582,16 +729,13 @@ int http_request_parse(server *srv, connection *con) {
311 if (invalid_version) {
312 - con->http_status = 400;
313 - con->keep_alive = 0;
315 if (srv->srvconf.log_request_header_on_error) {
316 log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
317 log_error_write(srv, __FILE__, __LINE__, "Sb",
319 con->request.request);
325 if (major_num == 1 && minor_num == 1) {
326 @@ -607,19 +751,16 @@ int http_request_parse(server *srv, connection *con) {
328 con->request.request);
334 - con->http_status = 400;
335 - con->keep_alive = 0;
337 if (srv->srvconf.log_request_header_on_error) {
338 log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
339 log_error_write(srv, __FILE__, __LINE__, "Sb",
341 con->request.request);
348 @@ -627,14 +768,14 @@ int http_request_parse(server *srv, connection *con) {
349 buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
350 } else if (0 == strncasecmp(uri, "http://", 7) &&
351 NULL != (nuri = strchr(uri + 7, '/'))) {
352 - reqline_host = uri + 7;
353 - reqline_hostlen = nuri - reqline_host;
354 + state.reqline_host = uri + 7;
355 + state.reqline_hostlen = nuri - state.reqline_host;
357 buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
358 } else if (0 == strncasecmp(uri, "https://", 8) &&
359 NULL != (nuri = strchr(uri + 8, '/'))) {
360 - reqline_host = uri + 8;
361 - reqline_hostlen = nuri - reqline_host;
362 + state.reqline_host = uri + 8;
363 + state.reqline_hostlen = nuri - state.reqline_host;
365 buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
366 } else if (!http_header_strict
367 @@ -643,10 +784,8 @@ int http_request_parse(server *srv, connection *con) {
368 /* everything looks good so far */
369 buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
371 - con->http_status = 400;
372 - con->keep_alive = 0;
373 log_error_write(srv, __FILE__, __LINE__, "ss", "request-URI parse error -> 400 for:", uri);
378 /* check uri for invalid characters */
379 @@ -660,33 +799,30 @@ int http_request_parse(server *srv, connection *con) {
380 j = (NULL == z) ? jlen : (size_t)(z - con->request.uri->ptr);
383 - con->http_status = 400;
384 - con->keep_alive = 0;
386 - if (srv->srvconf.log_request_header_on_error) {
387 - unsigned char buf[2];
388 - buf[0] = con->request.uri->ptr[j];
391 - if (con->request.uri->ptr[j] > 32 &&
392 - con->request.uri->ptr[j] != 127) {
393 - /* the character is printable -> print it */
394 - log_error_write(srv, __FILE__, __LINE__, "ss",
395 - "invalid character in URI -> 400",
398 - /* a control-character, print ascii-code */
399 - log_error_write(srv, __FILE__, __LINE__, "sd",
400 - "invalid character in URI -> 400",
401 - con->request.uri->ptr[j]);
404 - log_error_write(srv, __FILE__, __LINE__, "Sb",
405 - "request-header:\n",
406 - con->request.request);
407 + if (srv->srvconf.log_request_header_on_error) {
408 + unsigned char buf[2];
409 + buf[0] = con->request.uri->ptr[j];
412 + if (con->request.uri->ptr[j] > 32 &&
413 + con->request.uri->ptr[j] != 127) {
414 + /* the character is printable -> print it */
415 + log_error_write(srv, __FILE__, __LINE__, "ss",
416 + "invalid character in URI -> 400",
419 + /* a control-character, print ascii-code */
420 + log_error_write(srv, __FILE__, __LINE__, "sd",
421 + "invalid character in URI -> 400",
422 + con->request.uri->ptr[j]);
426 + log_error_write(srv, __FILE__, __LINE__, "Sb",
427 + "request-header:\n",
428 + con->request.request);
434 buffer_copy_buffer(con->request.orig_uri, con->request.uri);
435 @@ -711,17 +847,13 @@ int http_request_parse(server *srv, connection *con) {
438 /* ERROR, one space to much */
439 - con->http_status = 400;
440 - con->response.keep_alive = 0;
441 - con->keep_alive = 0;
443 if (srv->srvconf.log_request_header_on_error) {
444 log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400");
445 log_error_write(srv, __FILE__, __LINE__, "Sb",
447 con->request.request);
453 request_line_stage++;
454 @@ -732,20 +864,16 @@ int http_request_parse(server *srv, connection *con) {
457 if (buffer_string_is_empty(con->request.uri)) {
458 - con->http_status = 400;
459 - con->response.keep_alive = 0;
460 - con->keep_alive = 0;
462 if (srv->srvconf.log_request_header_on_error) {
463 log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400");
464 log_error_write(srv, __FILE__, __LINE__, "Sb",
466 con->request.request);
472 - if (reqline_host) {
473 + if (state.reqline_host) {
474 /* Insert as host header */
477 @@ -754,7 +882,7 @@ int http_request_parse(server *srv, connection *con) {
480 buffer_copy_string_len(ds->key, CONST_STR_LEN("Host"));
481 - buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen);
482 + buffer_copy_string_len(ds->value, state.reqline_host, state.reqline_hostlen);
483 array_insert_unique(con->request.headers, (data_unset *)ds);
484 con->request.http_host = ds->value;
486 @@ -799,10 +927,6 @@ int http_request_parse(server *srv, connection *con) {
490 - con->http_status = 400;
491 - con->keep_alive = 0;
492 - con->response.keep_alive = 0;
494 if (srv->srvconf.log_request_header_on_error) {
495 log_error_write(srv, __FILE__, __LINE__, "sbsds",
496 "invalid character in key", con->request.request, cur, *cur, "-> 400");
497 @@ -811,7 +935,7 @@ int http_request_parse(server *srv, connection *con) {
499 con->request.request);
506 @@ -850,11 +974,7 @@ int http_request_parse(server *srv, connection *con) {
507 con->request.request);
510 - con->http_status = 400;
511 - con->response.keep_alive = 0;
512 - con->keep_alive = 0;
519 @@ -876,15 +996,13 @@ int http_request_parse(server *srv, connection *con) {
520 con->request.request);
523 - con->http_status = 400;
524 - con->keep_alive = 0;
525 - con->response.keep_alive = 0;
531 if (http_header_strict) {
532 - return http_request_missing_CR_before_LF(srv, con);
533 + http_request_missing_CR_before_LF(srv, con);
535 } else if (i == first) {
536 con->parse_request->ptr[i] = '\0';
538 @@ -893,10 +1011,6 @@ int http_request_parse(server *srv, connection *con) {
541 if (http_header_strict ? (*cur < 32 || ((unsigned char)*cur) >= 127) : *cur == '\0') {
542 - con->http_status = 400;
543 - con->keep_alive = 0;
544 - con->response.keep_alive = 0;
546 if (srv->srvconf.log_request_header_on_error) {
547 log_error_write(srv, __FILE__, __LINE__, "sbsds",
548 "invalid character in key", con->request.request, cur, *cur, "-> 400");
549 @@ -906,7 +1020,7 @@ int http_request_parse(server *srv, connection *con) {
550 con->request.request);
558 @@ -916,9 +1030,13 @@ int http_request_parse(server *srv, connection *con) {
561 if (*cur == '\n' || con->parse_request->ptr[i+1] == '\n') {
562 - data_string *ds = NULL;
566 - if (http_header_strict) return http_request_missing_CR_before_LF(srv, con);
567 + if (http_header_strict) {
568 + http_request_missing_CR_before_LF(srv, con);
571 } else { /* (con->parse_request->ptr[i+1] == '\n') */
572 con->parse_request->ptr[i] = '\0';
574 @@ -927,17 +1045,15 @@ int http_request_parse(server *srv, connection *con) {
575 /* End of Headerline */
576 con->parse_request->ptr[i] = '\0';
578 + value_len = cur - value;
580 + /* strip trailing white-spaces */
581 + while (value_len > 0 && (value[value_len - 1] == ' ' || value[value_len - 1] == '\t')) {
587 - * we use a evil hack to handle the line-folding
589 - * As array_insert_unique() deletes 'ds' in the case of a duplicate
590 - * ds points somewhere and we get a evil crash. As a solution we keep the old
591 - * "key" and get the current value from the hash and append us
595 - if (!key || !key_len) {
596 + if (!current_header) {
599 if (srv->srvconf.log_request_header_on_error) {
600 @@ -948,193 +1064,35 @@ int http_request_parse(server *srv, connection *con) {
601 con->request.request);
605 - con->http_status = 400;
606 - con->keep_alive = 0;
607 - con->response.keep_alive = 0;
612 - if (NULL != (ds = (data_string *)array_get_element_klen(con->request.headers, key, key_len))) {
613 - buffer_append_string(ds->value, value);
615 + buffer_append_string_len(current_header->value, value, value_len);
618 - key = con->parse_request->ptr + first;
620 - s_len = cur - value;
622 - /* strip trailing white-spaces */
623 - for (; s_len > 0 &&
624 - (value[s_len - 1] == ' ' ||
625 - value[s_len - 1] == '\t'); s_len--);
627 - value[s_len] = '\0';
631 - if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
632 - ds = data_string_init();
634 - buffer_copy_string_len(ds->key, key, key_len);
635 - buffer_copy_string_len(ds->value, value, s_len);
640 - * the list of options is sorted to simplify the search
643 - if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
649 - vals = srv->split_vals;
653 - http_request_split_value(vals, ds->value);
655 - for (vi = 0; vi < vals->used; vi++) {
656 - data_string *dsv = (data_string *)vals->data[vi];
658 - if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
659 - keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
662 - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
663 - keep_alive_set = HTTP_CONNECTION_CLOSE;
669 - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
673 - if (con_length_set) {
674 - con->http_status = 400;
675 - con->keep_alive = 0;
677 - if (srv->srvconf.log_request_header_on_error) {
678 - log_error_write(srv, __FILE__, __LINE__, "s",
679 - "duplicate Content-Length-header -> 400");
680 - log_error_write(srv, __FILE__, __LINE__, "Sb",
681 - "request-header:\n",
682 - con->request.request);
684 - array_insert_unique(con->request.headers, (data_unset *)ds);
688 - r = strtoll(ds->value->ptr, &err, 10);
690 - if (*err == '\0' && r >= 0) {
691 - con_length_set = 1;
692 - con->request.content_length = r;
694 - log_error_write(srv, __FILE__, __LINE__, "sbs",
695 - "content-length broken:", ds->value, "-> 400");
697 - con->http_status = 400;
698 - con->keep_alive = 0;
700 - array_insert_unique(con->request.headers, (data_unset *)ds);
703 - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
704 - /* if dup, only the first one will survive */
705 - if (!con->request.http_content_type) {
706 - con->request.http_content_type = ds->value->ptr;
708 - con->http_status = 400;
709 - con->keep_alive = 0;
711 - if (srv->srvconf.log_request_header_on_error) {
712 - log_error_write(srv, __FILE__, __LINE__, "s",
713 - "duplicate Content-Type-header -> 400");
714 - log_error_write(srv, __FILE__, __LINE__, "Sb",
715 - "request-header:\n",
716 - con->request.request);
718 - array_insert_unique(con->request.headers, (data_unset *)ds);
721 - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
722 - if (reqline_host) {
723 - /* ignore all host: headers as we got the host in the request line */
724 - ds->free((data_unset*) ds);
726 - } else if (!con->request.http_host) {
727 - con->request.http_host = ds->value;
729 - con->http_status = 400;
730 - con->keep_alive = 0;
732 - if (srv->srvconf.log_request_header_on_error) {
733 - log_error_write(srv, __FILE__, __LINE__, "s",
734 - "duplicate Host-header -> 400");
735 - log_error_write(srv, __FILE__, __LINE__, "Sb",
736 - "request-header:\n",
737 - con->request.request);
739 - array_insert_unique(con->request.headers, (data_unset *)ds);
742 - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
743 - /* Proxies sometimes send dup headers
744 - * if they are the same we ignore the second
745 - * if not, we raise an error */
746 - if (!con->request.http_if_modified_since) {
747 - con->request.http_if_modified_since = ds->value->ptr;
748 - } else if (0 == strcasecmp(con->request.http_if_modified_since,
750 - /* ignore it if they are the same */
752 - ds->free((data_unset *)ds);
755 - con->http_status = 400;
756 - con->keep_alive = 0;
758 - if (srv->srvconf.log_request_header_on_error) {
759 - log_error_write(srv, __FILE__, __LINE__, "s",
760 - "duplicate If-Modified-Since header -> 400");
761 - log_error_write(srv, __FILE__, __LINE__, "Sb",
762 - "request-header:\n",
763 - con->request.request);
765 - array_insert_unique(con->request.headers, (data_unset *)ds);
768 - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
769 - /* if dup, only the first one will survive */
770 - if (!con->request.http_if_none_match) {
771 - con->request.http_if_none_match = ds->value->ptr;
773 - ds->free((data_unset*) ds);
776 + /* process previous header */
777 + if (current_header) {
778 + data_string *ds = current_header;
779 + current_header = NULL;
780 + if (!parse_single_header(srv, con, &state, ds)) {
781 + /* parse_single_header should already have logged it */
786 - if (ds) array_insert_unique(con->request.headers, (data_unset *)ds);
788 - /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
789 + key = con->parse_request->ptr + first;
791 + if (NULL == (current_header = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
792 + current_header = data_string_init();
795 + buffer_copy_string_len(current_header->key, key, key_len);
796 + buffer_copy_string_len(current_header->value, value, value_len);
804 - * for Bug 1230 keep the key_len a live
810 if (srv->srvconf.log_request_header_on_error) {
811 @@ -1142,10 +1100,7 @@ int http_request_parse(server *srv, connection *con) {
812 "CR without LF", con->request.request, "-> 400");
815 - con->http_status = 400;
816 - con->keep_alive = 0;
817 - con->response.keep_alive = 0;
823 @@ -1160,22 +1115,29 @@ int http_request_parse(server *srv, connection *con) {
824 "invalid char in header", (int)*cur, "-> 400");
827 - con->http_status = 400;
828 - con->keep_alive = 0;
838 + /* process last header */
839 + if (current_header) {
840 + data_string* ds = current_header;
841 + current_header = NULL;
842 + if (!parse_single_header(srv, con, &state, ds)) {
843 + /* parse_single_header should already have logged it */
850 /* do some post-processing */
852 if (con->request.http_version == HTTP_VERSION_1_1) {
853 - if (keep_alive_set != HTTP_CONNECTION_CLOSE) {
854 + if (state.keep_alive_set != HTTP_CONNECTION_CLOSE) {
855 /* no Connection-Header sent */
857 /* HTTP/1.1 -> keep-alive default TRUE */
858 @@ -1187,9 +1149,6 @@ int http_request_parse(server *srv, connection *con) {
859 /* RFC 2616, 14.23 */
860 if (con->request.http_host == NULL ||
861 buffer_string_is_empty(con->request.http_host)) {
862 - con->http_status = 400;
863 - con->response.keep_alive = 0;
864 - con->keep_alive = 0;
866 if (srv->srvconf.log_request_header_on_error) {
867 log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400");
868 @@ -1197,10 +1156,10 @@ int http_request_parse(server *srv, connection *con) {
870 con->request.request);
876 - if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
877 + if (state.keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
878 /* no Connection-Header sent */
880 /* HTTP/1.0 -> keep-alive default FALSE */
881 @@ -1222,11 +1181,7 @@ int http_request_parse(server *srv, connection *con) {
882 con->request.request);
885 - con->http_status = 400;
886 - con->response.keep_alive = 0;
887 - con->keep_alive = 0;
894 @@ -1235,24 +1190,21 @@ int http_request_parse(server *srv, connection *con) {
895 if (con->request.http_version == HTTP_VERSION_1_0) {
896 log_error_write(srv, __FILE__, __LINE__, "s",
897 "HTTP/1.0 with Transfer-Encoding (bad HTTP/1.0 proxy?) -> 400");
898 - con->keep_alive = 0;
899 - con->http_status = 400; /* Bad Request */
904 if (0 != strcasecmp(ds->value->ptr, "chunked")) {
905 /* Transfer-Encoding might contain additional encodings,
906 * which are not currently supported by lighttpd */
907 - con->keep_alive = 0;
908 con->http_status = 501; /* Not Implemented */
913 /* reset value for Transfer-Encoding, a hop-by-hop header,
914 * which must not be blindly forwarded to backends */
915 buffer_reset(ds->value); /* headers with empty values are ignored */
917 - con_length_set = 1;
918 + state.con_length_set = 1;
919 con->request.content_length = -1;
921 /*(note: ignore whether or not Content-Length was provided)*/
922 @@ -1265,27 +1217,23 @@ int http_request_parse(server *srv, connection *con) {
923 case HTTP_METHOD_GET:
924 case HTTP_METHOD_HEAD:
925 /* content-length is forbidden for those */
926 - if (con_length_set && con->request.content_length != 0) {
927 + if (state.con_length_set && con->request.content_length != 0) {
928 /* content-length is missing */
929 log_error_write(srv, __FILE__, __LINE__, "s",
930 "GET/HEAD with content-length -> 400");
932 - con->keep_alive = 0;
933 - con->http_status = 400;
938 case HTTP_METHOD_POST:
939 /* content-length is required for them */
940 - if (!con_length_set) {
941 + if (!state.con_length_set) {
942 /* content-length is missing */
943 log_error_write(srv, __FILE__, __LINE__, "s",
944 "POST-request, but content-length missing -> 411");
946 - con->keep_alive = 0;
947 con->http_status = 411;
954 @@ -1294,12 +1242,21 @@ int http_request_parse(server *srv, connection *con) {
957 /* check if we have read post data */
958 - if (con_length_set) {
959 + if (state.con_length_set) {
960 /* we have content */
961 if (con->request.content_length != 0) {
969 + if (current_header) current_header->free((data_unset *)current_header);
971 + con->keep_alive = 0;
972 + con->response.keep_alive = 0;
973 + if (!con->http_status) con->http_status = 400;