X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=filters.c;h=0450f5a7cc8c735699e22438b0ada91c3665c8a2;hb=ca3bfca1eadf00b68aad86a6fa1651f7d528ffc3;hp=290940d141e6d0e8e94775207d3f24fee8c7ede7;hpb=7759e9a72652fe7e10f8596212865d8b2cd22ad4;p=privoxy.git
diff --git a/filters.c b/filters.c
index 290940d1..0450f5a7 100644
--- a/filters.c
+++ b/filters.c
@@ -1,11 +1,11 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.180 2013/12/24 13:33:13 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.202 2016/05/25 10:50:55 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-2011 the
+ * Copyright : Written by and Copyright (C) 2001-2016 the
* Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
@@ -70,6 +70,9 @@ const char filters_rcs[] = "$Id: filters.c,v 1.180 2013/12/24 13:33:13 fabiankei
#include "deanimate.h"
#include "urlmatch.h"
#include "loaders.h"
+#ifdef FEATURE_CLIENT_TAGS
+#include "client-tags.h"
+#endif
#ifdef _WIN32
#include "win32.h"
@@ -79,8 +82,13 @@ const char filters_h_rcs[] = FILTERS_H_VERSION;
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);
static jb_err prepare_for_filtering(struct client_state *csp);
+static void apply_url_actions(struct current_action_spec *action,
+ struct http_request *http,
+#ifdef FEATURE_CLIENT_TAGS
+ const struct list *client_tags,
+#endif
+ struct url_actions *b);
#ifdef FEATURE_ACL
#ifdef HAVE_RFC2553
@@ -347,12 +355,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 +394,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 +425,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 +590,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 +607,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 +624,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 +644,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 +655,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 +681,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 +785,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 +819,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 +841,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);
@@ -951,6 +934,7 @@ pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const st
{"path", csp->http->path, 1},
{"host", csp->http->host, 1},
{"origin", csp->ip_addr_str, 1},
+ {"listen-address", csp->listen_addr_str, 1},
{NULL, NULL, 1}
};
@@ -1131,14 +1115,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 +1216,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 +1302,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 +1448,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);
@@ -1790,6 +1771,7 @@ static void set_privoxy_variables(const struct client_state *csp)
{ "PRIVOXY_PATH", csp->http->path },
{ "PRIVOXY_HOST", csp->http->host },
{ "PRIVOXY_ORIGIN", csp->ip_addr_str },
+ { "PRIVOXY_LISTEN_ADDRESS", csp->listen_addr_str },
};
for (i = 0; i < SZ(env); i++)
@@ -1873,6 +1855,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 +1886,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 +1901,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 +1912,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)
{
@@ -1971,7 +1956,11 @@ static char *execute_external_filter(const struct client_state *csp,
* or NULL in case something went wrong.
*
*********************************************************************/
+#ifdef FUZZ
+char *gif_deanimate_response(struct client_state *csp)
+#else
static char *gif_deanimate_response(struct client_state *csp)
+#endif
{
struct binbuffer *in, *out;
char *p;
@@ -1979,12 +1968,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))) )
- {
- log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)");
- return NULL;
- }
+ in = zalloc_or_die(sizeof(*in));
+ out = zalloc_or_die(sizeof(*out));
in->buffer = csp->iob->cur;
in->size = size;
@@ -2041,12 +2026,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 +2045,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
@@ -2073,11 +2058,22 @@ static filter_function_ptr get_filter_function(const struct client_state *csp)
* JB_ERR_PARSE otherwise
*
*********************************************************************/
+#ifdef FUZZ
+extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
+#else
static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
+#endif
{
size_t newsize = 0;
unsigned int chunksize = 0;
char *from_p, *to_p;
+ const char *end_of_buffer = buffer + *size;
+
+ if (*size == 0)
+ {
+ log_error(LOG_LEVEL_FATAL, "Invalid chunked input. Buffer is empty.");
+ return JB_ERR_PARSE;
+ }
assert(buffer);
from_p = to_p = buffer;
@@ -2090,27 +2086,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 +2283,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 +2298,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;
@@ -2306,13 +2346,16 @@ void get_url_actions(struct client_state *csp, struct http_request *http)
return;
}
+#ifdef FEATURE_CLIENT_TAGS
+ apply_url_actions(csp->action, http, csp->client_tags, b);
+#else
apply_url_actions(csp->action, http, b);
+#endif
}
return;
}
-
/*********************************************************************
*
* Function : apply_url_actions
@@ -2322,14 +2365,18 @@ void get_url_actions(struct client_state *csp, struct http_request *http)
* Parameters :
* 1 : action = Destination.
* 2 : http = Current URL
- * 3 : b = list of URL actions to apply
+ * 3 : client_tags = list of client tags
+ * 4 : b = list of URL actions to apply
*
* Returns : N/A
*
*********************************************************************/
-void apply_url_actions(struct current_action_spec *action,
- struct http_request *http,
- struct url_actions *b)
+static void apply_url_actions(struct current_action_spec *action,
+ struct http_request *http,
+#ifdef FEATURE_CLIENT_TAGS
+ const struct list *client_tags,
+#endif
+ struct url_actions *b)
{
if (b == NULL)
{
@@ -2343,6 +2390,12 @@ void apply_url_actions(struct current_action_spec *action,
{
merge_current_action(action, b->action);
}
+#ifdef FEATURE_CLIENT_TAGS
+ if (client_tag_match(b->url, client_tags))
+ {
+ merge_current_action(action, b->action);
+ }
+#endif
}
}
@@ -2373,7 +2426,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];
@@ -2404,14 +2457,7 @@ const static struct forward_spec *get_forward_override_settings(struct client_st
* the lifetime of this request. Save its location
* in csp as well, so sweep() can free it later on.
*/
- fwd = csp->fwd = zalloc(sizeof(*fwd));
- if (NULL == fwd)
- {
- log_error(LOG_LEVEL_FATAL,
- "can't allocate memory for forward-override{%s}", forward_override_line);
- /* Never get here - LOG_LEVEL_FATAL causes program exit */
- return NULL;
- }
+ fwd = csp->fwd = zalloc_or_die(sizeof(*fwd));
vec_count = ssplit(forward_settings, " \t", vec, SZ(vec));
if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
@@ -2421,6 +2467,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 +2630,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 +2702,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 +2713,7 @@ int content_requires_filtering(struct client_state *csp)
return TRUE;
}
- return (!list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]));
+ return FALSE;
}