X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=06ce04ae5524522cb898560d105cbf063ad42be0;hp=53d325e255ee7e7a6316361784bf99055bc8b7af;hb=0037521e356511ffd45f769e732e9bd4c120da0f;hpb=e6b3ab33f6caca1a1f3554c4118978d6884b1812 diff --git a/jcc.c b/jcc.c index 53d325e2..06ce04ae 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.141 2007/08/04 09:56:23 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.177 2008/05/10 11:51:12 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -6,7 +6,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.141 2007/08/04 09:56:23 fabiankeil Exp $" * Purpose : Main file. Contains main() method, main loop, and * the main connection-handling function. * - * Copyright : Written by and Copyright (C) 2001-2007 the SourceForge + * Copyright : Written by and Copyright (C) 2001-2008 the SourceForge * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -33,6 +33,147 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.141 2007/08/04 09:56:23 fabiankeil Exp $" * * Revisions : * $Log: jcc.c,v $ + * Revision 1.177 2008/05/10 11:51:12 fabiankeil + * Make the "read the rest of the headers" loop a bit more readable. + * + * Revision 1.176 2008/05/10 11:37:57 fabiankeil + * - Instead of logging when the IIS5 hack is enabled, log when it fails. + * - Remove useless comment. + * + * Revision 1.175 2008/05/09 18:53:59 fabiankeil + * Fix comment grammar. + * + * Revision 1.174 2008/05/07 18:05:53 fabiankeil + * Remove the pointless buffer in client_protocol_is_unsupported(). + * + * Revision 1.173 2008/05/06 15:09:00 fabiankeil + * Least-effort fix for bug #1821930 (reported by Lee): + * If the response doesn't look like HTTP, + * tell the client and log the problem. + * + * Revision 1.172 2008/04/16 16:38:21 fabiankeil + * Don't pass the whole csp structure to flush_socket() + * when it only needs a file descriptor and a buffer. + * + * Revision 1.171 2008/03/27 18:27:25 fabiankeil + * Remove kill-popups action. + * + * Revision 1.170 2008/03/06 16:33:46 fabiankeil + * If limit-connect isn't used, don't limit CONNECT requests to port 443. + * + * Revision 1.169 2008/03/04 18:30:39 fabiankeil + * Remove the treat-forbidden-connects-like-blocks action. We now + * use the "blocked" page for forbidden CONNECT requests by default. + * + * Revision 1.168 2008/03/02 12:25:25 fabiankeil + * Also use shiny new connect_port_is_forbidden() in jcc.c. + * + * Revision 1.167 2008/02/23 16:57:12 fabiankeil + * Rename url_actions() to get_url_actions() and let it + * use the standard parameter ordering. + * + * Revision 1.166 2008/02/23 16:33:43 fabiankeil + * Let forward_url() use the standard parameter ordering + * and mark its second parameter immutable. + * + * Revision 1.165 2008/02/02 19:36:56 fabiankeil + * Remove the "Listening ... for local connections only" log message. + * Whether or not remote connections are able to reach Privoxy is up + * to the operating system. + * + * Revision 1.164 2007/12/16 18:32:46 fabiankeil + * Prevent the log messages for CONNECT requests to unacceptable + * ports from printing the limit-connect argument as [null] if + * limit-connect hasn't been explicitly enabled. + * + * Revision 1.163 2007/12/13 01:47:11 david__schmidt + * Make sure all console-mode apps get a usage() instance + * + * Revision 1.162 2007/12/06 17:54:57 fabiankeil + * Reword NO_SERVER_DATA_RESPONSE to make it harder + * to misunderstand what the message is all about. + * + * Revision 1.161 2007/12/04 19:44:22 fabiankeil + * Unbreak trustfile which previously didn't work without + * FEATURE_TOGGLE. Fixes BR#1843585, reported by Lee. + * + * Revision 1.160 2007/11/29 18:00:29 fabiankeil + * Plug memory leak. Spotted by Valgrind, triggered by + * Privoxy-Regression-Test feeding proxyfuzz.py. + * + * Revision 1.159 2007/11/24 14:34:09 fabiankeil + * In the HTTP snipplets, refer to the client as client. + * + * Revision 1.158 2007/11/11 16:44:17 fabiankeil + * Emit a log message when activating the MS IIS5 hack. + * + * Revision 1.157 2007/11/03 17:34:49 fabiankeil + * Log the "weak randomization factor" warning only + * once for mingw32 and provide some more details. + * + * Revision 1.156 2007/11/01 18:20:58 fabiankeil + * Initialize log module after initializing mutexes, future + * deadlocks in that code should now work cross-platform. + * + * Revision 1.155 2007/10/23 20:12:45 fabiankeil + * Fix first CSUCCEED line to end in \r\n as required by RFC1945. + * Reported by Bert van Leeuwen in BR#1818808. + * + * Revision 1.154 2007/10/19 17:00:08 fabiankeil + * Downgrade "Flushing header and buffers" message to LOG_LEVEL_INFO. + * + * Revision 1.153 2007/10/14 14:12:41 fabiankeil + * When in daemon mode, close stderr after the configuration file has been + * parsed the first time. If logfile isn't set, stop logging. Fixes BR#897436. + * + * Revision 1.152 2007/10/04 18:03:34 fabiankeil + * - Fix a crash when parsing invalid requests whose first header + * is rejected by get_header(). Regression (re?)introduced + * in r1.143 by yours truly. + * - Move ACTION_VANILLA_WAFER handling into parsers.c's + * client_cookie_adder() to make sure send-vanilla-wafer can be + * controlled through tags (and thus regression-tested). + * + * Revision 1.151 2007/09/29 10:21:16 fabiankeil + * - Move get_filter_function() from jcc.c to filters.c + * so the filter functions can be static. + * - Don't bother filtering body-less responses. + * + * Revision 1.150 2007/09/28 16:39:29 fabiankeil + * Execute content filters through execute_content_filter(). + * + * Revision 1.149 2007/09/04 15:08:48 fabiankeil + * Initialize req to NULL to make sure it's defined if the + * first read_socket() call fails. Reported by icmp30. + * + * Revision 1.148 2007/08/26 16:47:13 fabiankeil + * Add Stephen Gildea's --pre-chroot-nslookup patch [#1276666], + * extensive comments moved to user manual. + * + * Revision 1.147 2007/08/25 14:42:40 fabiankeil + * Don't crash if a broken header filter wiped out the request line. + * + * Revision 1.146 2007/08/20 17:09:32 fabiankeil + * Fix byte_count calculation in case of flushes + * and don't parse the server headers a second time. + * + * Revision 1.145 2007/08/19 13:13:31 fabiankeil + * - If there's a connection problem after we already forwarded + * parts of the original content, just hang up. Fixes BR#1776724. + * - Fix warnings about unused code on mingw32. + * - In case of flushes, calculate the byte count + * less incorrectly (I think). + * + * Revision 1.144 2007/08/11 14:43:22 fabiankeil + * Add some more prototypes for static functions. + * + * Revision 1.143 2007/08/05 13:58:19 fabiankeil + * Comment out request_contains_null_bytes() until it's used again. + * + * Revision 1.142 2007/08/05 13:50:26 fabiankeil + * #1763173 from Stefan Huehner: s@const static@static const@ + * and declare some more functions static. + * * Revision 1.141 2007/08/04 09:56:23 fabiankeil * - Log rejected CONNECT requests with LOG_LEVEL_INFO * and explain why they were rejected in the first place. @@ -931,7 +1072,6 @@ http://www.fabiankeil.de/sourcecode/privoxy/ #include "filters.h" #include "loaders.h" #include "parsers.h" -#include "killpopup.h" #include "miscutil.h" #include "errlog.h" #include "jbsockets.h" @@ -957,8 +1097,28 @@ int urls_rejected = 0; /* total nr of urls rejected */ int g_terminate = 0; #endif -static void listen_loop(void); +#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) +static void sig_handler(int the_signal); +#endif +static int client_protocol_is_unsupported(const struct client_state *csp, char *req); +static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers); +static jb_err get_server_headers(struct client_state *csp); +static const char *crunch_reason(const struct http_response *rsp); +static void send_crunch_response(struct client_state *csp, struct http_response *rsp); +/* + * static int request_contains_null_bytes(const struct client_state *csp, char *buf, int len); + */ +static void build_request_line(struct client_state *csp, const struct forward_spec *fwd, char **request_line); +static jb_err change_request_destination(struct client_state *csp); static void chat(struct client_state *csp); +static void serve(struct client_state *csp); +#if !defined(_WIN32) || defined(_WIN_CONSOLE) +static void usage(const char *myname); +#endif +static void initialize_mutexes(void); +static jb_socket bind_port_helper(struct configuration_spec *config); +static void listen_loop(void); + #ifdef AMIGA void serve(struct client_state *csp); #else /* ifndef AMIGA */ @@ -1005,49 +1165,33 @@ const char *pidfile = NULL; int received_hup_signal = 0; #endif /* defined unix */ -/* The vanilla wafer. */ -static const char VANILLA_WAFER[] = - "NOTICE=TO_WHOM_IT_MAY_CONCERN_" - "Do_not_send_me_any_copyrighted_information_other_than_the_" - "document_that_I_am_requesting_or_any_of_its_necessary_components._" - "In_particular_do_not_send_me_any_cookies_that_" - "are_subject_to_a_claim_of_copyright_by_anybody._" - "Take_notice_that_I_refuse_to_be_bound_by_any_license_condition_" - "(copyright_or_otherwise)_applying_to_any_cookie._"; - /* HTTP snipplets. */ static const char CSUCCEED[] = - "HTTP/1.0 200 Connection established\n" + "HTTP/1.0 200 Connection established\r\n" "Proxy-Agent: Privoxy/" VERSION "\r\n\r\n"; static const char CHEADER[] = - "HTTP/1.0 400 Invalid header received from browser\r\n" + "HTTP/1.0 400 Invalid header received from client\r\n" "Proxy-Agent: Privoxy " VERSION "\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" - "Invalid header received from browser.\r\n"; - -static const char CFORBIDDEN[] = - "HTTP/1.0 403 Connection not allowable\r\n" - "Proxy-Agent: Privoxy " VERSION "\r\n" - "X-Hint: If you read this message interactively, then you know why this happens ,-)\r\n" - "Connection: close\r\n\r\n"; + "Invalid header received from client.\r\n"; static const char FTP_RESPONSE[] = - "HTTP/1.0 400 Invalid request received from browser\r\n" + "HTTP/1.0 400 Invalid request received from client\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" "Invalid request. Privoxy doesn't support FTP.\r\n"; static const char GOPHER_RESPONSE[] = - "HTTP/1.0 400 Invalid request received from browser\r\n" + "HTTP/1.0 400 Invalid request received from client\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" "Invalid request. Privoxy doesn't support gopher.\r\n"; /* XXX: should be a template */ static const char MISSING_DESTINATION_RESPONSE[] = - "HTTP/1.0 400 Bad request received from browser\r\n" + "HTTP/1.0 400 Bad request received from client\r\n" "Proxy-Agent: Privoxy " VERSION "\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" @@ -1060,15 +1204,25 @@ static const char NO_SERVER_DATA_RESPONSE[] = "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" "Empty server or forwarder response.\r\n" - "The connection was closed without sending any data.\r\n"; + "The connection has been closed but Privoxy didn't receive any data.\r\n"; + +/* XXX: should be a template */ +static const char INVALID_SERVER_HEADERS_RESPONSE[] = + "HTTP/1.0 502 Server or forwarder response invalid\r\n" + "Proxy-Agent: Privoxy " VERSION "\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Bad response. The server or forwarder response doesn't look like HTTP.\r\n"; +#if 0 /* XXX: should be a template */ static const char NULL_BYTE_RESPONSE[] = - "HTTP/1.0 400 Bad request received from browser\r\n" + "HTTP/1.0 400 Bad request received from client\r\n" "Proxy-Agent: Privoxy " VERSION "\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n\r\n" "Bad request. Null byte(s) before end of request.\r\n"; +#endif /* XXX: should be a template */ static const char MESSED_UP_REQUEST_RESPONSE[] = @@ -1081,8 +1235,6 @@ static const char MESSED_UP_REQUEST_RESPONSE[] = /* A function to crunch a response */ typedef struct http_response *(*crunch_func_ptr)(struct client_state *); -typedef char *(*filter_function_ptr)(); - /* Crunch function flags */ #define CF_NO_FLAGS 0 /* Cruncher applies to forced requests as well */ @@ -1097,6 +1249,8 @@ struct cruncher const int flags; }; +static int crunch_response_triggered(struct client_state *csp, const struct cruncher crunchers[]); + /* Complete list of cruncher functions */ static const struct cruncher crunchers_all[] = { { direct_response, CF_COUNT_AS_REJECT|CF_IGNORE_FORCE}, @@ -1184,8 +1338,6 @@ static void sig_handler(int the_signal) *********************************************************************/ static int client_protocol_is_unsupported(const struct client_state *csp, char *req) { - char buf[BUFFER_SIZE]; - /* * If it's a FTP or gopher request, we don't support it. * @@ -1201,21 +1353,26 @@ static int client_protocol_is_unsupported(const struct client_state *csp, char * */ if (!strncmpic(req, "GET ftp://", 10) || !strncmpic(req, "GET gopher://", 13)) { + const char *response = NULL; + const char *protocol = NULL; + if (!strncmpic(req, "GET ftp://", 10)) { - strlcpy(buf, FTP_RESPONSE, sizeof(buf)); - log_error(LOG_LEVEL_ERROR, "%s tried to use Privoxy as FTP proxy: %s", - csp->ip_addr_str, req); + response = FTP_RESPONSE; + protocol = "FTP"; } else { - strlcpy(buf, GOPHER_RESPONSE, sizeof(buf)); - log_error(LOG_LEVEL_ERROR, "%s tried to use Privoxy as gopher proxy: %s", - csp->ip_addr_str, req); + response = GOPHER_RESPONSE; + protocol = "GOPHER"; } - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, req); + log_error(LOG_LEVEL_ERROR, + "%s tried to use Privoxy as %s proxy: %s", + csp->ip_addr_str, protocol, req); + log_error(LOG_LEVEL_CLF, + "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, req); freez(req); - write_socket(csp->cfd, buf, strlen(buf)); + write_socket(csp->cfd, response, strlen(response)); return TRUE; } @@ -1323,7 +1480,7 @@ static jb_err get_server_headers(struct client_state *csp) int continue_hack_in_da_house = 0; char * header; - while (((header = get_header(csp)) != NULL) || continue_hack_in_da_house) + while (((header = get_header(csp->iob)) != NULL) || continue_hack_in_da_house) { if (header == NULL) { @@ -1374,7 +1531,6 @@ static jb_err get_server_headers(struct client_state *csp) return JB_ERR_PARSE; } - /* Enlist header */ if (JB_ERR_MEMORY == enlist(csp->headers, header)) { /* @@ -1530,6 +1686,7 @@ static void send_crunch_response(struct client_state *csp, struct http_response } +#if 0 /********************************************************************* * * Function : request_contains_null_bytes @@ -1537,6 +1694,8 @@ static void send_crunch_response(struct client_state *csp, struct http_response * Description : Checks for NULL bytes in the request and sends * an error message to the client if any were found. * + * XXX: currently not used, see comment in chat(). + * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * 2 : buf = Data from the client's request to check. @@ -1586,6 +1745,7 @@ static int request_contains_null_bytes(const struct client_state *csp, char *buf return FALSE; } +#endif /********************************************************************* @@ -1756,87 +1916,6 @@ static jb_err change_request_destination(struct client_state *csp) } -/********************************************************************* - * - * Function : get_filter_function - * - * Description : Decides which content filter function has - * to be applied (if any). - * - * XXX: Doesn't handle filter_popups() - * because of the different prototype. Probably - * we should ditch filter_popups() anyway, it's - * even less reliable than popup blocking based - * on pcrs filters. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * - * Returns : The content filter function to run, or - * NULL if no content filter is active - * - *********************************************************************/ -static filter_function_ptr get_filter_function(struct client_state *csp) -{ - filter_function_ptr filter_function = NULL; - - /* - * Are we enabling text mode by force? - */ - if (csp->action->flags & ACTION_FORCE_TEXT_MODE) - { - /* - * Do we really have to? - */ - if (csp->content_type & CT_TEXT) - { - log_error(LOG_LEVEL_HEADER, "Text mode is already enabled."); - } - else - { - csp->content_type |= CT_TEXT; - log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!"); - } - } - - if (!(csp->content_type & CT_DECLARED)) - { - /* - * The server didn't bother to declare a MIME-Type. - * Assume it's text that can be filtered. - * - * This also regulary happens with 304 responses, - * therefore logging anything here would cause - * too much noise. - */ - csp->content_type |= CT_TEXT; - } - - - /* - * Choose the applying filter function based on - * the content type and action settings. - */ - if ((csp->content_type & CT_TEXT) && - (csp->rlist != NULL) && - (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]))) - { - filter_function = pcrs_filter_response; - } - else if ((csp->content_type & CT_GIF) && - (csp->action->flags & ACTION_DEANIMATE)) - { - filter_function = gif_deanimate_response; - } - else if ((csp->content_type & CT_JPEG) && - (csp->action->flags & ACTION_JPEG_INSPECT)) - { - filter_function = jpeg_inspect_response; - } - - return filter_function; -} - /********************************************************************* * * Function : chat @@ -1861,7 +1940,7 @@ static void chat(struct client_state *csp) char buf[BUFFER_SIZE]; char *hdr; char *p; - char *req; + char *req = NULL; fd_set rfds; int n; jb_socket maxfd; @@ -1874,9 +1953,6 @@ static void chat(struct client_state *csp) struct http_request *http; int len; /* for buffer sizes (and negative error codes) */ jb_err err; -#ifdef FEATURE_KILL_POPUPS - int block_popups_now = 0; /* bool, 1==currently blocking popups */ -#endif /* def FEATURE_KILL_POPUPS */ /* Function that does the content filtering for the current request */ filter_function_ptr content_filter = NULL; @@ -1912,11 +1988,11 @@ static void chat(struct client_state *csp) return; } - req = get_header(csp); + req = get_header(csp->iob); } while ((NULL != req) && ('\0' == *req)); - if (NULL != req) + if ((NULL != req) && ('\0' != *req)) { /* Request received. Validate and parse it. */ @@ -2000,31 +2076,47 @@ static void chat(struct client_state *csp) init_list(headers); for (;;) { - if ( ( ( p = get_header(csp) ) != NULL) && ( *p == '\0' ) ) + p = get_header(csp->iob); + + if (p == NULL) { + /* There are no additional headers to read. */ + break; + } + + if (*p == '\0') + { + /* + * We didn't receive a complete header + * line yet, get the rest of it. + */ len = read_socket(csp->cfd, buf, sizeof(buf) - 1); if (len <= 0) { log_error(LOG_LEVEL_ERROR, "read from client failed: %E"); + destroy_list(headers); return; } - /* - * If there is no memory left for buffering the - * request, there is nothing we can do but hang up - */ if (add_to_iob(csp, buf, len)) { + /* + * If there is no memory left for buffering the + * request, there is nothing we can do but hang up + */ + destroy_list(headers); return; } - continue; } - - if (p == NULL) break; - - enlist(headers, p); - freez(p); - + else + { + /* + * We were able to read a complete + * header and can finaly enlist it. + */ + enlist(headers, p); + freez(p); + } } if (http->host == NULL) @@ -2044,7 +2136,7 @@ static void chat(struct client_state *csp) * An error response has already been send * and we're done here. */ - return; + return; } } @@ -2060,7 +2152,7 @@ static void chat(struct client_state *csp) else #endif /* ndef FEATURE_TOGGLE */ { - url_actions(http, csp); + get_url_actions(csp, http); } /* @@ -2079,16 +2171,6 @@ static void chat(struct client_state *csp) list_append_list_unique(csp->headers, headers); destroy_list(headers); - /* - * If the user has not supplied any wafers, and the user has not - * told us to suppress the vanilla wafer, then send the vanilla wafer. - */ - if (list_is_empty(csp->action->multi[ACTION_MULTI_WAFER]) - && ((csp->action->flags & ACTION_VANILLA_WAFER) != 0)) - { - enlist(csp->action->multi[ACTION_MULTI_WAFER], VANILLA_WAFER); - } - err = sed(client_patterns, add_client_headers, csp); if (JB_ERR_OK != err) { @@ -2097,32 +2179,33 @@ static void chat(struct client_state *csp) } csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE; - if (strcmp(http->cmd, csp->headers->first->str)) + /* Check request line for rewrites. */ + if ((NULL == csp->headers->first->str) + || (strcmp(http->cmd, csp->headers->first->str) && + (JB_ERR_OK != change_request_destination(csp)))) { /* - * A header filter rewrote the request line, - * modify the http request accordingly. + * A header filter broke the request line - bail out. */ - if (JB_ERR_OK != change_request_destination(csp)) - { - write_socket(csp->cfd, MESSED_UP_REQUEST_RESPONSE, strlen(MESSED_UP_REQUEST_RESPONSE)); - /* XXX: Use correct size */ - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request generated\" 500 0", csp->ip_addr_str); - log_error(LOG_LEVEL_ERROR, "Invalid request line after applying header filters."); + write_socket(csp->cfd, MESSED_UP_REQUEST_RESPONSE, strlen(MESSED_UP_REQUEST_RESPONSE)); + /* XXX: Use correct size */ + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request generated\" 500 0", csp->ip_addr_str); + log_error(LOG_LEVEL_ERROR, "Invalid request line after applying header filters."); - free_http_request(http); - return; - } + free_http_request(http); + return; } /* decide how to route the HTTP request */ - if (NULL == (fwd = forward_url(http, csp))) + fwd = forward_url(csp, http); + if (NULL == fwd) { log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!? This can't happen!"); /* Never get here - LOG_LEVEL_FATAL causes program exit */ } - /* build the http request to send to the server + /* + * build the http request to send to the server * we have to do one of the following: * * create = use the original HTTP request to create a new @@ -2158,50 +2241,16 @@ static void chat(struct client_state *csp) * */ - /* - * Check if a CONNECT request is allowable: - * In the absence of a +limit-connect action, allow only port 443. - * If there is an action, allow whatever matches the specificaton. - */ - if(http->ssl) + if (http->ssl && connect_port_is_forbidden(csp)) { - if( ( !(csp->action->flags & ACTION_LIMIT_CONNECT) && csp->http->port != 443) - || (csp->action->flags & ACTION_LIMIT_CONNECT - && !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) ) - { - if (csp->action->flags & ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS) - { - /* - * The response may confuse some clients, - * but makes unblocking easier. - * - * XXX: It seems to work with all major browsers, - * so we should consider returning a body by default someday ... - */ - log_error(LOG_LEVEL_INFO, "Request from %s marked for blocking. " - "limit-connect{%s} doesn't allow CONNECT requests to port %d.", - csp->ip_addr_str, csp->action->string[ACTION_STRING_LIMIT_CONNECT], - csp->http->port); - csp->action->flags |= ACTION_BLOCK; - http->ssl = 0; - } - else - { - write_socket(csp->cfd, CFORBIDDEN, strlen(CFORBIDDEN)); - log_error(LOG_LEVEL_INFO, "Request from %s denied. " - "limit-connect{%s} doesn't allow CONNECT requests to port %d.", - csp->ip_addr_str, csp->action->string[ACTION_STRING_LIMIT_CONNECT], - csp->http->port); - assert(NULL != csp->http->ocmd); - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 403 0", csp->ip_addr_str, csp->http->ocmd); - - list_remove_all(csp->headers); - /* - * XXX: For consistency we might want to log a crunch message here. - */ - return; - } - } + const char *acceptable_connect_ports = + csp->action->string[ACTION_STRING_LIMIT_CONNECT]; + assert(NULL != acceptable_connect_ports); + log_error(LOG_LEVEL_INFO, "Request from %s marked for blocking. " + "limit-connect{%s} doesn't allow CONNECT requests to port %d.", + csp->ip_addr_str, acceptable_connect_ports, csp->http->port); + csp->action->flags |= ACTION_BLOCK; + http->ssl = 0; } if (http->ssl == 0) @@ -2299,7 +2348,7 @@ static void chat(struct client_state *csp) */ if (write_socket(csp->sfd, hdr, strlen(hdr)) - || (flush_socket(csp->sfd, csp) < 0)) + || (flush_socket(csp->sfd, csp->iob) < 0)) { log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E", http->hostport); @@ -2413,10 +2462,23 @@ static void chat(struct client_state *csp) "CONNECT already confirmed. Unable to tell the client about the problem."); return; } + else if (byte_count) + { + /* + * Just hang up. We already transmitted the original headers + * and parts of the original content and therefore missed the + * chance to send an error message (without risking data corruption). + * + * XXX: we could retry with a fancy range request here. + */ + log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. " + "Unable to tell the client about the problem."); + return; + } rsp = error_response(csp, "connect-failed", errno); - if(rsp) + if (rsp) { send_crunch_response(csp, rsp); } @@ -2429,14 +2491,6 @@ static void chat(struct client_state *csp) */ buf[len] = '\0'; -#ifdef FEATURE_KILL_POPUPS - /* Filter the popups on this read. */ - if (block_popups_now) - { - filter_popups(buf, csp); - } -#endif /* def FEATURE_KILL_POPUPS */ - /* Normally, this would indicate that we've read * as much as the server has sent us and we can * close the client connection. However, Microsoft @@ -2467,12 +2521,13 @@ static void chat(struct client_state *csp) */ if (content_filter) { + p = execute_content_filter(csp, content_filter); /* * If the content filter fails, use the original * buffer and length. * (see p != NULL ? p : csp->iob->cur below) */ - if (NULL == (p = (*content_filter)(csp))) + if (NULL == p) { csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur); } @@ -2546,11 +2601,8 @@ static void chat(struct client_state *csp) size_t hdrlen; int flushed; - log_error(LOG_LEVEL_ERROR, "Flushing header and buffers. Stepping back from filtering."); - if (JB_ERR_OK != sed(server_patterns, add_server_headers, csp)) - { - log_error(LOG_LEVEL_FATAL, "Failed to parse server headers."); - } + log_error(LOG_LEVEL_INFO, "Flushing header and buffers. Stepping back from filtering."); + hdr = list_to_text(csp->headers); if (hdr == NULL) { @@ -2564,23 +2616,10 @@ static void chat(struct client_state *csp) return; } - - if (crunch_response_triggered(csp, crunchers_light)) - { - /* - * One of the tags created by a server-header - * tagger triggered a crunch. We already - * delivered the crunch response to the client - * and are done here after cleaning up. - */ - freez(hdr); - return; - } - hdrlen = strlen(hdr); if (write_socket(csp->cfd, hdr, hdrlen) - || ((flushed = flush_socket(csp->cfd, csp)) < 0) + || ((flushed = flush_socket(csp->cfd, csp->iob)) < 0) || (write_socket(csp->cfd, buf, (size_t)len))) { log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E"); @@ -2589,11 +2628,15 @@ static void chat(struct client_state *csp) return; } - byte_count += hdrlen + (size_t)flushed + (size_t)len; + /* + * Reset the byte_count to the amount of bytes + * we just flushed. len will be added a few lines below, + * hdrlen doesn't matter for LOG_LEVEL_CLF. + */ + byte_count = (size_t)flushed; freez(hdr); content_filter = NULL; server_body = 1; - } } else @@ -2639,6 +2682,8 @@ static void chat(struct client_state *csp) * and there isn't anything * we can do about it. */ + log_error(LOG_LEVEL_INFO, + "MS IIS5 hack didn't produce valid headers."); break; } else @@ -2662,6 +2707,29 @@ static void chat(struct client_state *csp) return; } + assert(csp->headers->first->str); + assert(!http->ssl); + if (strncmpic(csp->headers->first->str, "HTTP", 4)) + { + /* + * It doesn't look like a HTTP response: + * tell the client and log the problem. + */ + if (strlen(csp->headers->first->str) > 30) + { + csp->headers->first->str[30] = '\0'; + } + log_error(LOG_LEVEL_ERROR, + "Invalid server or forwarder response. Starts with: %s", + csp->headers->first->str); + log_error(LOG_LEVEL_CLF, + "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); + write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE)); + free_http_request(http); + return; + } + /* we have now received the entire header. * filter it and send the result to the client */ @@ -2691,20 +2759,6 @@ static void chat(struct client_state *csp) if (!http->ssl) /* We talk plaintext */ { - -#ifdef FEATURE_KILL_POPUPS - /* Start blocking popups if appropriate. */ - if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */ - (csp->action->flags & ACTION_NO_POPUPS) != 0) /* Policy allows */ - { - block_popups_now = 1; - /* - * Filter the part of the body that came in the same read - * as the last headers: - */ - filter_popups(csp->iob->cur, csp); - } -#endif /* def FEATURE_KILL_POPUPS */ content_filter = get_filter_function(csp); } /* @@ -2718,7 +2772,7 @@ static void chat(struct client_state *csp) */ if (write_socket(csp->cfd, hdr, strlen(hdr)) - || ((len = flush_socket(csp->cfd, csp)) < 0)) + || ((len = flush_socket(csp->cfd, csp->iob)) < 0)) { log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E"); @@ -2745,6 +2799,8 @@ static void chat(struct client_state *csp) */ if (ms_iis5_hack) { + log_error(LOG_LEVEL_INFO, + "Closed server connection detected with MS IIS5 hack enabled."); break; } } @@ -2823,6 +2879,7 @@ static int32 server_thread(void *data) #endif +#if !defined(_WIN32) || defined(_WIN_CONSOLE) /********************************************************************* * * Function : usage @@ -2843,7 +2900,7 @@ static void usage(const char *myname) #endif /* defined(unix) */ "[--help] " #if defined(unix) - "[--no-daemon] [--pidfile pidfile] [--user user[.group]] " + "[--no-daemon] [--pidfile pidfile] [--pre-chroot-nslookup hostname] [--user user[.group]] " #endif /* defined(unix) */ "[--version] [configfile]\n" "Aborting\n", myname); @@ -2851,6 +2908,7 @@ static void usage(const char *myname) exit(2); } +#endif /* #if !defined(_WIN32) || defined(_WIN_CONSOLE) */ /********************************************************************* @@ -2955,6 +3013,7 @@ int main(int argc, const char *argv[]) struct group *grp = NULL; char *p; int do_chroot = 0; + char *pre_chroot_nslookup_to_load_resolver = NULL; #endif Argc = argc; @@ -2972,7 +3031,7 @@ int main(int argc, const char *argv[]) * Parse the command line arguments * * XXX: simply printing usage information in case of - * invalid arguments isn't particular user friendly. + * invalid arguments isn't particularly user friendly. */ while (++argc_pos < argc) { @@ -3049,6 +3108,12 @@ int main(int argc, const char *argv[]) if (p != NULL) *--p = '\0'; } + else if (strcmp(argv[argc_pos], "--pre-chroot-nslookup" ) == 0) + { + if (++argc_pos == argc) usage(argv[0]); + pre_chroot_nslookup_to_load_resolver = strdup(argv[argc_pos]); + } + else if (strcmp(argv[argc_pos], "--chroot" ) == 0) { do_chroot = 1; @@ -3108,6 +3173,7 @@ int main(int argc, const char *argv[]) files->next = NULL; clients->next = NULL; + /* XXX: factor out initialising after the next stable release. */ #ifdef AMIGA InitAmiga(); #elif defined(_WIN32) @@ -3117,9 +3183,21 @@ int main(int argc, const char *argv[]) /* Prepare mutexes if supported and necessary. */ initialize_mutexes(); + /* Enable logging until further notice. */ + init_log_module(Argv[0]); + random_seed = (unsigned int)time(NULL); #ifdef HAVE_RANDOM srandom(random_seed); +#elif defined (_WIN32) + /* + * See pick_from_range() in miscutil.c for details. + */ + log_error(LOG_LEVEL_INFO, + "No thread-safe PRNG implemented for your platform. " + "Using weak \'randomization\' factor which will " + "limit the already questionable usefulness of " + "header-time-randomizing actions (disabled by default)."); #else srand(random_seed); #endif /* ifdef HAVE_RANDOM */ @@ -3227,10 +3305,9 @@ int main(int argc, const char *argv[]) close ( fd ); } #endif /* 1 */ - /* FIXME: should close stderr (fd 2) here too, but the test - * for existence - * and load config file is done in listen_loop() and puts - * some messages on stderr there. + /* + * stderr (fd 2) will be closed later on, when the + * log file has been parsed. */ close( 0 ); @@ -3257,6 +3334,14 @@ int main(int argc, const char *argv[]) { log_error(LOG_LEVEL_FATAL, "Home directory for %s undefined", pw->pw_name); } + /* Read the time zone file from /etc before doing chroot. */ + tzset(); + if (NULL != pre_chroot_nslookup_to_load_resolver + && '\0' != pre_chroot_nslookup_to_load_resolver[0]) + { + /* Initialize resolver library. */ + (void) resolve_hostname_to_ip(pre_chroot_nslookup_to_load_resolver); + } if (chroot(pw->pw_dir) < 0) { log_error(LOG_LEVEL_FATAL, "Cannot chroot to %s", pw->pw_dir); @@ -3352,16 +3437,7 @@ static jb_socket bind_port_helper(struct configuration_spec * config) int result; jb_socket bfd; - if ( (config->haddr != NULL) - && (config->haddr[0] == '1') - && (config->haddr[1] == '2') - && (config->haddr[2] == '7') - && (config->haddr[3] == '.') ) - { - log_error(LOG_LEVEL_INFO, "Listening on port %d for local connections only", - config->hport); - } - else if (config->haddr == NULL) + if (config->haddr == NULL) { log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses", config->hport); @@ -3461,7 +3537,7 @@ static void listen_loop(void) */ if (received_hup_signal) { - init_error_log(Argv[0], config->logfile, config->debug); + init_error_log(Argv[0], config->logfile); received_hup_signal = 0; } #endif @@ -3536,10 +3612,10 @@ static void listen_loop(void) #ifdef FEATURE_TOGGLE if (global_toggle_state) +#endif /* def FEATURE_TOGGLE */ { csp->flags |= CSP_FLAG_TOGGLED_ON; } -#endif /* def FEATURE_TOGGLE */ if (run_loader(csp)) {