X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=7d3b72329fa895bf12ec7b4f98f576e279f67403;hp=059e9e6054fd2f56bc3350d370d3d3623d2be225;hb=356eb03150a520a88b079f81f92138b2d988d91f;hpb=7b0f08d4e99e42f0fa01a9155c20775da0d40679 diff --git a/jcc.c b/jcc.c index 059e9e60..7d3b7232 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.397 2012/10/21 12:51:07 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.416 2012/11/24 14:01:25 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -951,6 +951,7 @@ static int server_response_is_complete(struct client_state *csp, */ csp->expected_content_length = 0; content_length_known = TRUE; + csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET; } if (csp->http->status == 204 || csp->http->status == 304) @@ -960,6 +961,7 @@ static int server_response_is_complete(struct client_state *csp, */ csp->expected_content_length = 0; content_length_known = TRUE; + csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET; } return (content_length_known && ((0 == csp->expected_content_length) @@ -1064,11 +1066,9 @@ void save_connection_destination(jb_socket sfd, * we do. * * Data that doesn't belong to the current request is - * thrown away to let the client retry on a clean socket. - * - * XXX: This is a hack until we can deal with multiple - * pipelined requests at the same time. - * + * either thrown away to let the client retry on a clean + * socket, or stashed to be dealt with after the current + * request is served. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -1109,9 +1109,9 @@ static void verify_request_length(struct client_state *csp) } if (!(csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ) - && ((csp->client_iob->cur[0] != '\0') || (csp->expected_client_content_length != 0))) + && ((csp->client_iob->cur < csp->client_iob->eod) + || (csp->expected_client_content_length != 0))) { - csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; if (strcmpic(csp->http->gpc, "GET") && strcmpic(csp->http->gpc, "HEAD") && strcmpic(csp->http->gpc, "TRACE") @@ -1120,19 +1120,33 @@ static void verify_request_length(struct client_state *csp) { /* XXX: this is an incomplete hack */ csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, - "There might be a request body. The connection will not be kept alive."); + log_error(LOG_LEVEL_CONNECT, "There better be a request body."); } else { - /* XXX: and so is this */ csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, - "Possible pipeline attempt detected. The connection will not " - "be kept alive and we will only serve the first request."); - /* Nuke the pipelined requests from orbit, just to be sure. */ - csp->client_iob->buf[0] = '\0'; - csp->client_iob->eod = csp->client_iob->cur = csp->client_iob->buf; + + if ((csp->config->feature_flags & RUNTIME_FEATURE_TOLERATE_PIPELINING) == 0) + { + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; + log_error(LOG_LEVEL_CONNECT, + "Possible pipeline attempt detected. The connection will not " + "be kept alive and we will only serve the first request."); + /* Nuke the pipelined requests from orbit, just to be sure. */ + clear_iob(csp->client_iob); + } + else + { + /* + * Keep the pipelined data around for now, we'll deal with + * it once we're done serving the current request. + */ + csp->flags |= CSP_FLAG_PIPELINED_REQUEST_WAITING; + assert(csp->client_iob->eod >= csp->client_iob->cur); + log_error(LOG_LEVEL_CONNECT, "Complete client request followed by " + "%d bytes of pipelined data received.", + (int)(csp->client_iob->eod - csp->client_iob->cur)); + } } } else @@ -1167,7 +1181,7 @@ static void mark_server_socket_tainted(struct client_state *csp) * actually been reused. */ if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) - && !(csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED)) + && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)) { log_error(LOG_LEVEL_CONNECT, "Marking the server socket %d tainted.", @@ -1196,6 +1210,27 @@ static char *get_request_line(struct client_state *csp) memset(buf, 0, sizeof(buf)); + if ((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) != 0) + { + /* + * If there are multiple pipelined requests waiting, + * the flag will be set again once the next request + * has been parsed. + */ + csp->flags &= ~CSP_FLAG_PIPELINED_REQUEST_WAITING; + + request_line = get_header(csp->client_iob); + if ((NULL != request_line) && ('\0' != *request_line)) + { + return request_line; + } + else + { + log_error(LOG_LEVEL_CONNECT, "No complete request line " + "received yet. Continuing reading from %d.", csp->cfd); + } + } + do { if (!data_is_available(csp->cfd, csp->config->socket_timeout)) @@ -1265,6 +1300,9 @@ static jb_err receive_client_request(struct client_state *csp) struct list header_list; struct list *headers = &header_list; + /* We don't care if the arriving data is a valid HTTP request or not. */ + csp->requests_received_total++; + http = csp->http; memset(buf, 0, sizeof(buf)); @@ -1539,7 +1577,7 @@ static void chat(struct client_state *csp) struct http_response *rsp; struct timeval timeout; #ifdef FEATURE_CONNECTION_KEEP_ALIVE - int watch_client_socket = 1; + int watch_client_socket; #endif memset(buf, 0, sizeof(buf)); @@ -1651,17 +1689,28 @@ static void chat(struct client_state *csp) && connection_destination_matches(&csp->server_connection, http, fwd)) { log_error(LOG_LEVEL_CONNECT, - "Reusing server socket %u. Opened for %s.", - csp->server_connection.sfd, csp->server_connection.host); + "Reusing server socket %d connected to %s. Total requests: %u.", + csp->server_connection.sfd, csp->server_connection.host, + csp->server_connection.requests_sent_total); } else { if (csp->server_connection.sfd != JB_INVALID_SOCKET) { - log_error(LOG_LEVEL_CONNECT, - "Closing server socket %u. Opened for %s.", - csp->server_connection.sfd, csp->server_connection.host); - close_socket(csp->server_connection.sfd); +#ifdef FEATURE_CONNECTION_SHARING + if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) + { + remember_connection(&csp->server_connection); + } + else +#endif /* def FEATURE_CONNECTION_SHARING */ + { + log_error(LOG_LEVEL_CONNECT, + "Closing server socket %d connected to %s. Total requests: %u.", + csp->server_connection.sfd, csp->server_connection.host, + csp->server_connection.requests_sent_total); + close_socket(csp->server_connection.sfd); + } mark_connection_closed(&csp->server_connection); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1700,6 +1749,8 @@ static void chat(struct client_state *csp) } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + csp->server_connection.requests_sent_total++; + if (fwd->forward_host || (http->ssl == 0)) { int write_failure; @@ -1723,7 +1774,8 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "Failed sending request headers to: %s: %E", http->hostport); } - else if (flush_socket(csp->server_connection.sfd, csp->client_iob) < 0)) + else if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0) + && (flush_socket(csp->server_connection.sfd, csp->client_iob) < 0)) { write_failure = 1; log_error(LOG_LEVEL_CONNECT, @@ -1752,7 +1804,7 @@ static void chat(struct client_state *csp) { return; } - IOB_RESET(csp->client_iob); + clear_iob(csp->client_iob); } log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport); @@ -1768,6 +1820,10 @@ static void chat(struct client_state *csp) server_body = 0; +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + watch_client_socket = 0 == (csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING); +#endif + for (;;) { #ifdef __OS2__ @@ -2467,7 +2523,7 @@ static void prepare_csp_for_next_request(struct client_state *csp) csp->expected_content_length = 0; csp->expected_client_content_length = 0; list_remove_all(csp->headers); - IOB_RESET(csp->iob); + clear_iob(csp->iob); freez(csp->error_message); free_http_request(csp->http); destroy_list(csp->headers); @@ -2484,6 +2540,37 @@ static void prepare_csp_for_next_request(struct client_state *csp) { csp->flags |= CSP_FLAG_TOGGLED_ON; } + + if (csp->client_iob->eod > csp->client_iob->cur) + { + long bytes_to_shift = csp->client_iob->cur - csp->client_iob->buf; + size_t data_length = (size_t)(csp->client_iob->eod - csp->client_iob->cur); + + assert(bytes_to_shift > 0); + assert(data_length > 0); + + log_error(LOG_LEVEL_CONNECT, "Shifting %d pipelined bytes by %d bytes", + data_length, bytes_to_shift); + memmove(csp->client_iob->buf, csp->client_iob->cur, data_length); + csp->client_iob->cur = csp->client_iob->buf; + assert(csp->client_iob->eod == csp->client_iob->buf + bytes_to_shift + data_length); + csp->client_iob->eod = csp->client_iob->buf + data_length; + memset(csp->client_iob->eod, '\0', (size_t)bytes_to_shift); + + csp->flags |= CSP_FLAG_PIPELINED_REQUEST_WAITING; + } + else + { + /* + * We mainly care about resetting client_iob->cur so we don't + * waste buffer space at the beginning and don't mess up the + * request restoration done by cgi_show_request(). + * + * Freeing the buffer itself isn't technically necessary, + * but makes debugging more convenient. + */ + clear_iob(csp->client_iob); + } } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -2508,12 +2595,12 @@ void serve(struct client_state *csp) static void serve(struct client_state *csp) #endif /* def AMIGA */ { + int config_file_change_detected = 0; /* Only used for debugging */ #ifdef FEATURE_CONNECTION_KEEP_ALIVE #ifdef FEATURE_CONNECTION_SHARING static int monitor_thread_running = 0; #endif /* def FEATURE_CONNECTION_SHARING */ int continue_chatting = 0; - int config_file_change_detected = 0; /* Only used for debugging */ log_error(LOG_LEVEL_CONNECT, "Accepted connection from %s on socket %d", csp->ip_addr_str, csp->cfd); @@ -2531,13 +2618,22 @@ static void serve(struct client_state *csp) latency = (unsigned)(csp->server_connection.response_received - csp->server_connection.request_sent) / 2; + if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) + && (csp->flags & CSP_FLAG_CRUNCHED) + && (csp->expected_client_content_length != 0)) + { + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; + log_error(LOG_LEVEL_CONNECT, + "Tainting client socket %d due to unread data.", csp->cfd); + } + continue_chatting = (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) && (csp->cfd != JB_INVALID_SOCKET) - && ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) - || (csp->config->feature_flags & - RUNTIME_FEATURE_CONNECTION_SHARING)); + && (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) + && ((csp->flags & CSP_FLAG_SERVER_CONTENT_LENGTH_SET) + || (csp->flags & CSP_FLAG_CHUNKED)); if (!(csp->flags & CSP_FLAG_CRUNCHED) && (csp->server_connection.sfd != JB_INVALID_SOCKET)) @@ -2552,14 +2648,13 @@ static void serve(struct client_state *csp) || !(latency < csp->server_connection.keep_alive_timeout)) { log_error(LOG_LEVEL_CONNECT, - "Closing connection on server socket %d to %s: " - "keep-alive %u, tainted: %u, socket alive %u. %s timeout: %u.", + "Closing server socket %d connected to %s. " + "Keep-alive %u. Tainted: %u. Socket alive %u. Timeout: %u.", csp->server_connection.sfd, csp->server_connection.host, 0 != (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE), 0 != (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED), socket_is_still_alive(csp->server_connection.sfd), - ((csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET) ? - "Specified" : "Assumed"), csp->server_connection.keep_alive_timeout); + csp->server_connection.keep_alive_timeout); #ifdef FEATURE_CONNECTION_SHARING if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) { @@ -2579,6 +2674,16 @@ static void serve(struct client_state *csp) if (continue_chatting) { + if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) != 0) + && socket_is_still_alive(csp->cfd)) + { + log_error(LOG_LEVEL_CONNECT, "Client request %d has been " + "pipelined on socket %d and the socket is still alive.", + csp->requests_received_total+1, csp->cfd); + prepare_csp_for_next_request(csp); + continue; + } + if (0 != (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) { if (csp->server_connection.sfd != JB_INVALID_SOCKET) @@ -2595,12 +2700,14 @@ static void serve(struct client_state *csp) "No server socket to keep open.", csp->cfd); } } + if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) && data_is_available(csp->cfd, (int)csp->config->keep_alive_timeout) && socket_is_still_alive(csp->cfd)) { log_error(LOG_LEVEL_CONNECT, - "Client request arrived in time on socket %d.", csp->cfd); + "Client request %u arrived in time on socket %d.", + csp->requests_received_total+1, csp->cfd); prepare_csp_for_next_request(csp); } else @@ -2639,8 +2746,8 @@ static void serve(struct client_state *csp) else if (csp->server_connection.sfd != JB_INVALID_SOCKET) { log_error(LOG_LEVEL_CONNECT, - "Closing server socket %d connected to %s. Keep-alive %u. " - "Tainted: %u. Socket alive %u. Timeout: %u. " + "Closing server socket %d connected to %s. Keep-alive: %u. " + "Tainted: %u. Socket alive: %u. Timeout: %u. " "Configuration file change detected: %u", csp->server_connection.sfd, csp->server_connection.host, 0 != (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE), @@ -2673,11 +2780,11 @@ static void serve(struct client_state *csp) if (csp->cfd != JB_INVALID_SOCKET) { log_error(LOG_LEVEL_CONNECT, "Closing client socket %d. " - "Keep-alive: %u, Socket alive: %u. Data available: %u. " - "Configuration file change detected: %u.", + "Keep-alive: %u. Socket alive: %u. Data available: %u. " + "Configuration file change detected: %u. Requests received: %u.", csp->cfd, 0 != (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE), socket_is_still_alive(csp->cfd), data_is_available(csp->cfd, 0), - config_file_change_detected); + config_file_change_detected, csp->requests_received_total); drain_and_close_socket(csp->cfd); } @@ -2723,7 +2830,7 @@ static int32 server_thread(void *data) static void usage(const char *myname) { printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n" - "Usage: %s " + "Usage: %s [--config-test] " #if defined(unix) "[--chroot] " #endif /* defined(unix) */ @@ -2913,6 +3020,7 @@ int main(int argc, char **argv) #endif { int argc_pos = 0; + int do_config_test = 0; unsigned int random_seed; #ifdef unix struct passwd *pw = NULL; @@ -3043,6 +3151,11 @@ int main(int argc, char **argv) } #endif /* defined(unix) */ + else if (strcmp(argv[argc_pos], "--config-test") == 0) + { + do_config_test = 1; + } + else if (argc_pos + 1 != argc) { /* @@ -3146,6 +3259,10 @@ int main(int argc, char **argv) # endif /* def _WIN_CONSOLE */ #endif /* def _WIN32 */ + if (do_config_test) + { + exit(NULL == load_config()); + } /* Initialize the CGI subsystem */ cgi_init_error_messages();