X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=20647fa708ee6c3b389555818331f3c95b8e117f;hp=85be466c1e091cdc9df134ba5af4b0325d8a75c0;hb=54b4e8c141117d636f6113ec4c43579248b77ed7;hpb=83aaee02fdd7ff7a778f0410a311fdc68f14bcc2 diff --git a/jcc.c b/jcc.c index 85be466c..20647fa7 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.262 2009/07/05 12:02:25 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.310 2009/12/29 13:17:37 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -119,7 +119,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.262 2009/07/05 12:02:25 fabiankeil Exp $" const char jcc_h_rcs[] = JCC_H_VERSION; const char project_h_rcs[] = PROJECT_H_VERSION; -int no_daemon = 0; +int daemon_mode = 1; struct client_state clients[1]; struct file_list files[1]; @@ -724,19 +724,21 @@ static void send_crunch_response(const struct client_state *csp, struct http_res status_code[2] = rsp->head[11]; status_code[3] = '\0'; + /* Log that the request was crunched and why. */ + log_error(LOG_LEVEL_CRUNCH, "%s: %s", crunch_reason(rsp), http->url); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %u", + csp->ip_addr_str, http->ocmd, status_code, rsp->content_length); + /* Write the answer to the client */ if (write_socket(csp->cfd, rsp->head, rsp->head_length) || write_socket(csp->cfd, rsp->body, rsp->content_length)) { /* There is nothing we can do about it. */ - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", csp->http->host); + log_error(LOG_LEVEL_ERROR, + "Couldn't deliver the error message through client socket %d: %E", + csp->cfd); } - /* Log that the request was crunched and why. */ - log_error(LOG_LEVEL_CRUNCH, "%s: %s", crunch_reason(rsp), http->url); - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %u", - csp->ip_addr_str, http->ocmd, status_code, rsp->content_length); - /* Clean up and return */ if (cgi_error_memory() != rsp) { @@ -1026,6 +1028,7 @@ static int server_response_is_complete(struct client_state *csp, } +#ifdef FEATURE_CONNECTION_SHARING /********************************************************************* * * Function : wait_for_alive_connections @@ -1053,6 +1056,7 @@ static void wait_for_alive_connections(void) log_error(LOG_LEVEL_CONNECT, "No connections to wait for left."); } +#endif /* def FEATURE_CONNECTION_SHARING */ /********************************************************************* @@ -1077,6 +1081,8 @@ void save_connection_destination(jb_socket sfd, { assert(sfd != JB_INVALID_SOCKET); assert(NULL != http->host); + + server_connection->sfd = sfd; server_connection->host = strdup(http->host); if (NULL == server_connection->host) { @@ -1120,6 +1126,94 @@ void save_connection_destination(jb_socket sfd, } server_connection->forward_port = fwd->forward_port; } + + +/********************************************************************* + * + * Function : verify_request_length + * + * Description : Checks if we already got the whole client requests + * and sets CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ if + * 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. + * + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : void + * + *********************************************************************/ +static void verify_request_length(struct client_state *csp) +{ + unsigned long long buffered_request_bytes = + (unsigned long long)(csp->iob->eod - csp->iob->cur); + + if ((csp->expected_client_content_length != 0) + && (buffered_request_bytes != 0)) + { + if (csp->expected_client_content_length >= buffered_request_bytes) + { + csp->expected_client_content_length -= buffered_request_bytes; + log_error(LOG_LEVEL_CONNECT, "Reduced expected bytes to %llu " + "to account for the %llu ones we already got.", + csp->expected_client_content_length, buffered_request_bytes); + } + else + { + assert(csp->iob->eod > csp->iob->cur + csp->expected_client_content_length); + csp->iob->eod = csp->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); + csp->expected_client_content_length = 0; + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; + } + + if (csp->expected_client_content_length == 0) + { + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + } + } + + if (!(csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ) + && ((csp->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") + && strcmpic(csp->http->gpc, "OPTIONS") + && strcmpic(csp->http->gpc, "DELETE")) + { + /* 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."); + } + 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; + } + } + else + { + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + log_error(LOG_LEVEL_CONNECT, "Complete client request received."); + } +} #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1139,10 +1233,12 @@ void save_connection_destination(jb_socket sfd, *********************************************************************/ static void mark_server_socket_tainted(struct client_state *csp) { - if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)) + if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) + && !(csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED)) { log_error(LOG_LEVEL_CONNECT, - "Marking the server socket %d tainted.", csp->sfd); + "Marking the server socket %d tainted.", + csp->server_connection.sfd); csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; } } @@ -1171,8 +1267,9 @@ static char *get_request_line(struct client_state *csp) { if (!data_is_available(csp->cfd, csp->config->socket_timeout)) { - log_error(LOG_LEVEL_ERROR, - "Stopped waiting for the request line."); + log_error(LOG_LEVEL_CONNECT, + "Stopped waiting for the request line. Timeout: %d.", + csp->config->socket_timeout); write_socket(csp->cfd, CLIENT_CONNECTION_TIMEOUT_RESPONSE, strlen(CLIENT_CONNECTION_TIMEOUT_RESPONSE)); return NULL; @@ -1237,6 +1334,7 @@ static jb_err receive_client_request(struct client_state *csp) req = get_request_line(csp); if (req == NULL) { + mark_server_socket_tainted(csp); return JB_ERR_PARSE; } assert(*req != '\0'); @@ -1415,6 +1513,16 @@ static jb_err parse_client_request(struct client_state *csp) struct http_request *http = csp->http; jb_err err; +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) + && (!strcmpic(csp->http->ver, "HTTP/1.1")) + && (csp->http->ssl == 0)) + { + /* Assume persistence until further notice */ + csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + err = sed(csp, FILTER_CLIENT_HEADERS); if (JB_ERR_OK != err) { @@ -1444,35 +1552,9 @@ static jb_err parse_client_request(struct client_state *csp) } #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) + if (csp->http->ssl == 0) { - if (csp->iob->cur[0] != '\0') - { - csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; - if (!strcmpic(csp->http->gpc, "POST")) - { - /* XXX: this is an incomplete hack */ - csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, - "POST request detected. 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; - } - } - else - { - csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - log_error(LOG_LEVEL_CONNECT, "Complete client request received."); - } + verify_request_length(csp); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1523,6 +1605,9 @@ static void chat(struct client_state *csp) /* Skeleton for HTTP response, if we should intercept the request */ struct http_response *rsp; struct timeval timeout; +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + int watch_client_socket = 1; +#endif memset(buf, 0, sizeof(buf)); @@ -1631,28 +1716,27 @@ static void chat(struct client_state *csp) /* here we connect to the server, gateway, or the forwarder */ #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->sfd != JB_INVALID_SOCKET) - && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) - && socket_is_still_usable(csp->sfd) + if ((csp->server_connection.sfd != JB_INVALID_SOCKET) + && socket_is_still_usable(csp->server_connection.sfd) && connection_destination_matches(&csp->server_connection, http, fwd)) { log_error(LOG_LEVEL_CONNECT, "Reusing server socket %u. Opened for %s.", - csp->sfd, csp->server_connection.host); + csp->server_connection.sfd, csp->server_connection.host); } else { - if (csp->sfd != JB_INVALID_SOCKET) + if (csp->server_connection.sfd != JB_INVALID_SOCKET) { log_error(LOG_LEVEL_CONNECT, "Closing server socket %u. Opened for %s.", - csp->sfd, csp->server_connection.host); - close_socket(csp->sfd); + csp->server_connection.sfd, csp->server_connection.host); + close_socket(csp->server_connection.sfd); mark_connection_closed(&csp->server_connection); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - while ((csp->sfd = forwarded_connect(fwd, http, csp)) + while ((csp->server_connection.sfd = forwarded_connect(fwd, http, csp)) && (errno == EINVAL) && (forwarded_connect_retries++ < max_forwarded_connect_retries)) { @@ -1661,7 +1745,7 @@ static void chat(struct client_state *csp) forwarded_connect_retries, http->hostport); } - if (csp->sfd == JB_INVALID_SOCKET) + if (csp->server_connection.sfd == JB_INVALID_SOCKET) { if (fwd->type != SOCKS_NONE) { @@ -1688,8 +1772,10 @@ static void chat(struct client_state *csp) return; } #ifdef FEATURE_CONNECTION_KEEP_ALIVE - save_connection_destination(csp->sfd, http, fwd, &csp->server_connection); - csp->server_connection.keep_alive_timeout = (unsigned)csp->config->keep_alive_timeout; + save_connection_destination(csp->server_connection.sfd, + http, fwd, &csp->server_connection); + csp->server_connection.keep_alive_timeout = + (unsigned)csp->config->keep_alive_timeout; } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1707,8 +1793,8 @@ 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->sfd, hdr, strlen(hdr)) - || (flush_socket(csp->sfd, csp->iob) < 0)) + if (write_socket(csp->server_connection.sfd, hdr, strlen(hdr)) + || (flush_socket(csp->server_connection.sfd, csp->iob) < 0)) { log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E", http->hostport); @@ -1740,10 +1826,13 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport); + csp->server_connection.request_sent = time(NULL); + /* we're finished with the client's header */ freez(hdr); - maxfd = (csp->cfd > csp->sfd) ? csp->cfd : csp->sfd; + maxfd = (csp->cfd > csp->server_connection.sfd) ? + csp->cfd : csp->server_connection.sfd; /* pass data between the client and server * until one or the other shuts down the connection. @@ -1763,9 +1852,9 @@ static void chat(struct client_state *csp) FD_ZERO(&rfds); #endif #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) + if (!watch_client_socket) { - maxfd = csp->sfd; + maxfd = csp->server_connection.sfd; } else #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1773,7 +1862,7 @@ static void chat(struct client_state *csp) FD_SET(csp->cfd, &rfds); } - FD_SET(csp->sfd, &rfds); + FD_SET(csp->server_connection.sfd, &rfds); #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CHUNKED) @@ -1790,10 +1879,20 @@ static void chat(struct client_state *csp) } if (server_body && server_response_is_complete(csp, byte_count)) { - log_error(LOG_LEVEL_CONNECT, - "Done reading from server. Expected content length: %llu. " - "Actual content length: %llu. Most recently received: %d.", - csp->expected_content_length, byte_count, len); + if (csp->expected_content_length == byte_count) + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from server. Content length: %llu as expected. " + "Bytes most recently read: %d.", + byte_count, len); + } + else + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from server. Expected content length: %llu. " + "Actual content length: %llu. Bytes most recently read: %d.", + csp->expected_content_length, byte_count, len); + } len = 0; /* * XXX: should not jump around, @@ -1834,7 +1933,51 @@ static void chat(struct client_state *csp) */ if (FD_ISSET(csp->cfd, &rfds)) { - len = read_socket(csp->cfd, buf, sizeof(buf) - 1); + int max_bytes_to_read = sizeof(buf) - 1; + +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) + { + if (data_is_available(csp->cfd, 0)) + { + /* + * If the next request is already waiting, we have + * to stop select()ing the client socket. Otherwise + * we would always return right away and get nothing + * else done. + */ + watch_client_socket = 0; + log_error(LOG_LEVEL_CONNECT, + "Stopping to watch the client socket. " + "There's already another request waiting."); + continue; + } + /* + * If the client socket is set, but there's no data + * available on the socket, the client went fishing + * and continuing talking to the server makes no sense. + */ + log_error(LOG_LEVEL_CONNECT, + "The client closed socket %d while " + "the server socket %d is still open.", + csp->cfd, csp->server_connection.sfd); + mark_server_socket_tainted(csp); + break; + } + if (csp->expected_client_content_length != 0) + { + if (csp->expected_client_content_length < (sizeof(buf) - 1)) + { + max_bytes_to_read = (int)csp->expected_client_content_length; + } + log_error(LOG_LEVEL_CONNECT, + "Waiting for up to %d bytes from the client.", + max_bytes_to_read); + } + assert(max_bytes_to_read < sizeof(buf)); +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + + len = read_socket(csp->cfd, buf, max_bytes_to_read); if (len <= 0) { @@ -1843,7 +1986,25 @@ static void chat(struct client_state *csp) break; /* "game over, man" */ } - if (write_socket(csp->sfd, buf, (size_t)len)) +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (csp->expected_client_content_length != 0) + { + assert(len <= max_bytes_to_read); + csp->expected_client_content_length -= (unsigned)len; + log_error(LOG_LEVEL_CONNECT, + "Expected client content length set to %llu " + "after reading %d bytes.", + csp->expected_client_content_length, len); + if (csp->expected_client_content_length == 0) + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from the client."); + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + } + } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + + if (write_socket(csp->server_connection.sfd, buf, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); mark_server_socket_tainted(csp); @@ -1857,10 +2018,25 @@ static void chat(struct client_state *csp) * If `hdr' is null, then it's the header otherwise it's the body. * FIXME: Does `hdr' really mean `host'? No. */ - if (FD_ISSET(csp->sfd, &rfds)) + if (FD_ISSET(csp->server_connection.sfd, &rfds)) { +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (!socket_is_still_usable(csp->cfd)) + { +#ifdef _WIN32 + log_error(LOG_LEVEL_CONNECT, + "The server still wants to talk, but the client may already have hung up on us."); +#else + log_error(LOG_LEVEL_CONNECT, + "The server still wants to talk, but the client hung up on us."); + mark_server_socket_tainted(csp); + return; +#endif /* def _WIN32 */ + } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + fflush(NULL); - len = read_socket(csp->sfd, buf, sizeof(buf) - 1); + len = read_socket(csp->server_connection.sfd, buf, sizeof(buf) - 1); if (len < 0) { @@ -2081,7 +2257,6 @@ static void chat(struct client_state *csp) } else { - const char *header_start; /* * We're still looking for the end of the server's header. * Buffer up the data we just read. If that fails, there's @@ -2096,8 +2271,6 @@ static void chat(struct client_state *csp) return; } - header_start = csp->iob->cur; - /* Convert iob into something sed() can digest */ if (JB_ERR_PARSE == get_server_headers(csp)) { @@ -2123,23 +2296,40 @@ static void chat(struct client_state *csp) * Since we have to wait for more from the server before * we can parse the headers we just continue here. */ - long header_offset = csp->iob->cur - header_start; - assert(csp->iob->cur >= header_start); - byte_count += (unsigned long long)(len - header_offset); - log_error(LOG_LEVEL_CONNECT, "Continuing buffering headers. " - "byte_count: %llu. header_offset: %d. len: %d.", - byte_count, header_offset, len); + log_error(LOG_LEVEL_CONNECT, + "Continuing buffering headers. Bytes most recently read: %d.", + len); continue; } } + else + { + /* + * Account for the content bytes we + * might have gotten with the headers. + */ + assert(csp->iob->eod >= csp->iob->cur); + byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur); + } /* Did we actually get anything? */ if (NULL == csp->headers->first) { - log_error(LOG_LEVEL_ERROR, - "Empty server or forwarder response received on socket %d.", csp->sfd); + if ((csp->flags & CSP_FLAG_REUSED_CLIENT_CONNECTION)) + { + log_error(LOG_LEVEL_ERROR, + "Empty server or forwarder response received on socket %d. " + "Closing client connection %d without sending data.", + csp->server_connection.sfd, csp->cfd); + } + else + { + log_error(LOG_LEVEL_ERROR, + "Empty server or forwarder response received on socket %d.", + csp->server_connection.sfd); + send_crunch_response(csp, error_response(csp, "no-server-data")); + } log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); - send_crunch_response(csp, error_response(csp, "no-server-data")); free_http_request(http); mark_server_socket_tainted(csp); return; @@ -2185,6 +2375,8 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); } + csp->server_connection.response_received = time(NULL); + if (crunch_response_triggered(csp, crunchers_light)) { /* @@ -2227,18 +2419,6 @@ static void chat(struct client_state *csp) mark_server_socket_tainted(csp); return; } - - byte_count += (unsigned long long)len; - } - else - { - /* - * XXX: the header lenght should probably - * be calculated by get_server_headers(). - */ - long header_length = csp->iob->cur - header_start; - assert(csp->iob->cur > header_start); - byte_count += (unsigned long long)(len - header_length); } /* we're finished with the server's header */ @@ -2292,6 +2472,8 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %llu", csp->ip_addr_str, http->ocmd, csp->content_length); + + csp->server_connection.timestamp = time(NULL); } @@ -2316,33 +2498,57 @@ static void serve(struct client_state *csp) #endif /* def AMIGA */ { #ifdef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef FEATURE_CONNECTION_SHARING static int monitor_thread_running = 0; +#endif /* def FEATURE_CONNECTION_SHARING */ int continue_chatting = 0; + do { + unsigned int latency; + chat(csp); + latency = (unsigned)(csp->server_connection.response_received - + csp->server_connection.request_sent) / 2; + continue_chatting = (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) && (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) && (csp->cfd != JB_INVALID_SOCKET) - && (csp->sfd != JB_INVALID_SOCKET) - && socket_is_still_usable(csp->sfd); + && (csp->server_connection.sfd != JB_INVALID_SOCKET) + && socket_is_still_usable(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; + 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); + } + continue_chatting = (latency < csp->server_connection.keep_alive_timeout); + } + + if (continue_chatting) + { + unsigned int client_timeout; + + client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency; + log_error(LOG_LEVEL_CONNECT, - "Waiting for the next client request. " + "Waiting for the next client request on socket %d. " "Keeping the server socket %d to %s open.", - csp->sfd, csp->server_connection.host); - + csp->cfd, csp->server_connection.sfd, csp->server_connection.host); if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) - && data_is_available(csp->cfd, (int)csp->server_connection.keep_alive_timeout) + && data_is_available(csp->cfd, (int)client_timeout) && socket_is_still_usable(csp->cfd)) { log_error(LOG_LEVEL_CONNECT, "Client request arrived in " - "time or the client closed the connection."); + "time or the client closed the connection on socket %d.", + csp->cfd); /* * Get the csp in a mostly vergin state again. * XXX: Should be done elsewhere. @@ -2350,6 +2556,7 @@ static void serve(struct client_state *csp) csp->content_type = 0; csp->content_length = 0; 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)); @@ -2365,18 +2572,27 @@ static void serve(struct client_state *csp) } /* XXX: Store per-connection flags someplace else. */ - csp->flags = CSP_FLAG_ACTIVE | (csp->flags & CSP_FLAG_TOGGLED_ON); + csp->flags = CSP_FLAG_ACTIVE | + (csp->flags & CSP_FLAG_TOGGLED_ON) | CSP_FLAG_REUSED_CLIENT_CONNECTION; } else { log_error(LOG_LEVEL_CONNECT, - "No additional client request received in time."); - if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)) + "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) + && (socket_is_still_usable(csp->server_connection.sfd))) { - remember_connection(csp->sfd, csp->http, - forward_url(csp, csp->http), - csp->server_connection.keep_alive_timeout); - csp->sfd = JB_INVALID_SOCKET; + time_t time_open = time(NULL) - csp->server_connection.timestamp; + + if (csp->server_connection.keep_alive_timeout < time_open + latency) + { + break; + } + + remember_connection(&csp->server_connection); + csp->server_connection.sfd = JB_INVALID_SOCKET; close_socket(csp->cfd); csp->cfd = JB_INVALID_SOCKET; privoxy_mutex_lock(&connection_reuse_mutex); @@ -2390,30 +2606,37 @@ static void serve(struct client_state *csp) } privoxy_mutex_unlock(&connection_reuse_mutex); } +#endif /* def FEATURE_CONNECTION_SHARING */ break; } } - else if (csp->sfd != JB_INVALID_SOCKET) + 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.", csp->sfd, csp->server_connection.host); + "Closing.", csp->server_connection.sfd, csp->server_connection.host); } } while (continue_chatting); - mark_connection_closed(&csp->server_connection); #else chat(csp); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - if (csp->sfd != JB_INVALID_SOCKET) + if (csp->server_connection.sfd != JB_INVALID_SOCKET) { -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - forget_connection(csp->sfd); -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - close_socket(csp->sfd); +#ifdef FEATURE_CONNECTION_SHARING + if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) + { + forget_connection(csp->server_connection.sfd); + } +#endif /* def FEATURE_CONNECTION_SHARING */ + close_socket(csp->server_connection.sfd); } +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + mark_connection_closed(&csp->server_connection); +#endif + if (csp->cfd != JB_INVALID_SOCKET) { close_socket(csp->cfd); @@ -2644,9 +2867,9 @@ static void initialize_mutexes(void) * *********************************************************************/ #ifdef __MINGW32__ -int real_main(int argc, const char *argv[]) +int real_main(int argc, char **argv) #else -int main(int argc, const char *argv[]) +int main(int argc, char **argv) #endif { int argc_pos = 0; @@ -2728,7 +2951,7 @@ int main(int argc, const char *argv[]) else if (strcmp(argv[argc_pos], "--no-daemon" ) == 0) { set_debug_level(LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO); - no_daemon = 1; + daemon_mode = 0; } else if (strcmp(argv[argc_pos], "--pidfile" ) == 0) @@ -2895,12 +3118,11 @@ int main(int argc, const char *argv[]) #if defined(unix) { pid_t pid = 0; -#if 0 - int fd; -#endif - if (!no_daemon) + if (daemon_mode) { + int fd; + pid = fork(); if ( pid < 0 ) /* error */ @@ -2925,33 +3147,50 @@ int main(int argc, const char *argv[]) exit( 0 ); } /* child */ -#if 1 - /* Should be more portable, but not as well tested */ + setsid(); -#else /* !1 */ -#ifdef __FreeBSD__ - setpgrp(0,0); -#else /* ndef __FreeBSD__ */ - setpgrp(); -#endif /* ndef __FreeBSD__ */ - fd = open("/dev/tty", O_RDONLY); - if ( fd ) - { - /* no error check here */ - ioctl( fd, TIOCNOTTY,0 ); - close ( fd ); - } -#endif /* 1 */ + /* * stderr (fd 2) will be closed later on, * when the config file has been parsed. */ + close(0); + close(1); + + /* + * Reserve fd 0 and 1 to prevent abort() and friends + * from sending stuff to the clients or servers. + */ + fd = open("/dev/null", O_RDONLY); + if (fd == -1) + { + log_error(LOG_LEVEL_FATAL, "Failed to open /dev/null: %E"); + } + else if (fd != 0) + { + if (dup2(fd, 0) == -1) + { + log_error(LOG_LEVEL_FATAL, "Failed to reserve fd 0: %E"); + } + close(fd); + } + fd = open("/dev/null", O_WRONLY); + if (fd == -1) + { + log_error(LOG_LEVEL_FATAL, "Failed to open /dev/null: %E"); + } + else if (fd != 1) + { + if (dup2(fd, 1) == -1) + { + log_error(LOG_LEVEL_FATAL, "Failed to reserve fd 1: %E"); + } + close(fd); + } - close( 0 ); - close( 1 ); chdir("/"); - } /* -END- if (!no_daemon) */ + } /* -END- if (daemon_mode) */ /* * As soon as we have written the PID file, we can switch @@ -3160,13 +3399,13 @@ static void listen_loop(void) config = load_config(); -#ifdef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef FEATURE_CONNECTION_SHARING /* * XXX: Should be relocated once it no * longer needs to emit log messages. */ initialize_reusable_connections(); -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ +#endif /* def FEATURE_CONNECTION_SHARING */ bfd = bind_port_helper(config); @@ -3209,7 +3448,7 @@ static void listen_loop(void) } csp->flags |= CSP_FLAG_ACTIVE; - csp->sfd = JB_INVALID_SOCKET; + csp->server_connection.sfd = JB_INVALID_SOCKET; csp->config = config = load_config();