X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=jcc.c;h=3330da6941e8fe4c372d0ee307f3cd52599082f9;hb=7539248e523a18d9e171d7355b0678ba1a87d81b;hp=408c649d1bc50c91ee5afb204c1d50dffa31779c;hpb=a1649d1c0992d9b1459d2b92cc02f7b723baf5e0;p=privoxy.git diff --git a/jcc.c b/jcc.c index 408c649d..3330da69 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,3 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.447 2016/09/27 22:48:28 ler762 Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -6,7 +5,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.447 2016/09/27 22:48:28 ler762 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 +92,20 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.447 2016/09/27 22:48:28 ler762 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 +#warning poll() appears to be unavailable. Your platform will become unsupported in the future. +#endif /* HAVE_POLL */ #endif @@ -120,9 +127,6 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.447 2016/09/27 22:48:28 ler762 Exp $"; #include "client-tags.h" #endif -const char jcc_h_rcs[] = JCC_H_VERSION; -const char project_h_rcs[] = PROJECT_H_VERSION; - int daemon_mode = 1; struct client_states clients[1]; struct file_list files[1]; @@ -155,7 +159,7 @@ static void serve(struct client_state *csp); static void usage(const char *myname); #endif static void initialize_mutexes(void); -static jb_socket bind_port_helper(const char *haddr, int hport); +static jb_socket bind_port_helper(const char *haddr, int hport, int backlog); static void bind_ports_helper(struct configuration_spec *config, jb_socket sockets[]); static void close_ports_helper(jb_socket sockets[]); static void listen_loop(void); @@ -178,6 +182,11 @@ static int32 server_thread(void *data); #define sleep(N) DosSleep(((N) * 100)) #endif +#ifdef FUZZ +int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file); +void show_fuzz_usage(const char *name); +#endif + #ifdef MUTEX_LOCKS_AVAILABLE /* * XXX: Does the locking stuff really belong in this file? @@ -205,9 +214,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 */ @@ -749,7 +758,7 @@ static void log_applied_actions(const struct current_action_spec *actions) * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) - * 1 : rsp = Fully prepared response. Will be freed on exit. + * 2 : rsp = Fully prepared response. Will be freed on exit. * * Returns : Nothing. * @@ -1056,7 +1065,7 @@ static void wait_for_alive_connections(void) * 1 : sfd = Open socket to remember. * 2 : http = The destination for the connection. * 3 : fwd = The forwarder settings used. - * 3 : server_connection = storage. + * 4 : server_connection = storage. * * Returns : void * @@ -1279,7 +1288,12 @@ static char *get_request_line(struct client_state *csp) do { - if (!data_is_available(csp->cfd, csp->config->socket_timeout)) + if ( +#ifdef FUZZ + 0 == (csp->flags & CSP_FLAG_FUZZED_INPUT) && +#endif + !data_is_available(csp->cfd, csp->config->socket_timeout) + ) { if (socket_is_still_alive(csp->cfd)) { @@ -1425,7 +1439,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; @@ -1464,6 +1478,80 @@ static jb_err receive_chunked_client_request_body(struct client_state *csp) } +#ifdef FUZZ +/********************************************************************* + * + * Function : fuzz_chunked_transfer_encoding + * + * Description : Treat the fuzzed input as chunked transfer encoding + * to check and dechunk. + * + * Parameters : + * 1 : csp = Used to store the data. + * 2 : fuzz_input_file = File to read the input from. + * + * Returns : Result of dechunking + * + *********************************************************************/ +extern int fuzz_chunked_transfer_encoding(struct client_state *csp, char *fuzz_input_file) +{ + size_t length; + size_t size = (size_t)(csp->iob->eod - csp->iob->cur); + enum chunk_status status; + + status = chunked_body_is_complete(csp->iob, &length); + if (CHUNK_STATUS_BODY_COMPLETE != status) + { + log_error(LOG_LEVEL_INFO, "Chunked body is incomplete or invalid"); + } + + return (JB_ERR_OK == remove_chunked_transfer_coding(csp->iob->cur, &size)); + +} + + +/********************************************************************* + * + * Function : fuzz_client_request + * + * Description : Try to get a client request from the fuzzed input. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : fuzz_input_file = File to read the input from. + * + * Returns : Result of fuzzing. + * + *********************************************************************/ +extern int fuzz_client_request(struct client_state *csp, char *fuzz_input_file) +{ + jb_err err; + + csp->cfd = 0; + csp->ip_addr_str = "fuzzer"; + + if (strcmp(fuzz_input_file, "-") != 0) + { + log_error(LOG_LEVEL_FATAL, + "Fuzzed client requests can currenty only be read from stdin (-)."); + } + err = receive_client_request(csp); + if (err != JB_ERR_OK) + { + return 1; + } + err = parse_client_request(csp); + if (err != JB_ERR_OK) + { + return 1; + } + + return 0; + +} +#endif /* def FUZZ */ + + #ifdef FEATURE_FORCE_LOAD /********************************************************************* * @@ -1761,6 +1849,10 @@ static jb_err parse_client_request(struct client_state *csp) } verify_request_length(csp); } + else + { + csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; + } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ err = sed(csp, FILTER_CLIENT_HEADERS); @@ -1804,6 +1896,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 @@ -1817,15 +1960,18 @@ static jb_err parse_client_request(struct client_state *csp) * Returns : Nothing. * *********************************************************************/ -static void handle_established_connection(struct client_state *csp, - const struct forward_spec *fwd) +static void handle_established_connection(struct client_state *csp) { - char buf[BUFFER_SIZE]; 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; @@ -1835,17 +1981,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 - memset(buf, 0, sizeof(buf)); + csp->receive_buffer_size = csp->config->receive_buffer_size; + csp->receive_buffer = zalloc(csp->receive_buffer_size + 1); + if (csp->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. @@ -1859,6 +2015,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. @@ -1880,6 +2037,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) @@ -1924,14 +2082,37 @@ 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) { - log_error(LOG_LEVEL_ERROR, - "Didn't receive data in time: %s", http->url); + log_error(LOG_LEVEL_CONNECT, "Socket timeout %d reached: %s", + csp->config->socket_timeout, http->url); if ((byte_count == 0) && (http->ssl == 0)) { send_crunch_response(csp, error_response(csp, "connection-timeout")); @@ -1941,7 +2122,11 @@ static void handle_established_connection(struct client_state *csp, } 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); return; } @@ -1953,9 +2138,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)csp->receive_buffer_size; #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) @@ -1970,7 +2169,7 @@ static void handle_established_connection(struct client_state *csp, */ watch_client_socket = 0; log_error(LOG_LEVEL_CONNECT, - "Stopping to watch the client socket %d. " + "Stop watching client socket %d. " "There's already another request waiting.", csp->cfd); continue; @@ -1989,7 +2188,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 < csp->receive_buffer_size) { max_bytes_to_read = (int)csp->expected_client_content_length; } @@ -1997,10 +2196,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 <= csp->receive_buffer_size); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->cfd, buf, max_bytes_to_read); + len = read_socket(csp->cfd, csp->receive_buffer, max_bytes_to_read); if (len <= 0) { @@ -2027,7 +2226,7 @@ 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, csp->receive_buffer, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); mark_server_socket_tainted(csp); @@ -2041,7 +2240,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 /* @@ -2065,13 +2268,13 @@ static void handle_established_connection(struct client_state *csp, } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->server_connection.sfd, buf, sizeof(buf) - 1); + len = read_socket(csp->server_connection.sfd, csp->receive_buffer, (int)csp->receive_buffer_size); if (len < 0) { log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host); - if (http->ssl && (fwd->forward_host == NULL)) + if (http->ssl && (csp->fwd == NULL)) { /* * Just hang up. We already confirmed the client's CONNECT @@ -2106,7 +2309,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(csp->receive_buffer+len-5, "0\r\n\r\n", 5)) { /* XXX: this is a temporary hack */ log_error(LOG_LEVEL_CONNECT, @@ -2119,11 +2322,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. + * + * csp->receive_buffer_size is the size of the part of the + * buffer we intentionally write to, but we actually + * allocated csp->receive_buffer_size+1 bytes so the assertion + * stays within the allocated range. + */ + assert(csp->receive_buffer[csp->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 <= csp->receive_buffer_size); + csp->receive_buffer[len] = '\0'; /* * Normally, this would indicate that we've read @@ -2216,8 +2431,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(csp->receive_buffer, csp->receive_buffer_size, "\r\n"); + len = (int)strlen(csp->receive_buffer); /* * Now, let the normal header parsing algorithm below do its @@ -2241,7 +2456,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, csp->receive_buffer, len)) { size_t hdrlen; long flushed; @@ -2266,7 +2481,7 @@ static void handle_established_connection(struct client_state *csp, 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, csp->receive_buffer, (size_t)len))) { log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E"); @@ -2288,7 +2503,7 @@ static void handle_established_connection(struct client_state *csp, } else { - if (write_socket(csp->cfd, buf, (size_t)len)) + if (write_socket(csp->cfd, csp->receive_buffer, (size_t)len)) { log_error(LOG_LEVEL_ERROR, "write to client failed: %E"); mark_server_socket_tainted(csp); @@ -2305,7 +2520,7 @@ 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, csp->receive_buffer, len)) { log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers."); rsp = cgi_error_memory(); @@ -2448,9 +2663,9 @@ static void handle_established_connection(struct client_state *csp, * delivered the crunch response to the client * and are done here after cleaning up. */ - freez(hdr); - mark_server_socket_tainted(csp); - return; + freez(hdr); + mark_server_socket_tainted(csp); + return; } /* Buffer and pcrs filter this if appropriate. */ @@ -2539,6 +2754,7 @@ static void handle_established_connection(struct client_state *csp, csp->server_connection.timestamp = time(NULL); } + /********************************************************************* * * Function : chat @@ -2565,15 +2781,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) @@ -2765,36 +2977,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) @@ -2824,10 +3007,73 @@ static void chat(struct client_state *csp) /* XXX: should the time start earlier for optimistically sent data? */ csp->server_connection.request_sent = time(NULL); - handle_established_connection(csp, fwd); + handle_established_connection(csp); + freez(csp->receive_buffer); } +#ifdef FUZZ +/********************************************************************* + * + * Function : fuzz_server_response + * + * Description : Treat the input as a whole server response. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : fuzz_input_file = File to read the input from. + * + * Returns : 0 + * + *********************************************************************/ +extern int fuzz_server_response(struct client_state *csp, char *fuzz_input_file) +{ + static struct forward_spec fwd; /* Zero'd due to being static */ + csp->cfd = 0; + + if (strcmp(fuzz_input_file, "-") == 0) + { + /* XXX: Doesn'T work yet. */ + csp->server_connection.sfd = 0; + } + else + { + csp->server_connection.sfd = open(fuzz_input_file, O_RDONLY); + if (csp->server_connection.sfd == -1) + { + log_error(LOG_LEVEL_FATAL, "Failed to open %s: %E", + fuzz_input_file); + } + } + csp->fwd = &fwd; + csp->content_type |= CT_GIF; + csp->action->flags |= ACTION_DEANIMATE; + csp->action->string[ACTION_STRING_DEANIMATE] = "last"; + + csp->http->path = strdup_or_die("/"); + csp->http->host = strdup_or_die("fuzz.example.org"); + csp->http->hostport = strdup_or_die("fuzz.example.org:80"); + /* Prevent client socket monitoring */ + csp->flags |= CSP_FLAG_PIPELINED_REQUEST_WAITING; + csp->flags |= CSP_FLAG_CHUNKED; + + csp->config->feature_flags |= RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE; + csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + + csp->content_type |= CT_DECLARED|CT_GIF; + + csp->config->socket_timeout = 0; + + cgi_init_error_messages(); + + handle_established_connection(csp); + freez(csp->receive_buffer); + + return 0; +} +#endif + + #ifdef FEATURE_CONNECTION_KEEP_ALIVE /********************************************************************* * @@ -3119,6 +3365,8 @@ static void serve(struct client_state *csp) drain_and_close_socket(csp->cfd); } + free_csp_resources(csp); + csp->flags &= ~CSP_FLAG_ACTIVE; } @@ -3158,7 +3406,7 @@ static int32 server_thread(void *data) * Returns : No. ,-) * *********************************************************************/ -static void usage(const char *myname) +static void usage(const char *name) { printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n" "Usage: %s [--config-test] " @@ -3169,8 +3417,14 @@ static void usage(const char *myname) #if defined(unix) "[--no-daemon] [--pidfile pidfile] [--pre-chroot-nslookup hostname] [--user user[.group]] " #endif /* defined(unix) */ - "[--version] [configfile]\n" - "Aborting\n", myname); + "[--version] [configfile]\n", + name); + +#ifdef FUZZ + show_fuzz_usage(name); +#endif + + printf("Aborting\n"); exit(2); @@ -3319,14 +3573,13 @@ 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 */ } - /********************************************************************* * * Function : main @@ -3358,13 +3611,19 @@ 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; int do_chroot = 0; char *pre_chroot_nslookup_to_load_resolver = NULL; #endif +#ifdef FUZZ + char *fuzz_input_type = NULL; + char *fuzz_input_file = NULL; +#endif Argc = argc; Argv = argv; @@ -3492,7 +3751,20 @@ int main(int argc, char **argv) { do_config_test = 1; } - +#ifdef FUZZ + else if (strcmp(argv[argc_pos], "--fuzz") == 0) + { + argc_pos++; + if (argc < argc_pos + 2) usage(argv[0]); + fuzz_input_type = argv[argc_pos]; + argc_pos++; + fuzz_input_file = argv[argc_pos]; + } + else if (strcmp(argv[argc_pos], "--stfu") == 0) + { + set_debug_level(LOG_LEVEL_STFU); + } +#endif else if (argc_pos + 1 != argc) { /* @@ -3549,19 +3821,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) { @@ -3596,6 +3873,16 @@ int main(int argc, char **argv) # endif /* def _WIN_CONSOLE */ #endif /* def _WIN32 */ +#ifdef FUZZ + if (fuzz_input_type != NULL) + { + exit(process_fuzzed_input(fuzz_input_type, fuzz_input_file)); + } + log_error(LOG_LEVEL_FATAL, + "When compiled with fuzzing support, Privoxy should only be used for fuzzing. " + "Various data structures are static which is unsafe when using threads."); +#endif + if (do_config_test) { exit(NULL == load_config()); @@ -3694,8 +3981,10 @@ int main(int argc, char **argv) * As soon as we have written the PID file, we can switch * to the user and group ID indicated by the --user option */ - write_pid_file(); - + if (pidfile != NULL) + { + write_pid_file(pidfile); + } if (NULL != pw) { if (setgid((NULL != grp) ? grp->gr_gid : pw->pw_gid)) @@ -3814,16 +4103,17 @@ int main(int argc, char **argv) * 1 : haddr = Host address to bind to. Use NULL to bind to * INADDR_ANY. * 2 : hport = Specifies port to bind to. + * 3 : backlog = Listen backlog. * * Returns : Port that was opened. * *********************************************************************/ -static jb_socket bind_port_helper(const char *haddr, int hport) +static jb_socket bind_port_helper(const char *haddr, int hport, int backlog) { int result; jb_socket bfd; - result = bind_port(haddr, hport, &bfd); + result = bind_port(haddr, hport, backlog, &bfd); if (result < 0) { @@ -3850,6 +4140,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) { @@ -3857,6 +4148,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) @@ -3901,7 +4193,22 @@ static void bind_ports_helper(struct configuration_spec * config, { if (config->hport[i]) { - sockets[i] = bind_port_helper(config->haddr[i], config->hport[i]); + sockets[i] = bind_port_helper(config->haddr[i], + config->hport[i], config->listen_backlog); +#if defined(FEATURE_ACCEPT_FILTER) && defined(SO_ACCEPTFILTER) + if (config->enable_accept_filter && sockets[i] != JB_INVALID_SOCKET) + { + struct accept_filter_arg af_options; + bzero(&af_options, sizeof(af_options)); + strlcpy(af_options.af_name, "httpready", sizeof(af_options.af_name)); + if (setsockopt(sockets[i], SOL_SOCKET, SO_ACCEPTFILTER, &af_options, + sizeof(af_options))) + { + log_error(LOG_LEVEL_ERROR, + "Enabling accept filter for socket %d failed: %E", sockets[i]); + } + } +#endif } else { @@ -3970,6 +4277,12 @@ static void listen_loop(void) jb_socket bfds[MAX_LISTENING_SOCKETS]; struct configuration_spec *config; unsigned int active_threads = 0; +#if defined(FEATURE_PTHREAD) + pthread_attr_t attrs; + + pthread_attr_init(&attrs); + pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED); +#endif config = load_config(); @@ -4057,7 +4370,7 @@ static void listen_loop(void) * new one. * * Which-ever is correct, we will serve 1 more page via the - * old settings. This should probably be a "show-proxy-args" + * old settings. This should probably be a "show-status" * request. This should not be a so common of an operation * that this will hurt people's feelings. */ @@ -4125,14 +4438,10 @@ static void listen_loop(void) #define SELECTED_ONE_OPTION { pthread_t the_thread; - pthread_attr_t attrs; - pthread_attr_init(&attrs); - pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED); errno = pthread_create(&the_thread, &attrs, (void * (*)(void *))serve, csp); child_id = errno ? -1 : 0; - pthread_attr_destroy(&attrs); } #endif @@ -4297,6 +4606,10 @@ static void listen_loop(void) } } +#if defined(FEATURE_PTHREAD) + pthread_attr_destroy(&attrs); +#endif + /* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */ /* Clean up. Aim: free all memory (no leaks) */