diff -u ../lighttpd-1.5/src/base.h ./src/base.h --- ../lighttpd-1.5/src/base.h 2009-12-11 11:24:07.000000000 +0800 +++ ./src/base.h 2009-12-30 15:57:40.000000000 +0800 @@ -548,6 +548,7 @@ unsigned short max_fds; unsigned short max_conns; unsigned int max_request_size; + unsigned int max_request_header_size; unsigned short log_request_header_on_error; unsigned short log_state_handling; diff -u ../lighttpd-1.5/src/configfile.c ./src/configfile.c --- ../lighttpd-1.5/src/configfile.c 2009-12-11 11:24:07.000000000 +0800 +++ ./src/configfile.c 2009-12-30 16:15:43.000000000 +0800 @@ -110,6 +110,7 @@ { "ssl.verifyclient.depth", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 62 */ { "ssl.verifyclient.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 63 */ { "ssl.verifyclient.exportcert", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 64 */ + { "server.max-request-header-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 65 */ { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, @@ -158,6 +159,7 @@ cv[51].destination = &(srv->srvconf.log_timing); cv[57].destination = srv->srvconf.breakagelog_file; + cv[65].destination = &(srv->srvconf.max_request_header_size); srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); diff -u ../lighttpd-1.5/src/connections.c ./src/connections.c --- ../lighttpd-1.5/src/connections.c 2009-12-11 11:24:07.000000000 +0800 +++ ./src/connections.c 2009-12-30 15:55:56.000000000 +0800 @@ -637,6 +637,12 @@ return HANDLER_ERROR; } + off_t header_len = 0; + off_t offset = 0; + if (con->recv_raw->first) { + offset = con->recv_raw->first->offset; + } + switch (http_request_parse_cq(con->recv_raw, con->http_req)) { case PARSE_ERROR: con->http_status = 400; /* the header is broken */ @@ -646,11 +652,37 @@ return HANDLER_FINISHED; case PARSE_NEED_MORE: + header_len = chunkqueue_written(con->recv_raw) - offset; + if (con->recv_raw && srv->srvconf.max_request_header_size != 0 + && header_len > (srv->srvconf.max_request_header_size << 10)) { + ERROR("request header length is too long, read_length=%d, client-ip: %s", + (int)header_len, SAFE_BUF_STR(con->dst_addr_buf)); + con->http_status = 400; + con->keep_alive = 0; + con->send->is_closed = 1; + chunkqueue_remove_finished_chunks(con->recv_raw); + + return HANDLER_FINISHED; + } + /* we need more */ fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); return HANDLER_WAIT_FOR_EVENT; case PARSE_SUCCESS: + header_len = chunkqueue_written(con->recv_raw) - offset; + if (con->recv_raw && srv->srvconf.max_request_header_size != 0 + && header_len > (srv->srvconf.max_request_header_size << 10) ) { + ERROR("request header length is too long, read_length=%d, client-ip: %s", + (int)header_len, SAFE_BUF_STR(con->dst_addr_buf)); + con->http_status = 400; + con->keep_alive = 0; + con->send->is_closed = 1; + + chunkqueue_remove_finished_chunks(con->recv_raw); + + return HANDLER_FINISHED; + } chunkqueue_remove_finished_chunks(con->recv_raw); break; default: @@ -1016,11 +1048,15 @@ con->request_count++; /* max-keepalive requests */ con->loops_per_request = 0; /* infinite loops */ + + off_t header_len = 0; /* if the content was short enough, it might have a header already in the pipe */ if (con->recv_raw->first && chunkqueue_length(con->recv_raw) - con->recv_raw->first->offset > 0) { /* pipelining */ + + off_t offset = con->recv_raw->first->offset; switch (http_request_parse_cq(con->recv_raw, con->http_req)) { case PARSE_ERROR: @@ -1032,10 +1068,36 @@ connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); break; case PARSE_NEED_MORE: + header_len = chunkqueue_written(con->recv_raw) - offset; + if (con->recv_raw && srv->srvconf.max_request_header_size != 0 + && header_len > (srv->srvconf.max_request_header_size << 10)) { + + ERROR("request header length is too long, read_length=%d, client-ip[%s]", + (int)header_len, SAFE_BUF_STR(con->dst_addr_buf)); + con->http_status = 400; + con->keep_alive = 0; + chunkqueue_remove_finished_chunks(con->recv_raw); + + connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); + break; + } /* the read() call is on the way */ connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER); break; case PARSE_SUCCESS: + header_len = chunkqueue_written(con->recv_raw) - offset; + if (con->recv_raw && srv->srvconf.max_request_header_size != 0 + && header_len > (srv->srvconf.max_request_header_size << 10) ) { + + ERROR("request header length is too long, read_length=%d, client-ip[%s]", + (int)header_len, SAFE_BUF_STR(con->dst_addr_buf)); + con->http_status = 400; + con->keep_alive = 0; + + chunkqueue_remove_finished_chunks(con->recv_raw); + connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); + break; + } /* pipelining worked, validate the header */ chunkqueue_remove_finished_chunks(con->recv_raw);