X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=filters.c;h=6dd679eee3159cc9c337ac50a2beaba61d8b3555;hb=f9888d9c1d0b5b4e082718508532ddaf2ba8759c;hp=7eb673340781f392d2fdf0b1d5bd90c182faf727;hpb=93953651c25a05476b1393493ef2b86ac73a21d1;p=privoxy.git
diff --git a/filters.c b/filters.c
index 7eb67334..6dd679ee 100644
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.62 2006/08/14 00:27:47 david__schmidt Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.84 2007/03/20 15:16:34 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/filters.c,v $
@@ -10,9 +10,10 @@ const char filters_rcs[] = "$Id: filters.c,v 1.62 2006/08/14 00:27:47 david__sch
* `filter_popups', `forward_url', 'redirect_url',
* `ij_untrusted_url', `intercept_url', `pcrs_filter_respose',
* `ijb_send_banner', `trust_url', `gif_deanimate_response',
- * `jpeg_inspect_response'
+ * `jpeg_inspect_response', `execute_single_pcrs_command',
+ * `rewrite_url', `get_last_url'
*
- * Copyright : Written by and Copyright (C) 2001, 2004 the SourceForge
+ * Copyright : Written by and Copyright (C) 2001, 2004-2007 the SourceForge
* Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
@@ -39,6 +40,99 @@ const char filters_rcs[] = "$Id: filters.c,v 1.62 2006/08/14 00:27:47 david__sch
*
* Revisions :
* $Log: filters.c,v $
+ * Revision 1.84 2007/03/20 15:16:34 fabiankeil
+ * Use dedicated header filter actions instead of abusing "filter".
+ * Replace "filter-client-headers" and "filter-client-headers"
+ * with "server-header-filter" and "client-header-filter".
+ *
+ * Revision 1.83 2007/03/17 15:20:05 fabiankeil
+ * New config option: enforce-blocks.
+ *
+ * Revision 1.82 2007/03/13 11:28:43 fabiankeil
+ * - Fix port handling in acl_addr() and use a temporary acl spec
+ * copy so error messages don't contain a truncated version.
+ * - Log size of iob before and after decompression.
+ *
+ * Revision 1.81 2007/03/05 14:40:53 fabiankeil
+ * - Cosmetical changes for LOG_LEVEL_RE_FILTER messages.
+ * - Hide the "Go there anyway" link for blocked CONNECT
+ * requests where going there anyway doesn't work anyway.
+ *
+ * Revision 1.80 2007/02/07 10:55:20 fabiankeil
+ * - Save the reason for generating http_responses.
+ * - Block (+block) with status code 403 instead of 404.
+ * - Use a different kludge to remember a failed decompression.
+ *
+ * Revision 1.79 2007/01/31 16:21:38 fabiankeil
+ * Search for Max-Forwards headers case-insensitive,
+ * don't generate the "501 unsupported" message for invalid
+ * Max-Forwards values and don't increase negative ones.
+ *
+ * Revision 1.78 2007/01/28 13:41:18 fabiankeil
+ * - Add HEAD support to finish_http_response.
+ * - Add error favicon to internal HTML error messages.
+ *
+ * Revision 1.77 2007/01/12 15:36:44 fabiankeil
+ * Mark *csp as immutable for is_untrusted_url()
+ * and is_imageurl(). Closes FR 1237736.
+ *
+ * Revision 1.76 2007/01/01 19:36:37 fabiankeil
+ * Integrate a modified version of Wil Mahan's
+ * zlib patch (PR #895531).
+ *
+ * Revision 1.75 2006/12/29 18:30:46 fabiankeil
+ * Fixed gcc43 conversion warnings,
+ * changed sprintf calls to snprintf.
+ *
+ * Revision 1.74 2006/12/24 17:37:38 fabiankeil
+ * Adjust comment in pcrs_filter_response()
+ * to recent pcrs changes. Hohoho.
+ *
+ * Revision 1.73 2006/12/23 16:01:02 fabiankeil
+ * Don't crash if pcre returns an error code
+ * that pcrs didn't expect. Fixes BR 1621173.
+ *
+ * Revision 1.72 2006/12/22 18:52:53 fabiankeil
+ * Modified is_untrusted_url to complain in case of
+ * write errors and to give a reason when adding new
+ * entries to the trustfile. Closes FR 1097611.
+ *
+ * Revision 1.71 2006/12/22 14:24:52 fabiankeil
+ * Skip empty filter files in pcrs_filter_response,
+ * but don't ignore the ones that come afterwards.
+ * Fixes parts of BR 1619208.
+ *
+ * Revision 1.70 2006/12/09 13:33:15 fabiankeil
+ * Added some sanity checks for get_last_url().
+ * Fixed possible segfault caused by my last commit.
+ *
+ * Revision 1.69 2006/12/08 12:39:13 fabiankeil
+ * Let get_last_url() catch https URLs as well.
+ *
+ * Revision 1.68 2006/12/05 14:45:48 fabiankeil
+ * Make sure get_last_url() behaves like advertised
+ * and fast-redirects{} can be combined with redirect{}.
+ *
+ * Revision 1.67 2006/11/28 15:19:43 fabiankeil
+ * Implemented +redirect{s@foo@bar@} to generate
+ * a redirect based on a rewritten version of the
+ * original URL.
+ *
+ * Revision 1.66 2006/09/23 13:26:38 roro
+ * Replace TABs by spaces in source code.
+ *
+ * Revision 1.65 2006/09/21 12:54:43 fabiankeil
+ * Fix +redirect{}. Didn't work with -fast-redirects.
+ *
+ * Revision 1.64 2006/08/31 10:55:49 fabiankeil
+ * Block requests for untrusted URLs with status
+ * code 403 instead of 200.
+ *
+ * Revision 1.63 2006/08/31 10:11:28 fabiankeil
+ * Don't free p which is still in use and will be later
+ * freed by free_map(). Don't claim the referrer is unknown
+ * when the client didn't set one.
+ *
* Revision 1.62 2006/08/14 00:27:47 david__schmidt
* Feature request 595948: Re-Filter logging in single line
*
@@ -574,20 +668,33 @@ int block_acl(struct access_control_addr *dst, struct client_state *csp)
* Returns : 0 => Ok, everything else is an error.
*
*********************************************************************/
-int acl_addr(char *aspec, struct access_control_addr *aca)
+int acl_addr(const char *aspec, struct access_control_addr *aca)
{
- int i, masklength, port;
+ int i, masklength;
+ long port;
char *p;
+ char *acl_spec = NULL;
masklength = 32;
port = 0;
- if ((p = strchr(aspec, '/')) != NULL)
+ /*
+ * 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)
{
- *p++ = '\0';
+ /* XXX: This will be logged as parse error. */
+ return(-1);
+ }
+ if ((p = strchr(acl_spec, '/')) != NULL)
+ {
+ *p++ = '\0';
if (ijb_isdigit(*p) == 0)
{
+ free(acl_spec);
return(-1);
}
masklength = atoi(p);
@@ -595,26 +702,32 @@ int acl_addr(char *aspec, struct access_control_addr *aca)
if ((masklength < 0) || (masklength > 32))
{
+ free(acl_spec);
return(-1);
}
- if ((p = strchr(aspec, ':')) != NULL)
+ if ((p = strchr(acl_spec, ':')) != NULL)
{
+ char *endptr;
+
*p++ = '\0';
+ port = strtol(p, &endptr, 10);
- if (ijb_isdigit(*p) == 0)
+ if (port <= 0 || port > 65535 || *endptr != '\0')
{
+ free(acl_spec);
return(-1);
}
- port = atoi(p);
}
- aca->port = port;
+ aca->port = (unsigned long)port;
- aca->addr = ntohl(resolve_hostname_to_ip(aspec));
+ aca->addr = ntohl(resolve_hostname_to_ip(acl_spec));
+ free(acl_spec);
if (aca->addr == INADDR_NONE)
{
+ /* XXX: This will be logged as parse error. */
return(-1);
}
@@ -760,7 +873,7 @@ struct http_response *block_url(struct client_state *csp)
if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
&& is_imageurl(csp))
{
- char *p;
+ char *p;
/* determine HOW images should be blocked */
p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
@@ -884,7 +997,6 @@ struct http_response *block_url(struct client_state *csp)
return cgi_error_memory();
}
}
-
}
else
#endif /* def FEATURE_IMAGE_BLOCKING */
@@ -913,7 +1025,7 @@ struct http_response *block_url(struct client_state *csp)
}
else
{
- rsp->status = strdup("404 Request for blocked URL");
+ rsp->status = strdup("403 Request for blocked URL");
}
if (rsp->status == NULL)
@@ -931,7 +1043,15 @@ struct http_response *block_url(struct client_state *csp)
#ifdef FEATURE_FORCE_LOAD
err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
- if (csp->http->ssl != 0)
+ /*
+ * Export the force conditional block killer if
+ *
+ * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
+ * - Privoxy is configured to enforce blocks, or
+ * - it's a CONNECT request and enforcing wouldn't work anyway.
+ */
+ if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+ || (0 == strcmpic(csp->http->gpc, "connect")))
#endif /* ndef FEATURE_FORCE_LOAD */
{
err = map_block_killer(exports, "force-support");
@@ -956,8 +1076,9 @@ struct http_response *block_url(struct client_state *csp)
return cgi_error_memory();
}
}
+ rsp->reason = RSP_REASON_BLOCKED;
- return finish_http_response(rsp);
+ return finish_http_response(csp, rsp);
}
@@ -968,7 +1089,7 @@ struct http_response *block_url(struct client_state *csp)
* Function : trust_url FIXME: I should be called distrust_url
*
* Description : Calls is_untrusted_url to determine if the URL is trusted
- * and if not, returns a HTTP 304 response with a reject message.
+ * and if not, returns a HTTP 403 response with a reject message.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
@@ -1002,8 +1123,9 @@ struct http_response *trust_url(struct client_state *csp)
return cgi_error_memory();
}
+ rsp->status = strdup("403 Request blocked by Privoxy");
exports = default_exports(csp, NULL);
- if (exports == NULL)
+ if (exports == NULL || rsp->status == NULL)
{
free_http_response(rsp);
return cgi_error_memory();
@@ -1038,7 +1160,7 @@ struct http_response *trust_url(struct client_state *csp)
p = strdup("");
for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
{
- sprintf(buf, "
%s\n", t->spec);
+ snprintf(buf, sizeof(buf), "%s\n", t->spec);
string_append(&p, buf);
}
err = map(exports, "trusted-referrers", 1, p, 0);
@@ -1060,7 +1182,7 @@ struct http_response *trust_url(struct client_state *csp)
p = strdup("");
for (l = csp->config->trust_info->first; l ; l = l->next)
{
- sprintf(buf, " %s
\n",l->str, l->str);
+ snprintf(buf, sizeof(buf), " %s
\n", l->str, l->str);
string_append(&p, buf);
}
err = map(exports, "trust-info", 1, p, 0);
@@ -1078,10 +1200,22 @@ struct http_response *trust_url(struct client_state *csp)
}
/*
- * Export the force prefix or the force conditional block killer
+ * Export the force conditional block killer if
+ *
+ * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
+ * - Privoxy is configured to enforce blocks, or
+ * - it's a CONNECT request and enforcing wouldn't work anyway.
*/
#ifdef FEATURE_FORCE_LOAD
- err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
+ if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+ || (0 == strcmpic(csp->http->gpc, "connect")))
+ {
+ err = map_block_killer(exports, "force-support");
+ }
+ else
+ {
+ err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
+ }
#else /* ifndef FEATURE_FORCE_LOAD */
err = map_block_killer(exports, "force-support");
#endif /* ndef FEATURE_FORCE_LOAD */
@@ -1102,107 +1236,320 @@ struct http_response *trust_url(struct client_state *csp)
free_http_response(rsp);
return cgi_error_memory();
}
+ rsp->reason = RSP_REASON_UNTRUSTED;
- return finish_http_response(rsp);
+ return finish_http_response(csp, rsp);
}
#endif /* def FEATURE_TRUST */
+/*********************************************************************
+ *
+ * Function : execute_single_pcrs_command
+ *
+ * Description : Apply single pcrs command to the subject.
+ * The subject itself is left untouched, memory for the result
+ * is malloc()ed and it is the caller's responsibility to free
+ * the result when it's no longer needed.
+ *
+ * Parameters :
+ * 1 : subject = the subject (== original) string
+ * 2 : pcrs_command = the pcrs command as string (s@foo@bar@)
+ * 3 : hits = int* for returning the number of modifications
+ *
+ * Returns : NULL in case of errors, otherwise the
+ * result of the pcrs command.
+ *
+ *********************************************************************/
+char *execute_single_pcrs_command(char *subject, const char *pcrs_command, int *hits)
+{
+ int error;
+ size_t size;
+ char *result = NULL;
+ pcrs_job *job;
+
+ assert(subject);
+ assert(pcrs_command);
+
+ *hits = 0;
+ size = strlen(subject);
+
+ if (NULL == (job = pcrs_compile_command(pcrs_command, &error)))
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to compile pcrs command \"%s\". Error: %d.",
+ pcrs_command, error);
+ }
+ else if ((*hits = pcrs_execute(job, subject, size, &result, &size)) < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to execute pcrs command: %s", pcrs_strerror(*hits));
+ *hits = 0;
+ freez(result);
+ }
+
+ if (job)
+ {
+ job = pcrs_free_job(job);
+ }
+
+ return result;
+
+}
-#ifdef FEATURE_FAST_REDIRECTS
/*********************************************************************
*
- * Function : redirect_url
+ * Function : rewrite_url
*
- * Description : Checks for redirection URLs and returns a HTTP redirect
- * to the destination URL, if necessary
+ * Description : Rewrites a URL with a single pcrs command
+ * and returns the result if it differs from the
+ * original and isn't obviously invalid.
*
* Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
+ * 1 : old_url = URL to rewrite.
+ * 2 : pcrs_command = pcrs command formatted as string (s@foo@bar@)
*
- * Returns : NULL if URL was clean, HTTP redirect otherwise.
+ *
+ * Returns : NULL if the pcrs_command didn't change the url, or
+ * the result of the modification.
*
*********************************************************************/
-struct http_response *redirect_url(struct client_state *csp)
+char *rewrite_url(char *old_url, const char *pcrs_command)
{
- char *p, *q;
- struct http_response *rsp;
- char *redirect_mode = NULL;
- int x, y;
+ char *new_url = NULL;
+ int hits;
- if ((csp->action->flags & ACTION_REDIRECT))
+ assert(old_url);
+ assert(pcrs_command);
+
+ new_url = execute_single_pcrs_command(old_url, pcrs_command, &hits);
+
+ if (hits == 0)
{
- q = csp->action->string[ACTION_STRING_REDIRECT];
+ log_error(LOG_LEVEL_REDIRECTS,
+ "pcrs command \"%s\" didn't change \"%s\".",
+ pcrs_command, old_url, new_url);
+ freez(new_url);
+ }
+ else if (strncmpic(new_url, "http://", 7) && strncmpic(new_url, "https://", 8))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s), "
+ "but the result doesn't look like a valid URL and will be ignored.",
+ pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
+ freez(new_url);
}
else
{
- redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS];
- if (0 == strcmpic(redirect_mode, "check-decoded-url"))
- {
- p = q = csp->http->path;
- log_error(LOG_LEVEL_REDIRECTS, "Decoding path: %s if necessary.", p);
- while (*p)
- {
- if (*p == '%') /* Escape sequence? */
- {
- /* Yes, translate from hexadecimal to decimal */
- p++;
- /* First byte */
- x=((int)*p++)-48;
- if (x>9) x-=7;
- x<<=4;
- /* Second byte */
- y=((int)*p++)-48;
- if (y>9)y-=7;
- /* Merge */
- *q++=(char)(x|y);
- }
- else
- {
- /* No, forward character. */
- *q++=*p++;
- }
- }
- *q='\0';
+ log_error(LOG_LEVEL_REDIRECTS,
+ "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s).",
+ pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
+ }
+
+ return new_url;
+
+}
+
+
+#ifdef FEATURE_FAST_REDIRECTS
+/*********************************************************************
+ *
+ * Function : get_last_url
+ *
+ * Description : Search for the last URL inside a string.
+ * If the string already is a URL, it will
+ * be the first URL found.
+ *
+ * Parameters :
+ * 1 : subject = the string to check
+ * 2 : redirect_mode = +fast-redirect{} mode
+ *
+ * Returns : NULL if no URL was found, or
+ * the last URL found.
+ *
+ *********************************************************************/
+char *get_last_url(char *subject, const char *redirect_mode)
+{
+ char *new_url = NULL;
+ char *tmp;
+
+ assert(subject);
+ assert(redirect_mode);
+
+ subject = strdup(subject);
+ if (subject == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR, "Out of memory while searching for redirects.");
+ return NULL;
+ }
+
+ if (0 == strcmpic(redirect_mode, "check-decoded-url"))
+ {
+ log_error(LOG_LEVEL_REDIRECTS, "Decoding \"%s\" if necessary.", subject);
+ new_url = url_decode(subject);
+ if (new_url != NULL)
+ {
+ freez(subject);
+ subject = new_url;
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", subject);
}
- p = q = csp->http->path;
- log_error(LOG_LEVEL_REDIRECTS, "Checking path for redirects: %s", p);
+ }
+
+ log_error(LOG_LEVEL_REDIRECTS, "Checking \"%s\" for redirects.", subject);
+ /*
+ * Find the last URL encoded in the request
+ */
+ tmp = subject;
+ while ((tmp = strstr(tmp, "http://")) != NULL)
+ {
+ new_url = tmp++;
+ }
+ tmp = (new_url != NULL) ? new_url : subject;
+ while ((tmp = strstr(tmp, "https://")) != NULL)
+ {
+ new_url = tmp++;
+ }
+
+ if ((new_url != NULL)
+ && ( (new_url != subject)
+ || (0 == strncmpic(subject, "http://", 7))
+ || (0 == strncmpic(subject, "https://", 8))
+ ))
+ {
/*
- * find the last URL encoded in the request
+ * Return new URL if we found a redirect
+ * or if the subject already was a URL.
+ *
+ * The second case makes sure that we can
+ * chain get_last_url after another redirection check
+ * (like rewrite_url) without losing earlier redirects.
*/
- while ((p = strstr(p, "http://")) != NULL)
- {
- q = p++;
- }
+ new_url = strdup(new_url);
+ freez(subject);
+ return new_url;
}
+
+ freez(subject);
+ return NULL;
+
+}
+#endif /* def FEATURE_FAST_REDIRECTS */
+
+
+/*********************************************************************
+ *
+ * Function : redirect_url
+ *
+ * Description : Checks if Privoxy should answer the request with
+ * a HTTP redirect and generates the redirect if
+ * necessary.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : NULL if the request can pass, HTTP redirect otherwise.
+ *
+ *********************************************************************/
+struct http_response *redirect_url(struct client_state *csp)
+{
+ struct http_response *rsp;
+#ifdef FEATURE_FAST_REDIRECTS
/*
- * if there was any, generate and return a HTTP redirect
+ * XXX: Do we still need FEATURE_FAST_REDIRECTS
+ * as compile-time option? The user can easily disable
+ * it in his action file.
*/
- if (q != csp->http->path)
+ char * redirect_mode;
+#endif /* def FEATURE_FAST_REDIRECTS */
+ char *old_url = NULL;
+ char *new_url = NULL;
+ char *redirection_string;
+
+ if ((csp->action->flags & ACTION_REDIRECT))
{
- log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q);
+ redirection_string = csp->action->string[ACTION_STRING_REDIRECT];
+
+ /*
+ * If the redirection string begins with 's',
+ * assume it's a pcrs command, otherwise treat it as
+ * 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}
+ * she would get undefined results anyway.
+ *
+ */
- if (NULL == (rsp = alloc_http_response()))
+ if (*redirection_string == 's')
{
- return cgi_error_memory();
+ old_url = csp->http->url;
+ new_url = rewrite_url(old_url, redirection_string);
}
-
- if ( enlist_unique_header(rsp->headers, "Location", q)
- || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))) )
+ else
{
- free_http_response(rsp);
- return cgi_error_memory();
+ log_error(LOG_LEVEL_REDIRECTS,
+ "No pcrs command recognized, assuming that \"%s\" is already properly formatted.",
+ redirection_string);
+ new_url = strdup(redirection_string);
}
+ }
- return finish_http_response(rsp);
+#ifdef FEATURE_FAST_REDIRECTS
+ if ((csp->action->flags & ACTION_FAST_REDIRECTS))
+ {
+ redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS];
+
+ /*
+ * If it exists, use the previously rewritten URL as input
+ * otherwise just use the old path.
+ */
+ old_url = (new_url != NULL) ? new_url : strdup(csp->http->path);
+ new_url = get_last_url(old_url, redirect_mode);
+ freez(old_url);
}
- else
+#endif /* def FEATURE_FAST_REDIRECTS */
+
+ /* Did any redirect action trigger? */
+ if (new_url)
{
- return NULL;
+ if (0 == strcmpic(new_url, csp->http->url))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "New URL \"%s\" and old URL \"%s\" are the same. Redirection loop prevented.",
+ csp->http->url, new_url);
+ freez(new_url);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_REDIRECTS, "New URL is: %s", new_url);
+
+ if (NULL == (rsp = alloc_http_response()))
+ {
+ freez(new_url);
+ return cgi_error_memory();
+ }
+
+ 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);
+ return cgi_error_memory();
+ }
+ rsp->reason = RSP_REASON_REDIRECTED;
+ freez(new_url);
+
+ return finish_http_response(csp, rsp);
+ }
}
+ /* Only reached if no redirect is required */
+ return NULL;
+
}
-#endif /* def FEATURE_FAST_REDIRECTS */
#ifdef FEATURE_IMAGE_BLOCKING
@@ -1223,7 +1570,7 @@ struct http_response *redirect_url(struct client_state *csp)
* otherwise
*
*********************************************************************/
-int is_imageurl(struct client_state *csp)
+int is_imageurl(const struct client_state *csp)
{
#ifdef FEATURE_IMAGE_DETECT_MSIE
char *tmp;
@@ -1270,7 +1617,7 @@ int is_imageurl(struct client_state *csp)
* Returns : 0 => trusted, 1 => untrusted
*
*********************************************************************/
-int is_untrusted_url(struct client_state *csp)
+int is_untrusted_url(const struct client_state *csp)
{
struct file_list *fl;
struct block_spec *b;
@@ -1322,7 +1669,7 @@ int is_untrusted_url(struct client_state *csp)
{
/* if the URL's referrer is from a trusted referrer, then
* add the target spec to the trustfile as an unblocked
- * domain and return NULL (which means it's OK).
+ * domain and return 0 (which means it's OK).
*/
FILE *fp;
@@ -1353,9 +1700,19 @@ int is_untrusted_url(struct client_state *csp)
string_join(&new_entry, path);
}
+ /*
+ * Give a reason for generating this entry.
+ */
+ string_append(&new_entry, " # Trusted referrer was: ");
+ string_append(&new_entry, referer);
+
if (new_entry != NULL)
{
- fprintf(fp, "%s\n", new_entry);
+ if (-1 == fprintf(fp, "%s\n", new_entry))
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to append \'%s\' to trustfile \'%s\': %E",
+ new_entry, csp->config->trustfile);
+ }
free(new_entry);
}
else
@@ -1366,9 +1723,15 @@ int is_untrusted_url(struct client_state *csp)
fclose(fp);
}
+ else
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to append new entry for \'%s\' to trustfile \'%s\': %E",
+ csp->http->hostport, csp->config->trustfile);
+ }
return 0;
}
}
+
return 1;
}
#endif /* def FEATURE_TRUST */
@@ -1384,6 +1747,11 @@ int is_untrusted_url(struct client_state *csp)
* csp->content_length to the modified size and raise the
* CSP_FLAG_MODIFIED flag.
*
+ * XXX: Currently pcrs_filter_response is also responsible
+ * for dechunking and decompressing. Both should be
+ * done in separate functions so other content modifiers
+ * profit as well, even if pcrs filtering is disabled.
+ *
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*
@@ -1412,7 +1780,7 @@ char *pcrs_filter_response(struct client_state *csp)
{
return(NULL);
}
- size = csp->iob->eod - csp->iob->cur;
+ size = (size_t)(csp->iob->eod - csp->iob->cur);
/*
* Need to check the set of re_filterfiles...
@@ -1433,7 +1801,7 @@ char *pcrs_filter_response(struct client_state *csp)
if (0 == found_filters)
{
log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
- return(NULL);
+ return(NULL);
}
/*
@@ -1451,11 +1819,61 @@ char *pcrs_filter_response(struct client_state *csp)
csp->flags |= CSP_FLAG_MODIFIED;
}
+#ifdef FEATURE_ZLIB
+ /*
+ * If the body has a compressed transfer-encoding,
+ * uncompress it first, adjusting size and iob->eod.
+ * Note that decompression occurs after de-chunking.
+ */
+ if (csp->content_type & (CT_GZIP | CT_DEFLATE))
+ {
+ /* Notice that we at least tried to decompress. */
+ if (JB_ERR_OK != decompress_iob(csp))
+ {
+ /*
+ * We failed to decompress the data; there's no point
+ * in continuing since we can't filter.
+ *
+ * XXX: Actually the Accept-Encoding header may
+ * just be incorrect in which case we could continue
+ * with filtering.
+ *
+ * Unset CT_GZIP and CT_DEFLATE to remember not
+ * to modify the Content-Encoding header later.
+ */
+ csp->content_type &= ~CT_GZIP;
+ csp->content_type &= ~CT_DEFLATE;
+ return(NULL);
+ }
+
+ /*
+ * Decompression gives us a completely new iob,
+ * so we need to update.
+ */
+ size = (size_t)(csp->iob->eod - csp->iob->cur);
+ old = csp->iob->cur;
+
+ csp->flags |= CSP_FLAG_MODIFIED;
+ csp->content_type &= ~CT_TABOO;
+ }
+#endif
+
for (i = 0; i < MAX_AF_FILES; i++)
{
fl = csp->rlist[i];
if ((NULL == fl) || (NULL == fl->f))
- break;
+ {
+ /*
+ * Either there are no filter files
+ * left, or this filter file just
+ * contains no valid filters.
+ *
+ * Continue to be sure we don't miss
+ * valid filter files that are chained
+ * after empty or invalid ones.
+ */
+ continue;
+ }
/*
* For all applying +filter actions, look if a filter by that
* name exists and if yes, execute it's pcrs_joblist on the
@@ -1463,12 +1881,20 @@ char *pcrs_filter_response(struct client_state *csp)
*/
for (b = fl->f; b; b = b->next)
{
+ if (b->type != FT_CONTENT_FILTER)
+ {
+ /* Skip header filters */
+ continue;
+ }
+
for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
filtername ; filtername = filtername->next)
{
if (strcmp(b->name, filtername->str) == 0)
{
- int current_hits = 0;
+ int current_hits = 0; /* Number of hits caused by this filter */
+ int job_number = 0; /* Which job we're currently executing */
+ int job_hits = 0; /* How many hits the current job caused */
if ( NULL == b->joblist )
{
@@ -1480,13 +1906,50 @@ char *pcrs_filter_response(struct client_state *csp)
/* Apply all jobs from the joblist */
for (job = b->joblist; NULL != job; job = job->next)
{
- current_hits += pcrs_execute(job, old, size, &new, &size);
- if (old != csp->iob->cur) free(old);
- old=new;
+ job_number++;
+ job_hits = pcrs_execute(job, old, size, &new, &size);
+
+ if (job_hits >= 0)
+ {
+ /*
+ * That went well. Continue filtering
+ * and use the result of this job as
+ * input for the next one.
+ */
+ current_hits += job_hits;
+ if (old != csp->iob->cur)
+ {
+ free(old);
+ }
+ old = new;
+ }
+ else
+ {
+ /*
+ * This job caused an unexpected error. Inform the user
+ * and skip the rest of the jobs in this filter. We could
+ * continue with the next job, but usually the jobs
+ * depend on each other or are similar enough to
+ * fail for the same reason.
+ *
+ * At the moment our pcrs expects the error codes of pcre 3.4,
+ * but newer pcre versions can return additional error codes.
+ * As a result pcrs_strerror()'s error message might be
+ * "Unknown error ...", therefore we print the numerical value
+ * as well.
+ *
+ * XXX: Is this important enough for LOG_LEVEL_ERROR or
+ * should we use LOG_LEVEL_RE_FILTER instead?
+ */
+ log_error(LOG_LEVEL_ERROR, "Skipped filter \'%s\' after job number %u: %s (%d)",
+ b->name, job_number, pcrs_strerror(job_hits), job_hits);
+ break;
+ }
}
- log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) with filter %s produced %d hits (new size %d).",
- csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
+ log_error(LOG_LEVEL_RE_FILTER,
+ "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
+ csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
hits += current_hits;
}
@@ -1532,7 +1995,9 @@ char *gif_deanimate_response(struct client_state *csp)
{
struct binbuffer *in, *out;
char *p;
- size_t size = csp->iob->eod - csp->iob->cur;
+ size_t size;
+
+ size = (size_t)(csp->iob->eod - csp->iob->cur);
/*
* If the body has a "chunked" transfer-encoding,
@@ -1602,9 +2067,12 @@ char *gif_deanimate_response(struct client_state *csp)
*********************************************************************/
char *jpeg_inspect_response(struct client_state *csp)
{
- struct binbuffer *in = NULL, *out = NULL;
+ struct binbuffer *in = NULL;
+ struct binbuffer *out = NULL;
char *p = NULL;
- size_t size = csp->iob->eod - csp->iob->cur;
+ size_t size;
+
+ size = (size_t)(csp->iob->eod - csp->iob->cur);
/*
* If the body has a "chunked" transfer-encoding,
@@ -1677,7 +2145,7 @@ char *jpeg_inspect_response(struct client_state *csp)
* went wrong
*
*********************************************************************/
-int remove_chunked_transfer_coding(char *buffer, const size_t size)
+size_t remove_chunked_transfer_coding(char *buffer, const size_t size)
{
size_t newsize = 0;
unsigned int chunksize = 0;
@@ -1859,29 +2327,42 @@ struct http_response *direct_response(struct client_state *csp)
{
for (p = csp->headers->first; (p != NULL) ; p = p->next)
{
- if (!strncmp("Max-Forwards:", p->str, 13)
- && (*(p->str+13) != '\0') && (atoi(p->str+13) == 0))
+ if (!strncmpic("Max-Forwards:", p->str, 13))
{
- /* FIXME: We could handle at least TRACE here,
- but that would require a verbatim copy of
- the request which we don't have anymore */
-
- log_error(LOG_LEVEL_HEADER, "Found Max-Forwards:0 in OPTIONS or TRACE request -- Returning 501");
+ unsigned int max_forwards;
- /* Get mem for response or fail*/
- if (NULL == (rsp = alloc_http_response()))
+ /*
+ * If it's a Max-Forwards value of zero,
+ * we have to intercept the request.
+ */
+ if (1 == sscanf(p->str+12, ": %u", &max_forwards) && max_forwards == 0)
{
- return cgi_error_memory();
- }
+ /*
+ * FIXME: We could handle at least TRACE here,
+ * but that would require a verbatim copy of
+ * the request which we don't have anymore
+ */
+ log_error(LOG_LEVEL_HEADER,
+ "Detected header \'%s\' in OPTIONS or TRACE request. Returning 501.",
+ p->str);
+
+ /* Get mem for response or fail*/
+ if (NULL == (rsp = alloc_http_response()))
+ {
+ return cgi_error_memory();
+ }
- if (NULL == (rsp->status = strdup("501 Not Implemented")))
- {
- free_http_response(rsp);
- return cgi_error_memory();
- }
+ if (NULL == (rsp->status = strdup("501 Not Implemented")))
+ {
+ free_http_response(rsp);
+ return cgi_error_memory();
+ }
+
+ rsp->is_static = 1;
+ rsp->reason = RSP_REASON_UNSUPPORTED;
- rsp->is_static = 1;
- return(finish_http_response(rsp));
+ return(finish_http_response(csp, rsp));
+ }
}
}
}