X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=00c92c1fadd95ea8d60430a1ed348c20773f9104;hp=8d3c79e4e7d7d7cdda9dac0c0f96cba744eff1a5;hb=f8d5fd3d89f98dd2c18ccfafe51e163998e4f4fd;hpb=b081249a57fcdcd2990db66e384d5a15dc2dc4cf diff --git a/jcc.c b/jcc.c index 8d3c79e4..00c92c1f 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.423 2013/01/03 15:25:08 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.434 2015/01/24 16:41:20 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -6,7 +6,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.423 2013/01/03 15:25:08 fabiankeil Exp $" * Purpose : Main file. Contains main() method, main loop, and * the main connection-handling function. * - * Copyright : Written by and Copyright (C) 2001-2012 the + * Copyright : Written by and Copyright (C) 2001-2014 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -182,6 +182,10 @@ privoxy_mutex_t log_mutex; privoxy_mutex_t log_init_mutex; privoxy_mutex_t connection_reuse_mutex; +#ifdef FEATURE_EXTERNAL_FILTERS +privoxy_mutex_t external_filter_mutex; +#endif + #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) privoxy_mutex_t resolver_mutex; #endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */ @@ -275,6 +279,13 @@ static const char CLIENT_BODY_PARSE_ERROR_RESPONSE[] = "Connection: close\r\n\r\n" "Failed parsing or buffering the chunk-encoded client body.\r\n"; +static const char UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE[] = + "HTTP/1.1 417 Expecting too much\r\n" + "Proxy-Agent: Privoxy " VERSION "\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Privoxy detected an unsupported Expect header value.\r\n"; + /* A function to crunch a response */ typedef struct http_response *(*crunch_func_ptr)(struct client_state *); @@ -433,6 +444,40 @@ static int client_protocol_is_unsupported(const struct client_state *csp, char * } +/********************************************************************* + * + * Function : client_has_unsupported_expectations + * + * Description : Checks if the client used an unsupported expectation + * in which case an error message is delivered. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : TRUE if an error response has been generated, or + * FALSE if the request doesn't look invalid. + * + *********************************************************************/ +static int client_has_unsupported_expectations(const struct client_state *csp) +{ + if ((csp->flags & CSP_FLAG_UNSUPPORTED_CLIENT_EXPECTATION)) + { + log_error(LOG_LEVEL_ERROR, + "Rejecting request from client %s with unsupported Expect header value", + csp->ip_addr_str); + log_error(LOG_LEVEL_CLF, + "%s - - [%T] \"%s\" 417 0", csp->ip_addr_str, csp->http->cmd); + write_socket(csp->cfd, UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE, + strlen(UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE)); + + return TRUE; + } + + return FALSE; + +} + + /********************************************************************* * * Function : get_request_destination_elsewhere @@ -920,11 +965,6 @@ static jb_err change_request_destination(struct client_state *csp) log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.", jb_err_to_string(err)); } - else - { - /* XXX: ocmd is a misleading name */ - http->ocmd = strdup_or_die(http->cmd); - } return err; } @@ -1293,7 +1333,7 @@ enum chunk_status * * Function : chunked_body_is_complete * - * Description : Figures out wheter or not a chunked body is complete. + * Description : Figures out whether or not a chunked body is complete. * * Currently it always starts at the beginning of the * buffer which is somewhat wasteful and prevents Privoxy @@ -1348,12 +1388,15 @@ static enum chunk_status chunked_body_is_complete(struct iob *iob, size_t *lengt { return CHUNK_STATUS_PARSE_ERROR; } - /* - * Skip "\r\n", the chunk data and another "\r\n". - * Moving p to either the beginning of the next chunk-size - * or one byte beyond the end of the chunked data. - */ - p += 2 + chunksize + 2; + /* Move beyond the chunkdata. */ + p += 2 + chunksize; + + /* There should be another "\r\n" to skip */ + if (memcmp(p, "\r\n", 2)) + { + return CHUNK_STATUS_PARSE_ERROR; + } + p += 2; } while (chunksize > 0U); *length = (size_t)(p - iob->cur); @@ -1603,17 +1646,13 @@ static jb_err receive_client_request(struct client_state *csp) get_url_actions(csp, http); } - /* - * Save a copy of the original request for logging - */ - http->ocmd = strdup_or_die(http->cmd); enlist(csp->headers, http->cmd); /* Append the previously read headers */ - list_append_list_unique(csp->headers, headers); + err = list_append_list_unique(csp->headers, headers); destroy_list(headers); - return JB_ERR_OK; + return err; } @@ -1673,9 +1712,12 @@ static jb_err parse_client_request(struct client_state *csp) err = sed(csp, FILTER_CLIENT_HEADERS); if (JB_ERR_OK != err) { - /* XXX: Should be handled in sed(). */ - assert(err == JB_ERR_PARSE); - log_error(LOG_LEVEL_FATAL, "Failed to parse client headers."); + log_error(LOG_LEVEL_ERROR, "Failed to parse client request from %s.", + csp->ip_addr_str); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", + csp->ip_addr_str, csp->http->cmd); + write_socket(csp->cfd, CHEADER, strlen(CHEADER)); + return JB_ERR_PARSE; } csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE; @@ -1698,6 +1740,11 @@ static jb_err parse_client_request(struct client_state *csp) return JB_ERR_PARSE; } + if (client_has_unsupported_expectations(csp)) + { + return JB_ERR_PARSE; + } + return JB_ERR_OK; } @@ -1909,6 +1956,21 @@ static void chat(struct client_state *csp) send_crunch_response(csp, rsp); } + /* + * Temporary workaround to prevent already-read client + * bodies from being parsed as new requests. For now we + * err on the safe side and throw all the following + * requests under the bus, even if no client body has been + * buffered. A compliant client will repeat the dropped + * requests on an untainted connection. + * + * The proper fix is to discard the no longer needed + * client body in the buffer (if there is one) and to + * continue parsing the bytes that follow. + */ + drain_and_close_socket(csp->cfd); + csp->cfd = JB_INVALID_SOCKET; + return; } #ifdef FEATURE_CONNECTION_KEEP_ALIVE @@ -2553,7 +2615,13 @@ static void chat(struct client_state *csp) */ if (JB_ERR_OK != sed(csp, FILTER_SERVER_HEADERS)) { - log_error(LOG_LEVEL_FATAL, "Failed to parse server headers."); + 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); + mark_server_socket_tainted(csp); + return; } hdr = list_to_text(csp->headers); if (hdr == NULL) @@ -3134,6 +3202,9 @@ static void initialize_mutexes(void) privoxy_mutex_init(&log_mutex); privoxy_mutex_init(&log_init_mutex); privoxy_mutex_init(&connection_reuse_mutex); +#ifdef FEATURE_EXTERNAL_FILTERS + privoxy_mutex_init(&external_filter_mutex); +#endif /* * XXX: The assumptions below are a bit naive @@ -3444,7 +3515,7 @@ int main(int argc, char **argv) cgi_init_error_messages(); /* - * If runnig on unix and without the --nodaemon + * If running on unix and without the --no-daemon * option, become a daemon. I.e. fork, detach * from tty and get process group leadership */ @@ -3518,6 +3589,13 @@ int main(int argc, char **argv) close(fd); } +#ifdef FEATURE_EXTERNAL_FILTERS + for (fd = 0; fd < 3; fd++) + { + mark_socket_for_close_on_execute(fd); + } +#endif + chdir("/"); } /* -END- if (daemon_mode) */ @@ -3643,7 +3721,7 @@ int main(int argc, char **argv) * on failure. * * Parameters : - * 1 : haddr = Host addres to bind to. Use NULL to bind to + * 1 : haddr = Host address to bind to. Use NULL to bind to * INADDR_ANY. * 2 : hport = Specifies port to bind to. *