X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=filters.c;h=72719095dd9ced444bf293b310649c7a1e27b3dc;hp=54bd06dcd96a2464ec25c35a622bf01087755a69;hb=777f23fd554b0811d16bc2323ced6fe9dc0cff72;hpb=43980e5c0dbe1b2090fa04f69c3989960f1d8975 diff --git a/filters.c b/filters.c index 54bd06dc..72719095 100644 --- a/filters.c +++ b/filters.c @@ -1,11 +1,11 @@ -const char filters_rcs[] = "$Id: filters.c,v 1.181 2014/06/02 06:19:05 fabiankeil Exp $"; +const char filters_rcs[] = "$Id: filters.c,v 1.198 2016/01/16 12:30:43 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/filters.c,v $ * * Purpose : Declares functions to parse/crunch headers and pages. * - * Copyright : Written by and Copyright (C) 2001-2014 the + * Copyright : Written by and Copyright (C) 2001-2016 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -347,12 +347,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca) * Use a temporary acl spec copy so we can log * the unmodified original in case of parse errors. */ - acl_spec = strdup(aspec); - if (acl_spec == NULL) - { - /* XXX: This will be logged as parse error. */ - return(-1); - } + acl_spec = strdup_or_die(aspec); if ((p = strchr(acl_spec, '/')) != NULL) { @@ -391,13 +386,19 @@ int acl_addr(const char *aspec, struct access_control_addr *aca) { p = strchr(acl_spec, ':'); } + if (p != NULL) + { + assert(*p == ':'); + *p = '\0'; + p++; + } #ifdef HAVE_RFC2553 memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - i = getaddrinfo(acl_spec, ((p) ? ++p : NULL), &hints, &result); + i = getaddrinfo(acl_spec, p, &hints, &result); if (i != 0) { @@ -416,7 +417,6 @@ int acl_addr(const char *aspec, struct access_control_addr *aca) { char *endptr; - *p++ = '\0'; port = strtol(p, &endptr, 10); if (port <= 0 || port > 65535 || *endptr != '\0') @@ -582,12 +582,7 @@ struct http_response *block_url(struct client_state *csp) /* and handle accordingly: */ if ((p == NULL) || (0 == strcmpic(p, "pattern"))) { - rsp->status = strdup("403 Request blocked by Privoxy"); - if (rsp->status == NULL) - { - free_http_response(rsp); - return cgi_error_memory(); - } + rsp->status = strdup_or_die("403 Request blocked by Privoxy"); rsp->body = bindup(image_pattern_data, image_pattern_length); if (rsp->body == NULL) { @@ -604,12 +599,7 @@ struct http_response *block_url(struct client_state *csp) } else if (0 == strcmpic(p, "blank")) { - rsp->status = strdup("403 Request blocked by Privoxy"); - if (rsp->status == NULL) - { - free_http_response(rsp); - return cgi_error_memory(); - } + rsp->status = strdup_or_die("403 Request blocked by Privoxy"); rsp->body = bindup(image_blank_data, image_blank_length); if (rsp->body == NULL) { @@ -626,12 +616,7 @@ struct http_response *block_url(struct client_state *csp) } else { - rsp->status = strdup("302 Local Redirect from Privoxy"); - if (rsp->status == NULL) - { - free_http_response(rsp); - return cgi_error_memory(); - } + rsp->status = strdup_or_die("302 Local Redirect from Privoxy"); if (enlist_unique_header(rsp->headers, "Location", p)) { @@ -651,7 +636,7 @@ struct http_response *block_url(struct client_state *csp) new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE]; freez(rsp->body); - rsp->body = strdup(" "); + rsp->body = strdup_or_die(" "); rsp->content_length = 1; if (csp->config->feature_flags & RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK) @@ -662,18 +647,13 @@ struct http_response *block_url(struct client_state *csp) * Return a 200 OK status for pages blocked with +handle-as-empty-document * if the "handle-as-empty-doc-returns-ok" runtime config option is set. */ - rsp->status = strdup("200 Request blocked by Privoxy"); + rsp->status = strdup_or_die("200 Request blocked by Privoxy"); } else { - rsp->status = strdup("403 Request blocked by Privoxy"); + rsp->status = strdup_or_die("403 Request blocked by Privoxy"); } - if (rsp->status == NULL) - { - free_http_response(rsp); - return cgi_error_memory(); - } if (new_content_type != 0) { log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type); @@ -693,12 +673,7 @@ struct http_response *block_url(struct client_state *csp) jb_err err; struct map * exports; - rsp->status = strdup("403 Request blocked by Privoxy"); - if (rsp->status == NULL) - { - free_http_response(rsp); - return cgi_error_memory(); - } + rsp->status = strdup_or_die("403 Request blocked by Privoxy"); exports = default_exports(csp, NULL); if (exports == NULL) @@ -802,9 +777,9 @@ struct http_response *trust_url(struct client_state *csp) return cgi_error_memory(); } - rsp->status = strdup("403 Request blocked by Privoxy"); + rsp->status = strdup_or_die("403 Request blocked by Privoxy"); exports = default_exports(csp, NULL); - if (exports == NULL || rsp->status == NULL) + if (exports == NULL) { free_http_response(rsp); return cgi_error_memory(); @@ -836,7 +811,7 @@ struct http_response *trust_url(struct client_state *csp) /* * Export the trust list */ - p = strdup(""); + p = strdup_or_die(""); for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++) { snprintf(buf, sizeof(buf), "
  • %s
  • \n", t->spec); @@ -858,7 +833,7 @@ struct http_response *trust_url(struct client_state *csp) { struct list_entry *l; - p = strdup(""); + p = strdup_or_die(""); for (l = csp->config->trust_info->first; l ; l = l->next) { snprintf(buf, sizeof(buf), "
  • %s
    \n", l->str, l->str); @@ -1131,14 +1106,8 @@ char *get_last_url(char *subject, const char *redirect_mode) } if (NULL != url_segment) { - url_segment = strdup(url_segment); + url_segment = strdup_or_die(url_segment); freez(dtoken); - if (url_segment == NULL) - { - log_error(LOG_LEVEL_ERROR, - "Out of memory while searching for redirects."); - return NULL; - } break; } freez(dtoken); @@ -1238,11 +1207,14 @@ struct http_response *redirect_url(struct client_state *csp) * properly formatted URL and use it for the redirection * directly. * - * According to RFC 2616 section 14.30 the URL - * has to be absolute and if the user tries: - * +redirect{shit/this/will/be/parsed/as/pcrs_command.html} + * According to (the now obsolete) RFC 2616 section 14.30 + * the URL has to be absolute and if the user tries: + * +redirect{sadly/this/will/be/parsed/as/pcrs_command.html} * she would get undefined results anyway. * + * RFC 7231 7.1.2 actually allows relative references, + * but those start with a leading slash (RFC 3986 4.2) and + * thus can't be mistaken for pcrs commands either. */ if (*redirection_string == 's') @@ -1321,8 +1293,8 @@ struct http_response *redirect_url(struct client_state *csp) return cgi_error_memory(); } - if (enlist_unique_header(rsp->headers, "Location", new_url) - || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy")))) + rsp->status = strdup_or_die("302 Local Redirect from Privoxy"); + if (enlist_unique_header(rsp->headers, "Location", new_url)) { freez(new_url); free_http_response(rsp); @@ -1467,7 +1439,7 @@ int is_untrusted_url(const struct client_state *csp) { char * path; char * path_end; - char * new_entry = strdup("~"); + char * new_entry = strdup_or_die("~"); string_append(&new_entry, csp->http->hostport); @@ -1873,6 +1845,7 @@ static char *execute_external_filter(const struct client_state *csp, { log_error(LOG_LEVEL_ERROR, "fwrite(..., %d, 1, ..) failed: %E", *size); unlink(file_name); + fclose(fp); return NULL; } fclose(fp); @@ -1903,7 +1876,8 @@ static char *execute_external_filter(const struct client_state *csp, return NULL; } - filter_output = malloc_or_die(*size); + /* Allocate at least one byte */ + filter_output = malloc_or_die(*size + 1); new_size = 0; while (!feof(fp) && !ferror(fp)) @@ -1917,7 +1891,7 @@ static char *execute_external_filter(const struct client_state *csp, char *p; /* Could be considered wasteful if the content is 'large'. */ - *size = (*size != 0) ? *size * 2 : READ_LENGTH; + *size += (*size >= READ_LENGTH) ? *size : READ_LENGTH; p = realloc(filter_output, *size); if (p == NULL) @@ -1928,6 +1902,7 @@ static char *execute_external_filter(const struct client_state *csp, } filter_output = p; } + assert(new_size + READ_LENGTH < *size); len = fread(&filter_output[new_size], 1, READ_LENGTH, fp); if (len > 0) { @@ -2041,12 +2016,11 @@ static filter_function_ptr get_filter_function(const struct client_state *csp) * 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) && + else if ((csp->content_type & CT_GIF) && (csp->action->flags & ACTION_DEANIMATE)) { filter_function = gif_deanimate_response; @@ -2061,7 +2035,8 @@ static filter_function_ptr get_filter_function(const struct client_state *csp) * Function : remove_chunked_transfer_coding * * Description : In-situ remove the "chunked" transfer coding as defined - * in rfc2616 from a buffer. + * in RFC 7230 4.1 from a buffer. XXX: The implementation + * is neither complete nor compliant (TODO #129). * * Parameters : * 1 : buffer = Pointer to the text buffer @@ -2078,6 +2053,7 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size) size_t newsize = 0; unsigned int chunksize = 0; char *from_p, *to_p; + const char *end_of_buffer = buffer + *size; assert(buffer); from_p = to_p = buffer; @@ -2090,27 +2066,62 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size) while (chunksize > 0U) { + /* + * If the chunk-size is valid, we should have at least + * chunk-size bytes of chunk-data and five bytes of + * meta data (chunk-size, CRLF, CRLF) left in the buffer. + */ + if (chunksize + 5 >= *size - newsize) + { + log_error(LOG_LEVEL_ERROR, + "Chunk size %u exceeds buffered data left. " + "Already digested %u of %u buffered bytes.", + chunksize, (unsigned int)newsize, (unsigned int)*size); + return JB_ERR_PARSE; + } + + /* + * Skip the chunk-size, the optional chunk-ext and the CRLF + * that is supposed to be located directly before the start + * of chunk-data. + */ if (NULL == (from_p = strstr(from_p, "\r\n"))) { log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding"); return JB_ERR_PARSE; } + from_p += 2; - if (chunksize >= *size - newsize) + /* + * The previous strstr() does not enforce chunk-validity + * and is sattisfied as long a CRLF is left in the buffer. + * + * Make sure the bytes we consider chunk-data are within + * the valid range. + */ + if (from_p + chunksize >= end_of_buffer) { log_error(LOG_LEVEL_ERROR, - "Chunk size %u exceeds buffered data left. " - "Already digested %u of %u buffered bytes.", - chunksize, (unsigned int)newsize, (unsigned int)*size); + "End of chunk is beyond the end of the buffer."); return JB_ERR_PARSE; } - newsize += chunksize; - from_p += 2; memmove(to_p, from_p, (size_t) chunksize); + newsize += chunksize; to_p = buffer + newsize; - from_p += chunksize + 2; + from_p += chunksize; + /* + * Not merging this check with the previous one allows us + * to keep chunks without trailing CRLF. It's not clear + * if we actually have to care about those, though. + */ + if (from_p + 2 >= end_of_buffer) + { + log_error(LOG_LEVEL_ERROR, "Not enough room for trailing CRLF."); + return JB_ERR_PARSE; + } + from_p += 2; if (sscanf(from_p, "%x", &chunksize) != 1) { log_error(LOG_LEVEL_INFO, "Invalid \"chunked\" transfer encoding detected and ignored."); @@ -2252,7 +2263,8 @@ char *execute_content_filters(struct client_state *csp) content = (content_filter != NULL) ? (*content_filter)(csp) : NULL; #ifdef FEATURE_EXTERNAL_FILTERS - if (!list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])) + if ((csp->content_type & CT_TEXT) && + !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])) { struct list_entry *filtername; size_t size = (size_t)csp->content_length; @@ -2266,7 +2278,15 @@ char *execute_content_filters(struct client_state *csp) for (filtername = csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]->first; filtername ; filtername = filtername->next) { - content = execute_external_filter(csp, filtername->str, content, &size); + char *result = execute_external_filter(csp, filtername->str, content, &size); + if (result != NULL) + { + if (content != csp->iob->cur) + { + free(content); + } + content = result; + } } csp->flags |= CSP_FLAG_MODIFIED; csp->content_length = size; @@ -2373,7 +2393,7 @@ void apply_url_actions(struct current_action_spec *action, * Invalid syntax is fatal. * *********************************************************************/ -const static struct forward_spec *get_forward_override_settings(struct client_state *csp) +static const struct forward_spec *get_forward_override_settings(struct client_state *csp) { const char *forward_override_line = csp->action->string[ACTION_STRING_FORWARD_OVERRIDE]; char forward_settings[BUFFER_SIZE]; @@ -2421,6 +2441,14 @@ const static struct forward_spec *get_forward_override_settings(struct client_st /* Parse the parent HTTP proxy host:port */ http_parent = vec[1]; + } + else if ((vec_count == 2) && !strcasecmp(vec[0], "forward-webserver")) + { + fwd->type = FORWARD_WEBSERVER; + + /* Parse the parent HTTP server host:port */ + http_parent = vec[1]; + } else if (vec_count == 3) { @@ -2576,12 +2604,7 @@ struct http_response *direct_response(struct client_state *csp) return cgi_error_memory(); } - if (NULL == (rsp->status = strdup("501 Not Implemented"))) - { - free_http_response(rsp); - return cgi_error_memory(); - } - + rsp->status = strdup_or_die("501 Not Implemented"); rsp->is_static = 1; rsp->crunch_reason = UNSUPPORTED; @@ -2653,8 +2676,8 @@ int content_requires_filtering(struct client_state *csp) * the content type and action settings. */ if ((csp->content_type & CT_TEXT) && - (csp->rlist != NULL) && - (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]))) + (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]) || + !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]))) { return TRUE; } @@ -2664,7 +2687,7 @@ int content_requires_filtering(struct client_state *csp) return TRUE; } - return (!list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])); + return FALSE; }