X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=22e59ec090c06d2067d409bcaaa463551d3bb481;hp=7a23ea66c7853d1257eec0969ec8e9e605842b93;hb=05dd2cc7b727e9600f3808d9156d94d1a3396155;hpb=a9020a996f608a81d1f894e6c854c1e6edd485d7 diff --git a/jcc.c b/jcc.c index 7a23ea66..22e59ec0 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.295 2009/09/26 13:32:35 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.315 2010/04/12 16:51:31 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -6,7 +6,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.295 2009/09/26 13:32:35 fabiankeil Exp $" * Purpose : Main file. Contains main() method, main loop, and * the main connection-handling function. * - * Copyright : Written by and Copyright (C) 2001-2009 the SourceForge + * Copyright : Written by and Copyright (C) 2001-2010 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -119,7 +119,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.295 2009/09/26 13:32:35 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]; @@ -711,7 +711,7 @@ static void send_crunch_response(const struct client_state *csp, struct http_res /* * Extract the status code from the actual head - * that was send to the client. It is the only + * that will be send to the client. It is the only * way to get it right for all requests, including * the fixed ones for out-of-memory problems. * @@ -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) { @@ -838,6 +840,7 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun { /* Deliver, log and free the interception response. */ send_crunch_response(csp, rsp); + csp->flags |= CSP_FLAG_CRUNCHED; return TRUE; } @@ -857,6 +860,7 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun { /* Deliver, log and free the interception response. */ send_crunch_response(csp, rsp); + csp->flags |= CSP_FLAG_CRUNCHED; #ifdef FEATURE_STATISTICS if (c->flags & CF_COUNT_AS_REJECT) { @@ -1235,7 +1239,8 @@ static void mark_server_socket_tainted(struct client_state *csp) && !(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; } } @@ -1264,8 +1269,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; @@ -1563,11 +1569,16 @@ static jb_err parse_client_request(struct client_state *csp) * * Function : chat * - * Description : Once a connection to the client has been accepted, + * Description : Once a connection from the client has been accepted, * this function is called (via serve()) to handle the - * main business of the communication. When this - * function returns, the caller must close the client - * socket handle. + * main business of the communication. This function + * returns after dealing with a single request. It can + * be called multiple times witht the same client socket + * if the client is keeping the connection alive. + * + * The decision whether or not a client connection will + * be kept alive is up to the caller which also must + * close the client socket when done. * * FIXME: chat is nearly thousand lines long. * Ridiculous. @@ -1601,7 +1612,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)); @@ -1710,27 +1723,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) - && socket_is_still_usable(csp->sfd) + if ((csp->server_connection.sfd != JB_INVALID_SOCKET) + && socket_is_still_alive(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)) { @@ -1739,7 +1752,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) { @@ -1766,8 +1779,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 */ @@ -1785,8 +1800,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); @@ -1823,7 +1838,8 @@ static void chat(struct client_state *csp) /* 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. @@ -1845,7 +1861,7 @@ static void chat(struct client_state *csp) #ifdef FEATURE_CONNECTION_KEEP_ALIVE if (!watch_client_socket) { - maxfd = csp->sfd; + maxfd = csp->server_connection.sfd; } else #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1853,7 +1869,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) @@ -1948,8 +1964,10 @@ static void chat(struct client_state *csp) * 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->sfd); + 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; } @@ -1993,7 +2011,7 @@ static void chat(struct client_state *csp) } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - if (write_socket(csp->sfd, buf, (size_t)len)) + 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); @@ -2007,10 +2025,10 @@ 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)) + if (!socket_is_still_alive(csp->cfd)) { #ifdef _WIN32 log_error(LOG_LEVEL_CONNECT, @@ -2025,7 +2043,7 @@ static void chat(struct client_state *csp) #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) { @@ -2304,10 +2322,21 @@ static void chat(struct client_state *csp) /* 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 socket %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; @@ -2487,42 +2516,63 @@ static void serve(struct client_state *csp) chat(csp); + /* + * If the request has been crunched, + * the calculated latency is zero. + */ 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) - && (latency < csp->server_connection.keep_alive_timeout); + && (((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE) + && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)) + || (csp->flags & CSP_FLAG_CRUNCHED)) + && (csp->cfd != JB_INVALID_SOCKET); + + if (continue_chatting && !(csp->flags & CSP_FLAG_CRUNCHED)) + { + 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; + 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; - if (!(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET)) + if (csp->server_connection.sfd != JB_INVALID_SOCKET) { - log_error(LOG_LEVEL_CONNECT, "The server didn't specify how long " - "the connection will stay open. Assume it's only a second."); - csp->server_connection.keep_alive_timeout = 1; + 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.", + csp->cfd, csp->server_connection.sfd, csp->server_connection.host); + } + else + { + client_timeout = 1; /* XXX: Use something else here? */ + log_error(LOG_LEVEL_CONNECT, + "Waiting for the next client request on socket %d. " + "No server socket to keep open.", csp->cfd); } - - client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency; - - log_error(LOG_LEVEL_CONNECT, - "Waiting for the next client request. " - "Keeping the server socket %d to %s open.", - csp->sfd, csp->server_connection.host); - if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) && data_is_available(csp->cfd, (int)client_timeout) - && socket_is_still_usable(csp->cfd)) + && socket_is_still_alive(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. @@ -2546,15 +2596,17 @@ 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."); + "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->sfd))) + && (socket_is_still_alive(csp->server_connection.sfd))) { time_t time_open = time(NULL) - csp->server_connection.timestamp; @@ -2563,8 +2615,8 @@ static void serve(struct client_state *csp) break; } - remember_connection(csp, forward_url(csp, csp->http)); - csp->sfd = JB_INVALID_SOCKET; + 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); @@ -2582,27 +2634,33 @@ static void serve(struct client_state *csp) 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_SHARING - forget_connection(csp->sfd); + if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING) + { + forget_connection(csp->server_connection.sfd); + } #endif /* def FEATURE_CONNECTION_SHARING */ - close_socket(csp->sfd); + 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); @@ -2917,7 +2975,7 @@ int main(int argc, 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) @@ -3084,12 +3142,11 @@ int main(int argc, 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 */ @@ -3114,33 +3171,50 @@ int main(int argc, 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 @@ -3398,7 +3472,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();