X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=filters.c;h=480190a2c68ab9e1adccf41e3527b89dff849df1;hp=cfdde10d8675a4dcaee691bb07e50272b154ea56;hb=50f87c9446b435d47c371f63615260636639f450;hpb=94c203c16875c63002889472e48cea13217996f8 diff --git a/filters.c b/filters.c index cfdde10d..480190a2 100644 --- a/filters.c +++ b/filters.c @@ -1,19 +1,11 @@ -const char filters_rcs[] = "$Id: filters.c,v 1.146 2011/10/30 16:15:29 fabiankeil Exp $"; +const char filters_rcs[] = "$Id: filters.c,v 1.168 2012/03/09 16:23:50 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/filters.c,v $ * * Purpose : Declares functions to parse/crunch headers and pages. - * Functions declared include: - * `acl_addr', `add_stats', `block_acl', `block_imageurl', - * `block_url', `url_actions', `domain_split', - * `filter_popups', `forward_url', 'redirect_url', - * `ij_untrusted_url', `intercept_url', `pcrs_filter_respose', - * `ijb_send_banner', `trust_url', `gif_deanimate_response', - * `execute_single_pcrs_command', `rewrite_url', - * `get_last_url' - * - * Copyright : Written by and Copyright (C) 2001-2010 the + * + * Copyright : Written by and Copyright (C) 2001-2011 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -84,15 +76,6 @@ const char filters_rcs[] = "$Id: filters.c,v 1.146 2011/10/30 16:15:29 fabiankei const char filters_h_rcs[] = FILTERS_H_VERSION; -/* Fix a problem with Solaris. There should be no effect on other - * platforms. - * Solaris's isspace() is a macro which uses it's argument directly - * as an array index. Therefore we need to make sure that high-bit - * characters generate +ve values, and ideally we also want to make - * the argument match the declared parameter type of "int". - */ -#define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X)) - typedef char *(*filter_function_ptr)(); static filter_function_ptr get_filter_function(const struct client_state *csp); static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size); @@ -193,10 +176,8 @@ static int match_sockaddr(const struct sockaddr_storage *network, if (network->ss_family != netmask->ss_family) { /* This should never happen */ - log_error(LOG_LEVEL_ERROR, - "Internal error at %s:%llu: network and netmask differ in family", - __FILE__, __LINE__); - return 0; + assert(network->ss_family == netmask->ss_family); + log_error(LOG_LEVEL_FATAL, "Network and netmask differ in family."); } sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port); @@ -280,7 +261,7 @@ int block_acl(const struct access_control_addr *dst, const struct client_state * #else (csp->ip_addr_long & acl->src->mask) == acl->src->addr #endif - ) + ) { if (dst == NULL) { @@ -310,7 +291,7 @@ int block_acl(const struct access_control_addr *dst, const struct client_state * ((dst->addr & acl->dst->mask) == acl->dst->addr) && ((dst->port == acl->dst->port) || (acl->dst->port == 0)) #endif - ) + ) { if (acl->action == ACL_PERMIT) { @@ -379,7 +360,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca) if ((p = strchr(acl_spec, '/')) != NULL) { *p++ = '\0'; - if (ijb_isdigit(*p) == 0) + if (privoxy_isdigit(*p) == 0) { freez(acl_spec); return(-1); @@ -393,7 +374,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca) #else (masklength > 32) #endif - ) + ) { freez(acl_spec); return(-1); @@ -596,7 +577,7 @@ struct http_response *block_url(struct client_state *csp) /* determine HOW images should be blocked */ p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER]; - if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) + if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) { log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image."); } @@ -665,7 +646,7 @@ struct http_response *block_url(struct client_state *csp) } else #endif /* def FEATURE_IMAGE_BLOCKING */ - if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) + if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) { /* * Send empty document. @@ -1105,52 +1086,78 @@ char *get_last_url(char *subject, const char *redirect_mode) return NULL; } - if (0 == strcmpic(redirect_mode, "check-decoded-url")) + if (0 == strcmpic(redirect_mode, "check-decoded-url") && strchr(subject, '%')) { log_error(LOG_LEVEL_REDIRECTS, "Checking \"%s\" for encoded redirects.", subject); + /* * Check each parameter in the URL separately. * Sectionize the URL at "?" and "&", - * then URL-decode each component, + * go backwards through the segments, URL-decode them * and look for a URL in the decoded result. - * Keep the last one we spot. + * Stop the search after the first match. + */ + char *url_segment = NULL; + /* + * XXX: This estimate is guaranteed to be high enough as we + * let ssplit() ignore empty fields, but also a bit wasteful. */ - char *found = NULL; - char *s = strdup(subject); - char *token = strtok(s, "?&"); - while (token) + size_t max_segments = strlen(subject) / 2; + char **url_segments = malloc(max_segments * sizeof(char *)); + int segments; + + if (NULL == url_segments) { - char *dtoken = url_decode(token); - if (!dtoken) continue; - char *h1 = strstr(dtoken, "http://"); - char *h2 = strstr(dtoken, "https://"); - char *h = (h1 && h2 - ? (h1 < h2 ? h1 : h2) - : (h1 ? h1 : h2)); - if (h) + log_error(LOG_LEVEL_ERROR, "Out of memory while decoding URL: %s", new_url); + freez(subject); + return NULL; + } + + segments = ssplit(subject, "?&", url_segments, max_segments, 1, 1); + + while (segments-- > 0) + { + char *dtoken = url_decode(url_segments[segments]); + if (NULL == dtoken) + { + log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", url_segments[segments]); + continue; + } + url_segment = strstr(dtoken, "http://"); + if (NULL == url_segment) + { + url_segment = strstr(dtoken, "https://"); + } + if (NULL != url_segment) { - freez(found); - found = strdup(h); + url_segment = strdup(url_segment); + freez(dtoken); + if (url_segment == NULL) + { + log_error(LOG_LEVEL_ERROR, + "Out of memory while searching for redirects."); + return NULL; + } + break; } - token = strtok(NULL, "?&"); + freez(dtoken); } - freez(s); + freez(subject); + freez(url_segments); - if (found) + if (url_segment == NULL) { - freez(subject); - return found; + return NULL; } - - freez(subject); - return NULL; + subject = url_segment; + } + else + { + /* Look for a URL inside this one, without decoding anything. */ + log_error(LOG_LEVEL_REDIRECTS, + "Checking \"%s\" for unencoded redirects.", subject); } - - /* Else, just look for a URL inside this one, without decoding anything. */ - - log_error(LOG_LEVEL_REDIRECTS, - "Checking \"%s\" for unencoded redirects.", subject); /* * Find the last URL encoded in the request @@ -1167,10 +1174,10 @@ char *get_last_url(char *subject, const char *redirect_mode) } if ((new_url != NULL) - && ( (new_url != subject) + && ( (new_url != subject) || (0 == strncmpic(subject, "http://", 7)) || (0 == strncmpic(subject, "https://", 8)) - )) + )) { /* * Return new URL if we found a redirect @@ -1282,6 +1289,21 @@ struct http_response *redirect_url(struct client_state *csp) /* Did any redirect action trigger? */ if (new_url) { + if (url_requires_percent_encoding(new_url)) + { + char *encoded_url; + log_error(LOG_LEVEL_REDIRECTS, "Percent-encoding redirect URL: %N", + strlen(new_url), new_url); + encoded_url = percent_encode_url(new_url); + freez(new_url); + if (encoded_url == NULL) + { + return cgi_error_memory(); + } + new_url = encoded_url; + assert(FALSE == url_requires_percent_encoding(new_url)); + } + if (0 == strcmpic(new_url, csp->http->url)) { log_error(LOG_LEVEL_ERROR, @@ -1299,8 +1321,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"))) ) + if (enlist_unique_header(rsp->headers, "Location", new_url) + || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy")))) { freez(new_url); free_http_response(rsp); @@ -1450,7 +1472,7 @@ int is_untrusted_url(const struct client_state *csp) string_append(&new_entry, csp->http->hostport); path = csp->http->path; - if ( (path[0] == '/') + if ((path[0] == '/') && (path[1] == '~') && ((path_end = strchr(path + 2, '/')) != NULL)) { @@ -1697,8 +1719,8 @@ static char *gif_deanimate_response(struct client_state *csp) size = (size_t)(csp->iob->eod - csp->iob->cur); - if ( (NULL == (in = (struct binbuffer *)zalloc(sizeof *in ))) - || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) ) + if ( (NULL == (in = (struct binbuffer *)zalloc(sizeof *in))) + || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out)))) { log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)"); return NULL; @@ -1813,19 +1835,15 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size) return JB_ERR_PARSE; } - if ((newsize += chunksize) >= *size) + if (chunksize >= *size - newsize) { - /* - * XXX: The message is a bit confusing. Isn't the real problem that - * the specified chunk size is greater than the number of bytes - * left in the buffer? This probably means the connection got - * closed prematurely. To be investigated after 3.0.17 is out. - */ log_error(LOG_LEVEL_ERROR, - "Chunk size %d exceeds buffer size %d in \"chunked\" transfer coding", - chunksize, *size); + "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; } + newsize += chunksize; from_p += 2; memmove(to_p, from_p, (size_t) chunksize);