X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=parsers.c;h=fc8f6c9e94f65c917f3c91c0caf0015bbbaa5452;hp=6f19139cfa7500e7814225b394704e32b0acedbc;hb=a63e355db41838804a61f8c39fe9227d4978d9a0;hpb=4741aaab8093cb425b21dcd3fa693f2e9db8234b diff --git a/parsers.c b/parsers.c index 6f19139c..fc8f6c9e 100644 --- a/parsers.c +++ b/parsers.c @@ -1,4 +1,4 @@ -const char parsers_rcs[] = "$Id: parsers.c,v 1.178 2009/06/11 14:13:19 david__schmidt Exp $"; +const char parsers_rcs[] = "$Id: parsers.c,v 1.224 2011/06/23 14:01:01 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/parsers.c,v $ @@ -152,10 +152,16 @@ static jb_err server_http (struct client_state *csp, char **header static jb_err crunch_server_header (struct client_state *csp, char **header); static jb_err server_last_modified (struct client_state *csp, char **header); static jb_err server_content_disposition(struct client_state *csp, char **header); +#ifdef FEATURE_ZLIB +static jb_err server_adjust_content_encoding(struct client_state *csp, char **header); +#endif #ifdef FEATURE_CONNECTION_KEEP_ALIVE static jb_err server_save_content_length(struct client_state *csp, char **header); static jb_err server_keep_alive(struct client_state *csp, char **header); +static jb_err server_proxy_connection(struct client_state *csp, char **header); +static jb_err client_keep_alive(struct client_state *csp, char **header); +static jb_err client_save_content_length(struct client_state *csp, char **header); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ static jb_err client_host_adder (struct client_state *csp); @@ -171,7 +177,6 @@ static jb_err create_forged_referrer(char **header, const char *hostport); static jb_err create_fake_referrer(char **header, const char *fake_referrer); static jb_err handle_conditional_hide_referrer_parameter(char **header, const char *host, const int parameter_conditional_block); -static const char *get_appropiate_connection_header(const struct client_state *csp); static void create_content_length_header(unsigned long long content_length, char *header, size_t buffer_length); @@ -201,7 +206,10 @@ static const struct parsers client_patterns[] = { { "TE:", 3, client_te }, { "Host:", 5, client_host }, { "if-modified-since:", 18, client_if_modified_since }, -#ifndef FEATURE_CONNECTION_KEEP_ALIVE +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + { "Keep-Alive:", 11, client_keep_alive }, + { "Content-Length:", 15, client_save_content_length }, +#else { "Keep-Alive:", 11, crumble }, #endif { "connection:", 11, client_connection }, @@ -228,6 +236,7 @@ static const struct parsers server_patterns[] = { #ifdef FEATURE_CONNECTION_KEEP_ALIVE { "Content-Length:", 15, server_save_content_length }, { "Keep-Alive:", 11, server_keep_alive }, + { "Proxy-Connection:", 17, server_proxy_connection }, #else { "Keep-Alive:", 11, crumble }, #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ @@ -312,7 +321,7 @@ long flush_socket(jb_socket fd, struct iob *iob) jb_err add_to_iob(struct client_state *csp, char *buf, long n) { struct iob *iob = csp->iob; - size_t used, offset, need, want; + size_t used, offset, need; char *p; if (n <= 0) return JB_ERR_OK; @@ -335,7 +344,12 @@ jb_err add_to_iob(struct client_state *csp, char *buf, long n) if (need > iob->size) { - for (want = csp->iob->size ? csp->iob->size : 512; want <= need;) want *= 2; + size_t want = csp->iob->size ? csp->iob->size : 512; + + while (want <= need) + { + want *= 2; + } if (want <= csp->config->buffer_limit && NULL != (p = (char *)realloc(iob->buf, want))) { @@ -582,7 +596,7 @@ jb_err decompress_iob(struct client_state *csp) assert(bufsize >= skip_size); memcpy(buf, csp->iob->buf, skip_size); - zstr.avail_out = bufsize - skip_size; + zstr.avail_out = (uInt)(bufsize - skip_size); zstr.next_out = (Bytef *)buf + skip_size; /* Try to decompress the whole stream in one shot. */ @@ -605,10 +619,12 @@ jb_err decompress_iob(struct client_state *csp) } /* - * If we tried the limit and still didn't have enough - * memory, just give up. + * If we reached the buffer limit and still didn't have enough + * memory, just give up. Due to the ceiling enforced by the next + * if block we could actually check for equality here, but as it + * can be easily mistaken for a bug we don't. */ - if (bufsize == csp->config->buffer_limit) + if (bufsize >= csp->config->buffer_limit) { log_error(LOG_LEVEL_ERROR, "Buffer limit reached while decompressing iob"); return JB_ERR_MEMORY; @@ -640,7 +656,7 @@ jb_err decompress_iob(struct client_state *csp) * buffer, which may be in a location different from * the old one. */ - zstr.avail_out += bufsize - oldbufsize; + zstr.avail_out += (uInt)(bufsize - oldbufsize); zstr.next_out = (Bytef *)tmpbuf + bufsize - zstr.avail_out; /* @@ -649,7 +665,6 @@ jb_err decompress_iob(struct client_state *csp) */ assert(zstr.avail_out == tmpbuf + bufsize - (char *)zstr.next_out); assert((char *)zstr.next_out == tmpbuf + ((char *)oldnext_out - buf)); - assert(zstr.avail_out > 0U); buf = tmpbuf; } @@ -1127,7 +1142,7 @@ jb_err update_server_headers(struct client_state *csp) { "Content-Length:", 15, server_adjust_content_length }, { "Transfer-Encoding:", 18, server_transfer_coding }, #ifdef FEATURE_ZLIB - { "Content-Encoding:", 17, server_content_encoding }, + { "Content-Encoding:", 17, server_adjust_content_encoding }, #endif /* def FEATURE_ZLIB */ { NULL, 0, NULL } }; @@ -1172,6 +1187,18 @@ jb_err update_server_headers(struct client_state *csp) } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ +#ifdef FEATURE_COMPRESSION + if ((JB_ERR_OK == err) + && (csp->flags & CSP_FLAG_BUFFERED_CONTENT_DEFLATED)) + { + err = enlist_unique_header(csp->headers, "Content-Encoding", "deflate"); + if (JB_ERR_OK == err) + { + log_error(LOG_LEVEL_HEADER, "Added header: Content-Encoding: deflate"); + } + } +#endif + return err; } @@ -1204,7 +1231,6 @@ static jb_err header_tagger(struct client_state *csp, char *header) struct re_filterfile_spec *b; struct list_entry *tag_name; - int found_filters = 0; const size_t header_length = strlen(header); if (csp->flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE) @@ -1218,21 +1244,7 @@ static jb_err header_tagger(struct client_state *csp, char *header) multi_action_index = ACTION_MULTI_CLIENT_HEADER_TAGGER; } - /* Check if there are any filters */ - for (i = 0; i < MAX_AF_FILES; i++) - { - fl = csp->rlist[i]; - if (NULL != fl) - { - if (NULL != fl->f) - { - found_filters = 1; - break; - } - } - } - - if (0 == found_filters) + if (filters_available(csp) == FALSE) { log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: " "tagging enabled, but no taggers available."); @@ -1415,7 +1427,7 @@ static jb_err filter_header(struct client_state *csp, char **header) struct re_filterfile_spec *b; struct list_entry *filtername; - int i, found_filters = 0; + int i; int wanted_filter_type; int multi_action_index; @@ -1435,23 +1447,7 @@ static jb_err filter_header(struct client_state *csp, char **header) multi_action_index = ACTION_MULTI_CLIENT_HEADER_FILTER; } - /* - * Need to check the set of re_filterfiles... - */ - for (i = 0; i < MAX_AF_FILES; i++) - { - fl = csp->rlist[i]; - if (NULL != fl) - { - if (NULL != fl->f) - { - found_filters = 1; - break; - } - } - } - - if (0 == found_filters) + if (filters_available(csp) == FALSE) { log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: " "header filtering enabled, but no matching filters available."); @@ -1580,7 +1576,11 @@ static jb_err filter_header(struct client_state *csp, char **header) *********************************************************************/ static jb_err server_connection(struct client_state *csp, char **header) { - if (!strcmpic(*header, "Connection: keep-alive")) + if (!strcmpic(*header, "Connection: keep-alive") +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) +#endif + ) { #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)) @@ -1620,7 +1620,7 @@ static jb_err server_connection(struct client_state *csp, char **header) * * Function : server_keep_alive * - * Description : Stores the servers keep alive timeout. + * Description : Stores the server's keep alive timeout. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -1658,6 +1658,157 @@ static jb_err server_keep_alive(struct client_state *csp, char **header) "Server keep-alive timeout is %u. Sticking with %u.", keep_alive_timeout, csp->server_connection.keep_alive_timeout); } + csp->flags |= CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : server_proxy_connection + * + * Description : Figures out whether or not we should add a + * Proxy-Connection header. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK. + * + *********************************************************************/ +static jb_err server_proxy_connection(struct client_state *csp, char **header) +{ + csp->flags |= CSP_FLAG_SERVER_PROXY_CONNECTION_HEADER_SET; + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_keep_alive + * + * Description : Stores the client's keep alive timeout. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK. + * + *********************************************************************/ +static jb_err client_keep_alive(struct client_state *csp, char **header) +{ + unsigned int keep_alive_timeout; + const char *timeout_position = strstr(*header, ": "); + + if (!(csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)) + { + log_error(LOG_LEVEL_HEADER, + "keep-alive support is disabled. Crunching: %s.", *header); + freez(*header); + return JB_ERR_OK; + } + + if ((NULL == timeout_position) + || (1 != sscanf(timeout_position, ": %u", &keep_alive_timeout))) + { + log_error(LOG_LEVEL_ERROR, "Couldn't parse: %s", *header); + } + else + { + if (keep_alive_timeout < csp->config->keep_alive_timeout) + { + log_error(LOG_LEVEL_HEADER, + "Reducing keep-alive timeout from %u to %u.", + csp->config->keep_alive_timeout, keep_alive_timeout); + csp->server_connection.keep_alive_timeout = keep_alive_timeout; + } + else + { + /* XXX: Is this log worthy? */ + log_error(LOG_LEVEL_HEADER, + "Client keep-alive timeout is %u. Sticking with %u.", + keep_alive_timeout, csp->config->keep_alive_timeout); + } + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : get_content_length + * + * Description : Gets the content length specified in a + * Content-Length header. + * + * Parameters : + * 1 : header = The Content-Length header. + * 2 : length = Storage to return the value. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_PARSE if no value is recognized. + * + *********************************************************************/ +static jb_err get_content_length(const char *header, unsigned long long *length) +{ + assert(header[14] == ':'); + +#ifdef _WIN32 + assert(sizeof(unsigned long long) > 4); + if (1 != sscanf(header+14, ": %I64u", length)) +#else + if (1 != sscanf(header+14, ": %llu", length)) +#endif + { + return JB_ERR_PARSE; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_save_content_length + * + * Description : Save the Content-Length sent by the client. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +static jb_err client_save_content_length(struct client_state *csp, char **header) +{ + unsigned long long content_length = 0; + + assert(*(*header+14) == ':'); + + if (JB_ERR_OK != get_content_length(*header, &content_length)) + { + log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header); + freez(*header); + } + else + { + csp->expected_client_content_length = content_length; } return JB_ERR_OK; @@ -1687,43 +1838,64 @@ static jb_err server_keep_alive(struct client_state *csp, char **header) *********************************************************************/ static jb_err client_connection(struct client_state *csp, char **header) { - const char *wanted_header = get_appropiate_connection_header(csp); + static const char connection_close[] = "Connection: close"; - if (strcmpic(*header, wanted_header)) + if (!strcmpic(*header, connection_close)) { #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if (!(csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)) + if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)) { - log_error(LOG_LEVEL_HEADER, - "Keeping the client header '%s' around. " - "The connection will not be kept alive.", - *header); + if (!strcmpic(csp->http->ver, "HTTP/1.1")) + { + log_error(LOG_LEVEL_HEADER, + "Removing \'%s\' to imply keep-alive.", *header); + freez(*header); + } + else + { + char *old_header = *header; + + *header = strdup("Connection: keep-alive"); + if (header == NULL) + { + return JB_ERR_MEMORY; + } + log_error(LOG_LEVEL_HEADER, + "Replaced: \'%s\' with \'%s\'", old_header, *header); + freez(old_header); + } } else -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ { - char *old_header = *header; - - *header = strdup(wanted_header); - if (header == NULL) - { - return JB_ERR_MEMORY; - } log_error(LOG_LEVEL_HEADER, - "Replaced: \'%s\' with \'%s\'", old_header, *header); - freez(old_header); + "Keeping the client header '%s' around. " + "The connection will not be kept alive.", + *header); + csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; } } -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - else + else if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)) { log_error(LOG_LEVEL_HEADER, "Keeping the client header '%s' around. " "The server connection will be kept alive if possible.", *header); csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; - } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + } + else + { + char *old_header = *header; + + *header = strdup(connection_close); + if (header == NULL) + { + return JB_ERR_MEMORY; + } + log_error(LOG_LEVEL_HEADER, + "Replaced: \'%s\' with \'%s\'", old_header, *header); + freez(old_header); + } /* Signal client_connection_adder() to return early. */ csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET; @@ -1801,7 +1973,7 @@ static jb_err crunch_server_header(struct client_state *csp, char **header) * Function : server_content_type * * Description : Set the content-type for filterable types (text/.*, - * .*xml.*, javascript and image/gif) unless filtering has been + * .*xml.*, .*script.* and image/gif) unless filtering has been * forbidden (CT_TABOO) while parsing earlier headers. * NOTE: Since text/plain is commonly used by web servers * for files whose correct type is unknown, we don't @@ -1851,7 +2023,7 @@ static jb_err server_content_type(struct client_state *csp, char **header) */ if ((strstr(*header, "text/") && !strstr(*header, "plain")) || strstr(*header, "xml") - || strstr(*header, "application/x-javascript")) + || strstr(*header, "script")) { csp->content_type |= CT_TEXT; } @@ -1867,7 +2039,7 @@ static jb_err server_content_type(struct client_state *csp, char **header) if (csp->action->flags & ACTION_CONTENT_TYPE_OVERWRITE) { /* - * Make sure the user doesn't accidently + * Make sure the user doesn't accidentally * change the content type of binary documents. */ if ((csp->content_type & CT_TEXT) || (csp->action->flags & ACTION_FORCE_TEXT_MODE)) @@ -1961,16 +2133,16 @@ static jb_err server_transfer_coding(struct client_state *csp, char **header) * * Function : server_content_encoding * - * Description : This function is run twice for each request, - * unless FEATURE_ZLIB and filtering are disabled. + * Description : Used to check if the content is compressed, and if + * FEATURE_ZLIB is disabled, filtering is disabled as + * well. * - * The first run is used to check if the content - * is compressed, if FEATURE_ZLIB is disabled - * filtering is then disabled as well, if FEATURE_ZLIB - * is enabled the content is marked for decompression. + * If FEATURE_ZLIB is enabled and the compression type + * supported, the content is marked for decompression. * - * The second run is used to remove the Content-Encoding - * header if the decompression was successful. + * XXX: Doesn't properly deal with multiple or with + * unsupported but unknown encodings. + * Is case-sensitive but shouldn't be. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -1986,19 +2158,25 @@ static jb_err server_transfer_coding(struct client_state *csp, char **header) static jb_err server_content_encoding(struct client_state *csp, char **header) { #ifdef FEATURE_ZLIB - if ((csp->flags & CSP_FLAG_MODIFIED) - && (csp->content_type & (CT_GZIP | CT_DEFLATE))) + if (strstr(*header, "sdch")) { /* - * We successfully decompressed the content, - * and have to clean the header now, so the - * client no longer expects compressed data.. - * - * XXX: There is a difference between cleaning - * and removing it completely. + * Shared Dictionary Compression over HTTP isn't supported, + * filtering it anyway is pretty much guaranteed to mess up + * the encoding. */ - log_error(LOG_LEVEL_HEADER, "Crunching: %s", *header); - freez(*header); + csp->content_type |= CT_TABOO; + + /* + * Log a warning if the user expects the content to be filtered. + */ + if ((csp->rlist != NULL) && + (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]))) + { + log_error(LOG_LEVEL_INFO, + "SDCH-compressed content detected, content filtering disabled. " + "Consider suppressing SDCH offers made by the client."); + } } else if (strstr(*header, "gzip")) { @@ -2019,7 +2197,16 @@ static jb_err server_content_encoding(struct client_state *csp, char **header) csp->content_type |= CT_TABOO; } #else /* !defined(FEATURE_ZLIB) */ - if (strstr(*header, "gzip") || strstr(*header, "compress") || strstr(*header, "deflate")) + /* + * XXX: Using a black list here isn't the right approach. + * + * In case of SDCH, building with zlib support isn't + * going to help. + */ + if (strstr(*header, "gzip") || + strstr(*header, "compress") || + strstr(*header, "deflate") || + strstr(*header, "sdch")) { /* * Body is compressed, turn off pcrs and gif filtering. @@ -2045,6 +2232,49 @@ static jb_err server_content_encoding(struct client_state *csp, char **header) } +#ifdef FEATURE_ZLIB +/********************************************************************* + * + * Function : server_adjust_content_encoding + * + * Description : Remove the Content-Encoding header if the + * decompression was successful and the content + * has been modifed. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +static jb_err server_adjust_content_encoding(struct client_state *csp, char **header) +{ + if ((csp->flags & CSP_FLAG_MODIFIED) + && (csp->content_type & (CT_GZIP | CT_DEFLATE))) + { + /* + * We successfully decompressed the content, + * and have to clean the header now, so the + * client no longer expects compressed data.. + * + * XXX: There is a difference between cleaning + * and removing it completely. + */ + log_error(LOG_LEVEL_HEADER, "Crunching: %s", *header); + freez(*header); + } + + return JB_ERR_OK; + +} +#endif /* defined(FEATURE_ZLIB) */ + + /********************************************************************* * * Function : server_adjust_content_length @@ -2108,7 +2338,7 @@ static jb_err server_save_content_length(struct client_state *csp, char **header assert(*(*header+14) == ':'); - if (1 != sscanf(*header+14, ": %llu", &content_length)) + if (JB_ERR_OK != get_content_length(*header, &content_length)) { log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header); freez(*header); @@ -2239,15 +2469,9 @@ static jb_err server_last_modified(struct client_state *csp, char **header) { const char *newval; char buf[BUFFER_SIZE]; - + time_t last_modified; char newheader[50]; -#ifdef HAVE_GMTIME_R - struct tm gmt; -#endif - struct tm *timeptr = NULL; - time_t now, last_modified; - long int days, hours, minutes, seconds; - + /* * Are we messing with the Last-Modified header? */ @@ -2292,16 +2516,7 @@ static jb_err server_last_modified(struct client_state *csp, char **header) const char *header_time = *header + sizeof("Last-Modified:"); log_error(LOG_LEVEL_HEADER, "Randomizing: %s", *header); - now = time(NULL); -#ifdef HAVE_GMTIME_R - gmtime_r(&now, &gmt); -#elif defined(MUTEX_LOCKS_AVAILABLE) - privoxy_mutex_lock(&gmtime_mutex); - gmtime(&now); - privoxy_mutex_unlock(&gmtime_mutex); -#else - gmtime(&now); -#endif + if (JB_ERR_OK != parse_header_time(header_time, &last_modified)) { log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s in %s (crunching!)", header_time, *header); @@ -2309,9 +2524,17 @@ static jb_err server_last_modified(struct client_state *csp, char **header) } else { - long int rtime = (long int)difftime(now, last_modified); + time_t now; + struct tm *timeptr = NULL; + long int rtime; +#ifdef HAVE_GMTIME_R + struct tm gmt; +#endif + now = time(NULL); + rtime = (long int)difftime(now, last_modified); if (rtime) { + long int days, hours, minutes, seconds; const int negative_delta = (rtime < 0); if (negative_delta) @@ -2334,7 +2557,16 @@ static jb_err server_last_modified(struct client_state *csp, char **header) #else timeptr = gmtime(&last_modified); #endif - strftime(newheader, sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr); + if ((NULL == timeptr) || !strftime(newheader, + sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr)) + { + log_error(LOG_LEVEL_ERROR, + "Randomizing '%s' failed. Crunching the header without replacement.", + *header); + freez(*header); + return JB_ERR_OK; + } + freez(*header); *header = strdup("Last-Modified: "); string_append(header, newheader); @@ -2387,25 +2619,17 @@ static jb_err server_last_modified(struct client_state *csp, char **header) *********************************************************************/ static jb_err client_accept_encoding(struct client_state *csp, char **header) { +#ifdef FEATURE_COMPRESSION + if ((csp->config->feature_flags & RUNTIME_FEATURE_COMPRESSION) + && strstr(*header, "deflate")) + { + csp->flags |= CSP_FLAG_CLIENT_SUPPORTS_DEFLATE; + } +#endif if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0) { log_error(LOG_LEVEL_HEADER, "Suppressed offer to compress content"); - freez(*header); - - /* Temporarily disable the correct behaviour to - * work around a PHP bug. - * - * if (!strcmpic(csp->http->ver, "HTTP/1.1")) - * { - * *header = strdup("Accept-Encoding: identity;q=1.0, *;q=0"); - * if (*header == NULL) - * { - * return JB_ERR_MEMORY; - * } - * } - * - */ } return JB_ERR_OK; @@ -2989,7 +3213,6 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header) struct tm *timeptr = NULL; time_t tm = 0; const char *newval; - long int hours, minutes, seconds; char * endptr; if ( 0 == strcmpic(*header, "If-Modified-Since: Wed, 08 Jun 1955 12:00:00 GMT")) @@ -3024,6 +3247,7 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header) } else { + long int hours, minutes, seconds; long int rtime = strtol(newval, &endptr, 0); const int negative_range = (rtime < 0); @@ -3053,11 +3277,13 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header) #else timeptr = gmtime(&tm); #endif - if (!strftime(newheader, sizeof(newheader), - "%a, %d %b %Y %H:%M:%S GMT", timeptr)) + if ((NULL == timeptr) || !strftime(newheader, + sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr)) { log_error(LOG_LEVEL_ERROR, - "Randomizing %s failed. Keeping the header unmodified."); + "Randomizing '%s' failed. Crunching the header without replacement.", + *header); + freez(*header); return JB_ERR_OK; } @@ -3364,7 +3590,7 @@ static jb_err client_x_forwarded_for_adder(struct client_state *csp) * * Function : server_connection_adder * - * Description : Adds an appropiate "Connection:" header to csp->headers + * Description : Adds an appropriate "Connection:" header to csp->headers * unless the header was already present. Called from `sed'. * * Parameters : @@ -3378,7 +3604,7 @@ static jb_err server_connection_adder(struct client_state *csp) { const unsigned int flags = csp->flags; const char *response_status_line = csp->headers->first->str; - const char *wanted_header = get_appropiate_connection_header(csp); + static const char connection_close[] = "Connection: close"; if ((flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE) && (flags & CSP_FLAG_SERVER_CONNECTION_HEADER_SET)) @@ -3392,16 +3618,21 @@ static jb_err server_connection_adder(struct client_state *csp) if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) && (NULL != response_status_line) - && !strncmpic(response_status_line, "HTTP/1.1", 8)) + && !strncmpic(response_status_line, "HTTP/1.1", 8) +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) +#endif + ) { log_error(LOG_LEVEL_HEADER, "A HTTP/1.1 response " "without Connection header implies keep-alive."); csp->flags |= CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE; + return JB_ERR_OK; } - log_error(LOG_LEVEL_HEADER, "Adding: %s", wanted_header); + log_error(LOG_LEVEL_HEADER, "Adding: %s", connection_close); - return enlist(csp->headers, wanted_header); + return enlist(csp->headers, connection_close); } @@ -3412,7 +3643,7 @@ static jb_err server_connection_adder(struct client_state *csp) * * Description : Adds a "Proxy-Connection: keep-alive" header to * csp->headers if the client asked for keep-alive. - * XXX: We should reuse existant ones. + * XXX: We should reuse existent ones. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -3426,7 +3657,9 @@ static jb_err server_proxy_connection_adder(struct client_state *csp) static const char proxy_connection_header[] = "Proxy-Connection: keep-alive"; jb_err err = JB_ERR_OK; - if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) + if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) + && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED) + && !(csp->flags & CSP_FLAG_SERVER_PROXY_CONNECTION_HEADER_SET)) { log_error(LOG_LEVEL_HEADER, "Adding: %s", proxy_connection_header); err = enlist(csp->headers, proxy_connection_header); @@ -3453,7 +3686,7 @@ static jb_err server_proxy_connection_adder(struct client_state *csp) *********************************************************************/ static jb_err client_connection_header_adder(struct client_state *csp) { - const char *wanted_header = get_appropiate_connection_header(csp); + static const char connection_close[] = "Connection: close"; if (!(csp->flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE) && (csp->flags & CSP_FLAG_CLIENT_CONNECTION_HEADER_SET)) @@ -3463,15 +3696,17 @@ static jb_err client_connection_header_adder(struct client_state *csp) #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) - && (csp->http->ssl == 0)) + && (csp->http->ssl == 0) + && !strcmpic(csp->http->ver, "HTTP/1.1")) { csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + return JB_ERR_OK; } #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ - log_error(LOG_LEVEL_HEADER, "Adding: %s", wanted_header); + log_error(LOG_LEVEL_HEADER, "Adding: %s", connection_close); - return enlist(csp->headers, wanted_header); + return enlist(csp->headers, connection_close); } @@ -3532,8 +3767,7 @@ static jb_err server_http(struct client_state *csp, char **header) * Function : server_set_cookie * * Description : Handle the server "cookie" header properly. - * Log cookie to the jar file. Then "crunch", - * accept or rewrite it to a session cookie. + * Crunch, accept or rewrite it to a session cookie. * Called from `sed'. * * TODO: Allow the user to specify a new expiration @@ -3611,6 +3845,17 @@ static jb_err server_set_cookie(struct client_state *csp, char **header) { char *expiration_date = cur_tag + 8; /* Skip "[Ee]xpires=" */ + if ((expiration_date[0] == '"') + && (expiration_date[1] != '\0')) + { + /* + * Skip quotation mark. RFC 2109 10.1.2 seems to hint + * that the expiration date isn't supposed to be quoted, + * but some servers do it anyway. + */ + expiration_date++; + } + /* Did we detect the date properly? */ if (JB_ERR_OK != parse_header_time(expiration_date, &cookie_time)) { @@ -3710,7 +3955,7 @@ static jb_err server_set_cookie(struct client_state *csp, char **header) * * Function : strclean * - * Description : In-Situ-Eliminate all occurances of substring in + * Description : In-Situ-Eliminate all occurrences of substring in * string * * Parameters : @@ -3830,7 +4075,8 @@ jb_err get_destination_from_headers(const struct list *headers, struct http_requ return JB_ERR_PARSE; } - if (NULL == (p = strdup((host)))) + p = strdup(host); + if (NULL == p) { log_error(LOG_LEVEL_ERROR, "Out of memory while parsing \"Host:\" header"); return JB_ERR_MEMORY; @@ -3974,7 +4220,7 @@ static jb_err handle_conditional_hide_referrer_parameter(char **header, const char *host, const int parameter_conditional_block) { char *referer = strdup(*header); - const size_t hostlenght = strlen(host); + const size_t hostlength = strlen(host); const char *referer_url = NULL; if (NULL == referer) @@ -3984,14 +4230,14 @@ static jb_err handle_conditional_hide_referrer_parameter(char **header, } /* referer begins with 'Referer: http[s]://' */ - if ((hostlenght+17) < strlen(referer)) + if ((hostlength+17) < strlen(referer)) { /* * Shorten referer to make sure the referer is blocked * if www.example.org/www.example.com-shall-see-the-referer/ * links to www.example.com/ */ - referer[hostlenght+17] = '\0'; + referer[hostlength+17] = '\0'; } referer_url = strstr(referer, "http://"); if ((NULL == referer_url) || (NULL == strstr(referer_url, host))) @@ -4016,34 +4262,6 @@ static jb_err handle_conditional_hide_referrer_parameter(char **header, } -/********************************************************************* - * - * Function : get_appropiate_connection_header - * - * Description : Returns an appropiate Connection header - * depending on whether or not we try to keep - * the connection to the server alive. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * - * Returns : Pointer to statically allocated header buffer. - * - *********************************************************************/ -static const char *get_appropiate_connection_header(const struct client_state *csp) -{ - static const char connection_keep_alive[] = "Connection: keep-alive"; - static const char connection_close[] = "Connection: close"; - - if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE) - && (csp->http->ssl == 0)) - { - return connection_keep_alive; - } - return connection_close; -} - - /********************************************************************* * * Function : create_content_length_header