X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=b2b8a37943debcb8b75ee827e1add2379938cb67;hp=294255c237721353a223388f42faba683d7df626;hb=61dc91dd24280d66ff6ca6ae7b72a37c7ef032c7;hpb=f744c407d0d497dd9393ddc52031f2cf959ab19d diff --git a/jcc.c b/jcc.c index 294255c2..b2b8a379 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.390 2012/10/21 12:30:37 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.407 2012/10/23 10:20:14 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -1079,7 +1079,7 @@ void save_connection_destination(jb_socket sfd, static void verify_request_length(struct client_state *csp) { unsigned long long buffered_request_bytes = - (unsigned long long)(csp->iob->eod - csp->iob->cur); + (unsigned long long)(csp->client_iob->eod - csp->client_iob->cur); if ((csp->expected_client_content_length != 0) && (buffered_request_bytes != 0)) @@ -1093,8 +1093,8 @@ static void verify_request_length(struct client_state *csp) } else { - assert(csp->iob->eod > csp->iob->cur + csp->expected_client_content_length); - csp->iob->eod = csp->iob->cur + csp->expected_client_content_length; + assert(csp->client_iob->eod > csp->client_iob->cur + csp->expected_client_content_length); + csp->client_iob->eod = csp->client_iob->cur + csp->expected_client_content_length; log_error(LOG_LEVEL_CONNECT, "Reducing expected bytes to 0. " "Marking the server socket tainted after throwing %llu bytes away.", buffered_request_bytes - csp->expected_client_content_length); @@ -1109,9 +1109,8 @@ static void verify_request_length(struct client_state *csp) } if (!(csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ) - && ((csp->iob->cur[0] != '\0') || (csp->expected_client_content_length != 0))) + && ((csp->client_iob->cur[0] != '\0') || (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 +1119,35 @@ static void verify_request_length(struct client_state *csp) { /* XXX: this is an incomplete hack */ csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; log_error(LOG_LEVEL_CONNECT, "There might be a request body. The connection will not be kept alive."); } 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->iob->buf[0] = '\0'; - csp->iob->eod = csp->iob->cur = csp->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 @@ -1196,6 +1211,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)) @@ -1225,12 +1261,12 @@ static char *get_request_line(struct client_state *csp) * If there is no memory left for buffering the * request, there is nothing we can do but hang up */ - if (add_to_iob(csp, buf, len)) + if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len)) { return NULL; } - request_line = get_header(csp->iob); + request_line = get_header(csp->client_iob); } while ((NULL != request_line) && ('\0' == *request_line)); @@ -1265,6 +1301,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)); @@ -1322,7 +1361,7 @@ static jb_err receive_client_request(struct client_state *csp) init_list(headers); for (;;) { - p = get_header(csp->iob); + p = get_header(csp->client_iob); if (p == NULL) { @@ -1352,7 +1391,7 @@ static jb_err receive_client_request(struct client_state *csp) return JB_ERR_PARSE; } - if (add_to_iob(csp, buf, len)) + if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len)) { /* * If there is no memory left for buffering the @@ -1539,7 +1578,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 +1690,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,8 +1750,11 @@ 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; hdr = list_to_text(csp->headers); if (hdr == NULL) { @@ -1714,22 +1767,31 @@ static void chat(struct client_state *csp) * Write the client's (modified) header to the server * (along with anything else that may be in the buffer) */ - if (write_socket(csp->server_connection.sfd, hdr, strlen(hdr)) - || (flush_socket(csp->server_connection.sfd, csp->iob) < 0)) + write_failure = 0 != write_socket(csp->server_connection.sfd, hdr, strlen(hdr)); + freez(hdr); + + if (write_failure) { log_error(LOG_LEVEL_CONNECT, - "write header to: %s failed: %E", http->hostport); + "Failed sending request headers to: %s: %E", http->hostport); + } + 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, + "Failed sending request body to: %s: %E", http->hostport); + } + if (write_failure) + { rsp = error_response(csp, "connect-failed"); if (rsp) { send_crunch_response(csp, rsp); } - - freez(hdr); return; } - freez(hdr); } else { @@ -1743,7 +1805,7 @@ static void chat(struct client_state *csp) { return; } - IOB_RESET(csp->iob); + clear_iob(csp->client_iob); } log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport); @@ -1759,6 +1821,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__ @@ -1967,7 +2033,6 @@ static void chat(struct client_state *csp) } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - fflush(NULL); len = read_socket(csp->server_connection.sfd, buf, sizeof(buf) - 1); if (len < 0) @@ -2144,7 +2209,7 @@ static void chat(struct client_state *csp) * has been reached, switch to non-filtering mode, i.e. make & write the * header, flush the iob and buf, and get out of the way. */ - if (add_to_iob(csp, buf, len)) + if (add_to_iob(csp->iob, csp->config->buffer_limit, buf, len)) { size_t hdrlen; long flushed; @@ -2208,7 +2273,7 @@ static void chat(struct client_state *csp) * Buffer up the data we just read. If that fails, there's * little we can do but send our static out-of-memory page. */ - if (add_to_iob(csp, buf, len)) + if (add_to_iob(csp->iob, csp->config->buffer_limit, buf, len)) { log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers."); rsp = cgi_error_memory(); @@ -2459,8 +2524,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); - freez(csp->iob->buf); - memset(csp->iob, 0, sizeof(csp->iob)); + clear_iob(csp->iob); freez(csp->error_message); free_http_request(csp->http); destroy_list(csp->headers); @@ -2477,6 +2541,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 */ @@ -2501,6 +2596,7 @@ 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; @@ -2513,7 +2609,6 @@ static void serve(struct client_state *csp) do { unsigned int latency; - int config_file_change_detected = 0; /* Only used for debugging */ chat(csp); @@ -2524,30 +2619,49 @@ 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->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) - || (csp->flags & CSP_FLAG_CRUNCHED)) && (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); - if (continue_chatting && !(csp->flags & CSP_FLAG_CRUNCHED)) + if (!(csp->flags & CSP_FLAG_CRUNCHED) + && (csp->server_connection.sfd != JB_INVALID_SOCKET)) { - continue_chatting = (csp->server_connection.sfd != JB_INVALID_SOCKET) - && socket_is_still_alive(csp->server_connection.sfd); - if (continue_chatting) + if (!(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET)) + { + csp->server_connection.keep_alive_timeout = csp->config->default_server_timeout; + } + if (!(csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) + || (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) + || !socket_is_still_alive(csp->server_connection.sfd) + || !(latency < csp->server_connection.keep_alive_timeout)) { - if (!(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET)) + log_error(LOG_LEVEL_CONNECT, + "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->server_connection.keep_alive_timeout); +#ifdef FEATURE_CONNECTION_SHARING + if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) { - csp->server_connection.keep_alive_timeout = csp->config->default_server_timeout; - log_error(LOG_LEVEL_CONNECT, - "The server didn't specify how long the connection will stay open. " - "Assumed timeout is: %u.", csp->server_connection.keep_alive_timeout); + forget_connection(csp->server_connection.sfd); } - continue_chatting = (latency < csp->server_connection.keep_alive_timeout); +#endif /* def FEATURE_CONNECTION_SHARING */ + close_socket(csp->server_connection.sfd); + mark_connection_closed(&csp->server_connection); } } @@ -2559,13 +2673,20 @@ static void serve(struct client_state *csp) if (continue_chatting) { - unsigned int client_timeout = 1; /* XXX: Use something else here? */ + 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) { - client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency; log_error(LOG_LEVEL_CONNECT, "Waiting for the next client request on socket %d. " "Keeping the server socket %d to %s open.", @@ -2578,24 +2699,21 @@ 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)client_timeout) + && 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 { - if (0 != (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) - { - log_error(LOG_LEVEL_CONNECT, - "No additional client request received in time on socket %d.", - csp->cfd); - } #ifdef FEATURE_CONNECTION_SHARING if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) + && (csp->server_connection.sfd != JB_INVALID_SOCKET) && (socket_is_still_alive(csp->server_connection.sfd))) { time_t time_open = time(NULL) - csp->server_connection.timestamp; @@ -2627,15 +2745,13 @@ static void serve(struct client_state *csp) else if (csp->server_connection.sfd != JB_INVALID_SOCKET) { log_error(LOG_LEVEL_CONNECT, - "The connection on server socket %d to %s isn't reusable. Closing. " - "Server connection: keep-alive %u, tainted: %u, socket alive %u. " - "Client connection: socket alive: %u. Server 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), 0 != (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED), socket_is_still_alive(csp->server_connection.sfd), - socket_is_still_alive(csp->cfd), csp->server_connection.keep_alive_timeout, config_file_change_detected); } @@ -2663,9 +2779,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.", + "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)); + socket_is_still_alive(csp->cfd), data_is_available(csp->cfd, 0), + config_file_change_detected, csp->requests_received_total); drain_and_close_socket(csp->cfd); }