X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=dd8b7657457ba53b8b22e05bea782dabf61cf4b6;hp=e4ecd01fbd6b76384f3b6b09d54a877b8138c00e;hb=af31785cde42288c236ca4d0ba850d02e466c907;hpb=f67b3326138f428863c21c7738e0c8db87fa6f5c diff --git a/jcc.c b/jcc.c index e4ecd01f..dd8b7657 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.448 2016/12/24 15:58:49 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.457 2017/05/25 11:17:21 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -6,7 +6,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.448 2016/12/24 15:58:49 fabiankeil Exp $" * Purpose : Main file. Contains main() method, main loop, and * the main connection-handling function. * - * Copyright : Written by and Copyright (C) 2001-2016 the + * Copyright : Written by and Copyright (C) 2001-2017 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -93,12 +93,19 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.448 2016/12/24 15:58:49 fabiankeil Exp $" # ifdef __OS2__ #define INCL_DOS # include -#define bzero(B,N) memset(B,0x00,n) # endif +#ifdef HAVE_POLL +#ifdef __GLIBC__ +#include +#else +#include +#endif /* def __GLIBC__ */ +#else # ifndef FD_ZERO # include # endif +#endif /* HAVE_POLL */ #endif @@ -210,9 +217,9 @@ privoxy_mutex_t gmtime_mutex; privoxy_mutex_t localtime_mutex; #endif /* ndef HAVE_GMTIME_R */ -#ifndef HAVE_RANDOM +#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_RANDOM) privoxy_mutex_t rand_mutex; -#endif /* ndef HAVE_RANDOM */ +#endif /* !defined(HAVE_ARC4RANDOM) && !defined(HAVE_RANDOM) */ #endif /* def MUTEX_LOCKS_AVAILABLE */ @@ -1435,7 +1442,7 @@ static jb_err receive_chunked_client_request_body(struct client_state *csp) enum chunk_status status; while (CHUNK_STATUS_MISSING_DATA == - (status = chunked_body_is_complete(csp->client_iob,&body_length))) + (status = chunked_body_is_complete(csp->client_iob, &body_length))) { char buf[BUFFER_SIZE]; int len; @@ -1888,6 +1895,57 @@ static jb_err parse_client_request(struct client_state *csp) } +/********************************************************************* + * + * Function : send_http_request + * + * Description : Sends the HTTP headers from the client request + * and all the body data that has already been received. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 on success, anything else is na error. + * + *********************************************************************/ +static int send_http_request(struct client_state *csp) +{ + char *hdr; + int write_failure; + + hdr = list_to_text(csp->headers); + if (hdr == NULL) + { + /* FIXME Should handle error properly */ + log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header"); + } + list_remove_all(csp->headers); + + /* + * Write the client's (modified) header to the server + * (along with anything else that may be in the buffer) + */ + write_failure = 0 != write_socket(csp->server_connection.sfd, hdr, strlen(hdr)); + freez(hdr); + + if (write_failure) + { + log_error(LOG_LEVEL_CONNECT, "Failed sending request headers to: %s: %E", + csp->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", + csp->http->hostport); + } + + return write_failure; + +} + + /********************************************************************* * * Function : handle_established_connection @@ -1904,12 +1962,17 @@ static jb_err parse_client_request(struct client_state *csp) static void handle_established_connection(struct client_state *csp, const struct forward_spec *fwd) { - char buf[BUFFER_SIZE]; + char *receive_buffer; char *hdr; char *p; - fd_set rfds; int n; +#ifdef HAVE_POLL + struct pollfd poll_fds[2]; +#else + fd_set rfds; jb_socket maxfd; + struct timeval timeout; +#endif int server_body; int ms_iis5_hack = 0; unsigned long long byte_count = 0; @@ -1919,17 +1982,27 @@ static void handle_established_connection(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; #endif + const size_t receive_buffer_size = csp->config->receive_buffer_size; - memset(buf, 0, sizeof(buf)); + receive_buffer = zalloc(receive_buffer_size + 1); + if (receive_buffer == NULL) + { + log_error(LOG_LEVEL_ERROR, + "Out of memory. Failed to allocate the receive buffer."); + rsp = cgi_error_memory(); + send_crunch_response(csp, rsp); + return; + } http = csp->http; +#ifndef HAVE_POLL maxfd = (csp->cfd > csp->server_connection.sfd) ? csp->cfd : csp->server_connection.sfd; +#endif /* pass data between the client and server * until one or the other shuts down the connection. @@ -1943,6 +2016,7 @@ static void handle_established_connection(struct client_state *csp, for (;;) { +#ifndef HAVE_POLL #ifdef __OS2__ /* * FD_ZERO here seems to point to an errant macro which crashes. @@ -1964,6 +2038,7 @@ static void handle_established_connection(struct client_state *csp, } FD_SET(csp->server_connection.sfd, &rfds); +#endif /* ndef HAVE_POLL */ #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CHUNKED) @@ -2008,9 +2083,32 @@ static void handle_established_connection(struct client_state *csp, } #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ +#ifdef HAVE_POLL + poll_fds[0].fd = csp->cfd; +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (!watch_client_socket) + { + /* + * Ignore incoming data, but still watch out + * for disconnects etc. These flags are always + * implied anyway but explicitly setting them + * doesn't hurt. + */ + poll_fds[0].events = POLLERR|POLLHUP; + } + else +#endif + { + poll_fds[0].events = POLLIN; + } + poll_fds[1].fd = csp->server_connection.sfd; + poll_fds[1].events = POLLIN; + n = poll(poll_fds, 2, csp->config->socket_timeout * 1000); +#else timeout.tv_sec = csp->config->socket_timeout; timeout.tv_usec = 0; n = select((int)maxfd+1, &rfds, NULL, NULL, &timeout); +#endif /* def HAVE_POLL */ if (n == 0) { @@ -2021,12 +2119,18 @@ static void handle_established_connection(struct client_state *csp, send_crunch_response(csp, error_response(csp, "connection-timeout")); } mark_server_socket_tainted(csp); + freez(receive_buffer); return; } else if (n < 0) { +#ifdef HAVE_POLL + log_error(LOG_LEVEL_ERROR, "poll() failed!: %E"); +#else log_error(LOG_LEVEL_ERROR, "select() failed!: %E"); +#endif mark_server_socket_tainted(csp); + freez(receive_buffer); return; } @@ -2037,9 +2141,23 @@ static void handle_established_connection(struct client_state *csp, * XXX: Make sure the client doesn't use pipelining * behind Privoxy's back. */ +#ifdef HAVE_POLL + if ((poll_fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0) + { + log_error(LOG_LEVEL_CONNECT, + "The client socket %d has become unusable while " + "the server socket %d is still open.", + csp->cfd, csp->server_connection.sfd); + mark_server_socket_tainted(csp); + break; + } + + if (poll_fds[0].revents != 0) +#else if (FD_ISSET(csp->cfd, &rfds)) +#endif /* def HAVE_POLL*/ { - int max_bytes_to_read = sizeof(buf) - 1; + int max_bytes_to_read = (int)receive_buffer_size; #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) @@ -2073,7 +2191,7 @@ static void handle_established_connection(struct client_state *csp, } if (csp->expected_client_content_length != 0) { - if (csp->expected_client_content_length < (sizeof(buf) - 1)) + if (csp->expected_client_content_length < receive_buffer_size) { max_bytes_to_read = (int)csp->expected_client_content_length; } @@ -2081,10 +2199,10 @@ static void handle_established_connection(struct client_state *csp, "Waiting for up to %d bytes from the client.", max_bytes_to_read); } - assert(max_bytes_to_read < sizeof(buf)); + assert(max_bytes_to_read <= receive_buffer_size); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->cfd, buf, max_bytes_to_read); + len = read_socket(csp->cfd, receive_buffer, max_bytes_to_read); if (len <= 0) { @@ -2111,10 +2229,11 @@ static void handle_established_connection(struct client_state *csp, } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - if (write_socket(csp->server_connection.sfd, buf, (size_t)len)) + if (write_socket(csp->server_connection.sfd, receive_buffer, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } continue; @@ -2125,7 +2244,11 @@ static void handle_established_connection(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. */ +#ifdef HAVE_POLL + if (poll_fds[1].revents != 0) +#else if (FD_ISSET(csp->server_connection.sfd, &rfds)) +#endif /* HAVE_POLL */ { #ifdef FEATURE_CONNECTION_KEEP_ALIVE /* @@ -2144,12 +2267,13 @@ static void handle_established_connection(struct client_state *csp, log_error(LOG_LEVEL_CONNECT, "The server still wants to talk, but the client hung up on us."); mark_server_socket_tainted(csp); + freez(receive_buffer); return; #endif /* def _WIN32 */ } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->server_connection.sfd, buf, sizeof(buf) - 1); + len = read_socket(csp->server_connection.sfd, receive_buffer, (int)receive_buffer_size); if (len < 0) { @@ -2164,6 +2288,7 @@ static void handle_established_connection(struct client_state *csp, */ log_error(LOG_LEVEL_ERROR, "CONNECT already confirmed. Unable to tell the client about the problem."); + freez(receive_buffer); return; } else if (byte_count) @@ -2178,6 +2303,7 @@ static void handle_established_connection(struct client_state *csp, log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. " "Unable to tell the client about the problem."); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } /* @@ -2190,7 +2316,7 @@ static void handle_established_connection(struct client_state *csp, #ifdef FEATURE_CONNECTION_KEEP_ALIVE if (csp->flags & CSP_FLAG_CHUNKED) { - if ((len >= 5) && !memcmp(buf+len-5, "0\r\n\r\n", 5)) + if ((len >= 5) && !memcmp(receive_buffer+len-5, "0\r\n\r\n", 5)) { /* XXX: this is a temporary hack */ log_error(LOG_LEVEL_CONNECT, @@ -2203,11 +2329,23 @@ static void handle_established_connection(struct client_state *csp, reading_done: #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ + /* + * This is guaranteed by allocating with zalloc_or_die() + * and never (intentionally) writing to the last byte. + * + * receive_buffer_size is the size of the part of the + * buffer we intentionally write to, but we actually + * allocated receive_buffer_size+1 bytes so the assertion + * stays within the allocated range. + */ + assert(receive_buffer[receive_buffer_size] == '\0'); + /* * Add a trailing zero to let be able to use string operations. * XXX: do we still need this with filter_popups gone? */ - buf[len] = '\0'; + assert(len <= receive_buffer_size); + receive_buffer[len] = '\0'; /* * Normally, this would indicate that we've read @@ -2286,6 +2424,7 @@ static void handle_established_connection(struct client_state *csp, freez(hdr); freez(p); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } @@ -2300,8 +2439,8 @@ static void handle_established_connection(struct client_state *csp, * This is NOT the body, so * Let's pretend the server just sent us a blank line. */ - snprintf(buf, sizeof(buf), "\r\n"); - len = (int)strlen(buf); + snprintf(receive_buffer, receive_buffer_size, "\r\n"); + len = (int)strlen(receive_buffer); /* * Now, let the normal header parsing algorithm below do its @@ -2325,7 +2464,7 @@ static void handle_established_connection(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->iob, csp->config->buffer_limit, buf, len)) + if (add_to_iob(csp->iob, csp->config->buffer_limit, receive_buffer, len)) { size_t hdrlen; long flushed; @@ -2344,18 +2483,20 @@ static void handle_established_connection(struct client_state *csp, rsp = cgi_error_memory(); send_crunch_response(csp, rsp); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } hdrlen = strlen(hdr); if (write_socket(csp->cfd, hdr, hdrlen) || ((flushed = flush_socket(csp->cfd, csp->iob)) < 0) - || (write_socket(csp->cfd, buf, (size_t)len))) + || (write_socket(csp->cfd, receive_buffer, (size_t)len))) { log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E"); freez(hdr); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } @@ -2372,10 +2513,11 @@ static void handle_established_connection(struct client_state *csp, } else { - if (write_socket(csp->cfd, buf, (size_t)len)) + if (write_socket(csp->cfd, receive_buffer, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to client failed: %E"); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } } @@ -2389,12 +2531,13 @@ static void handle_established_connection(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->iob, csp->config->buffer_limit, buf, len)) + if (add_to_iob(csp->iob, csp->config->buffer_limit, receive_buffer, len)) { log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers."); rsp = cgi_error_memory(); send_crunch_response(csp, rsp); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } @@ -2415,6 +2558,7 @@ static void handle_established_connection(struct client_state *csp, write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, strlen(INVALID_SERVER_HEADERS_RESPONSE)); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } else @@ -2460,6 +2604,7 @@ static void handle_established_connection(struct client_state *csp, } free_http_request(http); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } @@ -2500,6 +2645,7 @@ static void handle_established_connection(struct client_state *csp, strlen(INVALID_SERVER_HEADERS_RESPONSE)); free_http_request(http); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } hdr = list_to_text(csp->headers); @@ -2534,6 +2680,7 @@ static void handle_established_connection(struct client_state *csp, */ freez(hdr); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } /* Buffer and pcrs filter this if appropriate. */ @@ -2564,6 +2711,7 @@ static void handle_established_connection(struct client_state *csp, */ freez(hdr); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } } @@ -2588,14 +2736,17 @@ static void handle_established_connection(struct client_state *csp, write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, strlen(INVALID_SERVER_HEADERS_RESPONSE)); mark_server_socket_tainted(csp); + freez(receive_buffer); return; } } continue; } mark_server_socket_tainted(csp); + freez(receive_buffer); return; /* huh? we should never get here */ } + freez(receive_buffer); if (csp->content_length == 0) { @@ -2650,15 +2801,11 @@ static void handle_established_connection(struct client_state *csp, *********************************************************************/ static void chat(struct client_state *csp) { - char buf[BUFFER_SIZE]; - char *hdr; const struct forward_spec *fwd; struct http_request *http; /* Skeleton for HTTP response, if we should intercept the request */ struct http_response *rsp; - memset(buf, 0, sizeof(buf)); - http = csp->http; if (receive_client_request(csp) != JB_ERR_OK) @@ -2850,36 +2997,7 @@ static void chat(struct client_state *csp) } else if (fwd->forward_host || (http->ssl == 0)) { - int write_failure; - hdr = list_to_text(csp->headers); - if (hdr == NULL) - { - /* FIXME Should handle error properly */ - log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header"); - } - list_remove_all(csp->headers); - - /* - * Write the client's (modified) header to the server - * (along with anything else that may be in the buffer) - */ - write_failure = 0 != write_socket(csp->server_connection.sfd, hdr, strlen(hdr)); - freez(hdr); - - if (write_failure) - { - log_error(LOG_LEVEL_CONNECT, - "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) + if (send_http_request(csp)) { rsp = error_response(csp, "connect-failed"); if (rsp) @@ -3470,9 +3588,9 @@ static void initialize_mutexes(void) privoxy_mutex_init(&localtime_mutex); #endif /* ndef HAVE_GMTIME_R */ -#ifndef HAVE_RANDOM +#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_RANDOM) privoxy_mutex_init(&rand_mutex); -#endif /* ndef HAVE_RANDOM */ +#endif /* !defined(HAVE_ARC4RANDOM) && !defined(HAVE_RANDOM) */ #endif /* def MUTEX_LOCKS_AVAILABLE */ } @@ -3508,7 +3626,9 @@ int main(int argc, char **argv) { int argc_pos = 0; int do_config_test = 0; +#ifndef HAVE_ARC4RANDOM unsigned int random_seed; +#endif #ifdef unix struct passwd *pw = NULL; struct group *grp = NULL; @@ -3716,19 +3836,24 @@ int main(int argc, char **argv) InitWin32(); #endif +#ifndef HAVE_ARC4RANDOM random_seed = (unsigned int)time(NULL); #ifdef HAVE_RANDOM srandom(random_seed); #else srand(random_seed); #endif /* ifdef HAVE_RANDOM */ +#endif /* ifndef HAVE_ARC4RANDOM */ /* * Unix signal handling * * Catch the abort, interrupt and terminate signals for a graceful exit * Catch the hangup signal so the errlog can be reopened. - * Ignore the broken pipe signals (FIXME: Why?) + * + * Ignore the broken pipe signal as connection failures + * are handled when and where they occur without relying + * on a signal. */ #if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) { @@ -4024,6 +4149,7 @@ static jb_socket bind_port_helper(const char *haddr, int hport) return JB_INVALID_SOCKET; } +#ifndef HAVE_POLL #ifndef _WIN32 if (bfd >= FD_SETSIZE) { @@ -4031,6 +4157,7 @@ static jb_socket bind_port_helper(const char *haddr, int hport) "Bind socket number too high to use select(): %d >= %d", bfd, FD_SETSIZE); } +#endif #endif if (haddr == NULL)