X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=408612bb309f23e27724676bf1b82ddb1a21a026;hb=24283c9444f87d167cdd9efbcaad109b1d44b3d9;hp=0e83a68fcdbce2565acb2b35e12f6f7316393bdc;hpb=0faa75b156fa3f5c0e09a620a9a16db5996c3e7a;p=privoxy.git diff --git a/jcc.c b/jcc.c index 0e83a68f..408612bb 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.135 2007/05/24 17:03:50 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.139 2007/07/14 07:46:41 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -33,6 +33,29 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.135 2007/05/24 17:03:50 fabiankeil Exp $" * * Revisions : * $Log: jcc.c,v $ + * Revision 1.139 2007/07/14 07:46:41 fabiankeil + * - Allow to rewrite the request destination behind the client's back. + * - Turn the weird-looking unconditional for loop that + * reads the client request into a conditional while loop. + * Move the stuff that only runs once out of the loop. + * - Move parts of chat(), server_content_type() and the + * necessary stuff to fix BR#1750917 into get_filter_function(). + * + * Revision 1.138 2007/06/03 18:45:18 fabiankeil + * Temporary workaround for BR#1730105. + * + * Revision 1.137 2007/06/01 18:16:36 fabiankeil + * Use the same mutex for gethostbyname() and gethostbyaddr() to prevent + * deadlocks and crashes on OpenBSD and possibly other OS with neither + * gethostbyname_r() nor gethostaddr_r(). Closes BR#1729174. + * Thanks to Ralf Horstmann for report and solution. + * + * Revision 1.136 2007/06/01 16:41:11 fabiankeil + * Add forward-override{} to change the forwarding settings through + * action sections. This is mainly interesting to forward different + * clients differently (for example based on User-Agent or request + * origin). + * * Revision 1.135 2007/05/24 17:03:50 fabiankeil * - Let usage() mention the --chroot parameter. * - Use read_socket() consistently and always leave @@ -945,6 +968,10 @@ static int32 server_thread(void *data); pthread_mutex_t log_mutex; pthread_mutex_t log_init_mutex; +#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) +pthread_mutex_t resolver_mutex; +#endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */ + #ifndef HAVE_GMTIME_R pthread_mutex_t gmtime_mutex; #endif /* ndef HAVE_GMTIME_R */ @@ -953,14 +980,6 @@ pthread_mutex_t gmtime_mutex; pthread_mutex_t localtime_mutex; #endif /* ndef HAVE_GMTIME_R */ -#ifndef HAVE_GETHOSTBYADDR_R -pthread_mutex_t gethostbyaddr_mutex; -#endif /* ndef HAVE_GETHOSTBYADDR_R */ - -#ifndef HAVE_GETHOSTBYNAME_R -pthread_mutex_t gethostbyname_mutex; -#endif /* ndef HAVE_GETHOSTBYNAME_R */ - #ifndef HAVE_RANDOM pthread_mutex_t rand_mutex; #endif /* ndef HAVE_RANDOM */ @@ -1038,9 +1057,19 @@ const static char NULL_BYTE_RESPONSE[] = "Connection: close\r\n\r\n" "Bad request. Null byte(s) before end of request.\r\n"; +/* XXX: should be a template */ +const static char MESSED_UP_REQUEST_RESPONSE[] = + "HTTP/1.0 400 Malformed request after rewriting\r\n" + "Proxy-Agent: Privoxy " VERSION "\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Bad request. Messed up with header filters.\r\n"; + /* 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 */ @@ -1566,6 +1595,19 @@ int crunch_response_triggered(struct client_state *csp, const struct cruncher cr struct http_response *rsp = NULL; const struct cruncher *c; + /* + * If CGI request crunching is disabled, + * check the CGI dispatcher out of order to + * prevent unintentional blocks or redirects. + */ + if (!(csp->config->feature_flags & RUNTIME_FEATURE_CGI_CRUNCHING) + && (NULL != (rsp = dispatch_cgi(csp)))) + { + /* Deliver, log and free the interception response. */ + send_crunch_response(csp, rsp); + return TRUE; + } + for (c = crunchers; c->cruncher != NULL; c++) { /* @@ -1664,6 +1706,124 @@ void build_request_line(struct client_state *csp, const struct forward_spec *fwd } +/********************************************************************* + * + * Function : change_request_destination + * + * Description : Parse a (rewritten) request line and regenerate + * the http request data. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Forwards the parse_http_request() return code. + * Terminates in case of memory problems. + * + *********************************************************************/ +jb_err change_request_destination(struct client_state *csp) +{ + struct http_request *http = csp->http; + jb_err err; + + log_error(LOG_LEVEL_INFO, "Rewrite detected: %s", csp->headers->first->str); + free_http_request(http); + err = parse_http_request(csp->headers->first->str, http, csp); + if (JB_ERR_OK != err) + { + log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.", + jb_err_to_string(err)); + } + http->ocmd = strdup(http->cmd); /* XXX: ocmd is a misleading name */ + if (http->ocmd == NULL) + { + log_error(LOG_LEVEL_FATAL, "Out of memory copying rewritten HTTP request line"); + } + + return err; +} + + +/********************************************************************* + * + * 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 + * + *********************************************************************/ +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 @@ -1700,17 +1860,13 @@ static void chat(struct client_state *csp) const struct forward_spec * fwd; struct http_request *http; int len; /* for buffer sizes (and negative error codes) */ + jb_err err; #ifdef FEATURE_KILL_POPUPS - int block_popups; /* bool, 1==will block popups */ int block_popups_now = 0; /* bool, 1==currently blocking popups */ #endif /* def FEATURE_KILL_POPUPS */ - int pcrs_filter; /* bool, 1==will filter through pcrs */ - int gif_deanimate; /* bool, 1==will deanimate gifs */ - int jpeg_inspect; /* bool, 1==will inspect jpegs */ - /* Function that does the content filtering for the current request */ - char *(*content_filter)() = NULL; + filter_function_ptr content_filter = NULL; /* Skeleton for HTTP response, if we should intercept the request */ struct http_response *rsp; @@ -1728,18 +1884,12 @@ static void chat(struct client_state *csp) * could get blocked here if a client connected, then didn't say anything! */ - for (;;) + do { len = read_socket(csp->cfd, buf, sizeof(buf) - 1); if (len <= 0) break; /* error! */ - if (request_contains_null_bytes(csp, buf, len)) - { - /* NULL bytes found and dealt with, just hang up. */ - return; - } - /* * If there is no memory left for buffering the * request, there is nothing we can do but hang up @@ -1751,15 +1901,35 @@ static void chat(struct client_state *csp) req = get_header(csp); - if (req == NULL) - { - break; /* no HTTP request! */ - } + } while ((NULL != req) && ('\0' == *req)); + + if (NULL != req) + { + /* Request received. Validate and parse it. */ - if (*req == '\0') +#if 0 + /* + * XXX: Temporary disabled to prevent problems + * with POST requests whose bodies are allowed to + * contain NULL bytes. BR#1730105. + * + * The main purpose of this check is to properly + * log stuff like BitTorrent traffic and other junk + * that hits public proxies. It's not required for + * Privoxy to functions as those requests are discarded + * later on anyway. + * + * It probably should be rewritten to only check + * the head of the request. Another option would + * be to let all POST requests pass, although that + * may not be good enough. + */ + if (request_contains_null_bytes(csp, buf, len)) { - continue; /* more to come! */ + /* NULL bytes found and dealt with, just hang up. */ + return; } +#endif /* Does the request line look invalid? */ if (client_protocol_is_unsupported(csp, req)) @@ -1793,19 +1963,13 @@ static void chat(struct client_state *csp) } #endif /* def FEATURE_FORCE_LOAD */ - - switch( parse_http_request(req, http, csp) ) + err = parse_http_request(req, http, csp); + if (JB_ERR_OK != err) { - case JB_ERR_MEMORY: - log_error(LOG_LEVEL_ERROR, "Out of memory while parsing request."); - break; - case JB_ERR_PARSE: - log_error(LOG_LEVEL_ERROR, "Couldn't parse request: %s.", req); - break; + log_error(LOG_LEVEL_ERROR, "Couldn't parse request: %s.", jb_err_to_string(err)); } freez(req); - break; } if (http->cmd == NULL) @@ -1912,12 +2076,32 @@ static void chat(struct client_state *csp) enlist(csp->action->multi[ACTION_MULTI_WAFER], VANILLA_WAFER); } - if (JB_ERR_OK != sed(client_patterns, add_client_headers, csp)) + err = sed(client_patterns, add_client_headers, csp); + if (JB_ERR_OK != err) { + assert(err == JB_ERR_PARSE); log_error(LOG_LEVEL_FATAL, "Failed to parse client headers"); } csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE; + if (strcmp(http->cmd, csp->headers->first->str)) + { + /* + * A header filter rewrote the request line, + * modify the http request accordingly. + */ + 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."); + + free_http_request(http); + return; + } + } + /* decide how to route the HTTP request */ if (NULL == (fwd = forward_url(http, csp))) { @@ -2009,17 +2193,6 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header"); } -#ifdef FEATURE_KILL_POPUPS - block_popups = ((csp->action->flags & ACTION_NO_POPUPS) != 0); -#endif /* def FEATURE_KILL_POPUPS */ - - pcrs_filter = (csp->rlist != NULL) && /* There are expressions to be used */ - (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])); - - gif_deanimate = ((csp->action->flags & ACTION_DEANIMATE) != 0); - - jpeg_inspect = ((csp->action->flags & ACTION_JPEG_INSPECT) != 0); - /* * We have a request. Check if one of the crunchers wants it. */ @@ -2490,50 +2663,26 @@ static void chat(struct client_state *csp) freez(hdr); return; } -#ifdef FEATURE_KILL_POPUPS - /* Start blocking popups if appropriate. */ - - if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */ - !http->ssl && /* We talk plaintext */ - block_popups) /* 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 */ - /* Buffer and pcrs filter this if appropriate. */ - if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */ - !http->ssl && /* We talk plaintext */ - pcrs_filter) /* Policy allows */ + if (!http->ssl) /* We talk plaintext */ { - content_filter = pcrs_filter_response; - } - /* Buffer and gif_deanimate this if appropriate. */ - - if ((csp->content_type & CT_GIF) && /* It's an image/gif MIME-Type */ - !http->ssl && /* We talk plaintext */ - gif_deanimate) /* Policy allows */ - { - content_filter = gif_deanimate_response; - } - - /* Buffer and jpg_inspect this if appropriate. */ - - if ((csp->content_type & CT_JPEG) && /* It's an image/jpeg MIME-Type */ - !http->ssl && /* We talk plaintext */ - jpeg_inspect) /* Policy allows */ - { - content_filter = jpeg_inspect_response; +#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); } - /* * Only write if we're not buffering for content modification */ @@ -2711,6 +2860,13 @@ void initialize_mutexes() * have no gethostbyname_r, but gethostbyname is * thread safe. */ +#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) + if (!err) err = pthread_mutex_init(&resolver_mutex, 0); +#endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */ + /* + * XXX: should we use a single mutex for + * localtime() and gmtime() as well? + */ #ifndef HAVE_GMTIME_R if (!err) err = pthread_mutex_init(&gmtime_mutex, 0); #endif /* ndef HAVE_GMTIME_R */ @@ -2719,14 +2875,6 @@ void initialize_mutexes() if (!err) err = pthread_mutex_init(&localtime_mutex, 0); #endif /* ndef HAVE_GMTIME_R */ -#ifndef HAVE_GETHOSTBYADDR_R - if (!err) err = pthread_mutex_init(&gethostbyaddr_mutex, 0); -#endif /* ndef HAVE_GETHOSTBYADDR_R */ - -#ifndef HAVE_GETHOSTBYNAME_R - if (!err) err = pthread_mutex_init(&gethostbyname_mutex, 0); -#endif /* ndef HAVE_GETHOSTBYNAME_R */ - #ifndef HAVE_RANDOM if (!err) err = pthread_mutex_init(&rand_mutex, 0); #endif /* ndef HAVE_RANDOM */