X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=53de19a607b4ca16450e2307b83f9877edbbef18;hp=6f1f06c4cf477c4ee9a302d8939b43b89bf0adea;hb=59587f70b2176457abad3d978404a32205d432a3;hpb=f490deef1980fba26c921c515941a8fc0879976b diff --git a/jcc.c b/jcc.c index 6f1f06c4..53de19a6 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.415 2012/11/24 14:00:25 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.421 2012/12/07 12:50:37 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -268,6 +268,13 @@ static const char CLIENT_CONNECTION_TIMEOUT_RESPONSE[] = "Connection: close\r\n\r\n" "The connection timed out because the client request didn't arrive in time.\r\n"; +static const char CLIENT_BODY_PARSE_ERROR_RESPONSE[] = + "HTTP/1.1 400 Failed reading client body\r\n" + "Proxy-Agent: Privoxy " VERSION "\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Failed parsing or buffering the chunk-encoded client body.\r\n"; + /* A function to crunch a response */ typedef struct http_response *(*crunch_func_ptr)(struct client_state *); @@ -904,7 +911,8 @@ static 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); + log_error(LOG_LEVEL_REDIRECTS, "Rewrite detected: %s", + csp->headers->first->str); free_http_request(http); err = parse_http_request(csp->headers->first->str, http); if (JB_ERR_OK != err) @@ -1120,9 +1128,7 @@ static void verify_request_length(struct client_state *csp) { /* XXX: this is an incomplete hack */ csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; - csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED; - log_error(LOG_LEVEL_CONNECT, - "There might be a request body. The connection will not be kept alive."); + log_error(LOG_LEVEL_CONNECT, "There better be a request body."); } else { @@ -1275,6 +1281,146 @@ static char *get_request_line(struct client_state *csp) } +enum chunk_status +{ + CHUNK_STATUS_MISSING_DATA, + CHUNK_STATUS_BODY_COMPLETE, + CHUNK_STATUS_PARSE_ERROR +}; + + +/********************************************************************* + * + * Function : chunked_body_is_complete + * + * Description : Figures out wheter or not a chunked body is complete. + * + * Currently it always starts at the beginning of the + * buffer which is somewhat wasteful and prevents Privoxy + * from starting to forward the correctly parsed chunks + * as soon as theoretically possible. + * + * Should be modified to work with a common buffer, + * and allow the caller to skip already parsed chunks. + * + * This would allow the function to be used for unbuffered + * response bodies as well. + * + * Parameters : + * 1 : iob = Buffer with the body to check. + * 2 : length = Length of complete body + * + * Returns : Enum with the result of the check. + * + *********************************************************************/ +static enum chunk_status chunked_body_is_complete(struct iob *iob, size_t *length) +{ + unsigned int chunksize; + char *p = iob->cur; + + do + { + /* + * We need at least a single digit, followed by "\r\n", + * followed by an unknown amount of data, followed by "\r\n". + */ + if (p + 5 > iob->eod) + { + return CHUNK_STATUS_MISSING_DATA; + } + if (sscanf(p, "%x", &chunksize) != 1) + { + return CHUNK_STATUS_PARSE_ERROR; + } + + /* + * We want at least a single digit, followed by "\r\n", + * followed by the specified amount of data, followed by "\r\n". + */ + if (p + chunksize + 5 > iob->eod) + { + return CHUNK_STATUS_MISSING_DATA; + } + + /* Skip chunk-size. */ + p = strstr(p, "\r\n"); + if (NULL == p) + { + 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; + } while (chunksize > 0U); + + *length = (size_t)(p - iob->cur); + assert(*length <= (size_t)(iob->eod - iob->cur)); + assert(p <= iob->eod); + + return CHUNK_STATUS_BODY_COMPLETE; + +} + + +/********************************************************************* + * + * Function : receive_chunked_client_request_body + * + * Description : Read the chunk-encoded client request body. + * Failures are dealt with. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK or JB_ERR_PARSE + * + *********************************************************************/ +static jb_err receive_chunked_client_request_body(struct client_state *csp) +{ + size_t body_length; + enum chunk_status status; + + while (CHUNK_STATUS_MISSING_DATA == + (status = chunked_body_is_complete(csp->client_iob,&body_length))) + { + char buf[BUFFER_SIZE]; + int len; + + if (!data_is_available(csp->cfd, csp->config->socket_timeout)) + { + log_error(LOG_LEVEL_ERROR, + "Timeout while waiting for the client body."); + break; + } + len = read_socket(csp->cfd, buf, sizeof(buf) - 1); + if (len <= 0) + { + log_error(LOG_LEVEL_ERROR, "Read the client body failed: %E"); + break; + } + if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len)) + { + break; + } + } + if (status != CHUNK_STATUS_BODY_COMPLETE) + { + write_socket(csp->cfd, CLIENT_BODY_PARSE_ERROR_RESPONSE, + strlen(CLIENT_BODY_PARSE_ERROR_RESPONSE)); + log_error(LOG_LEVEL_CLF, + "%s - - [%T] \"Failed reading chunked client body\" 400 0", csp->ip_addr_str); + return JB_ERR_PARSE; + } + log_error(LOG_LEVEL_CONNECT, + "Chunked client body completely read. Length: %d", body_length); + csp->expected_client_content_length = body_length; + + return JB_ERR_OK; + +} /********************************************************************* * @@ -1404,6 +1550,14 @@ static jb_err receive_client_request(struct client_state *csp) } else { + if (!strncmpic(p, "Transfer-Encoding:", 18)) + { + /* + * XXX: should be called through sed() + * but currently can't. + */ + client_transfer_encoding(csp, &p); + } /* * We were able to read a complete * header and can finally enlist it. @@ -1497,7 +1651,21 @@ static jb_err parse_client_request(struct client_state *csp) if (csp->http->ssl == 0) { - csp->expected_client_content_length = get_expected_content_length(csp->headers); + /* + * This whole block belongs to chat() but currently + * has to be executed before sed(). + */ + if (csp->flags & CSP_FLAG_CHUNKED_CLIENT_BODY) + { + if (receive_chunked_client_request_body(csp) != JB_ERR_OK) + { + return JB_ERR_PARSE; + } + } + else + { + csp->expected_client_content_length = get_expected_content_length(csp->headers); + } verify_request_length(csp); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -1753,7 +1921,12 @@ static void chat(struct client_state *csp) csp->server_connection.requests_sent_total++; - if (fwd->forward_host || (http->ssl == 0)) + if ((fwd->type == SOCKS_5T) && (NULL == csp->headers->first)) + { + /* Client headers have been sent optimistically */ + assert(csp->headers->last == NULL); + } + else if (fwd->forward_host || (http->ssl == 0)) { int write_failure; hdr = list_to_text(csp->headers); @@ -1811,6 +1984,7 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport); + /* XXX: should the time start earlier for optimistically sent data? */ csp->server_connection.request_sent = time(NULL); maxfd = (csp->cfd > csp->server_connection.sfd) ? @@ -2668,7 +2842,7 @@ static void serve(struct client_state *csp) } } - if (continue_chatting && any_loaded_file_changed(csp->config->config_file_list)) + if (continue_chatting && any_loaded_file_changed(csp)) { continue_chatting = 0; config_file_change_detected = 1;