-const char filters_rcs[] = "$Id: filters.c,v 1.81 2007/03/05 14:40:53 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.87 2007/04/30 15:53:10 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/filters.c,v $
*
* Revisions :
* $Log: filters.c,v $
+ * Revision 1.87 2007/04/30 15:53:10 fabiankeil
+ * Make sure filters with dynamic jobs actually use them.
+ *
+ * Revision 1.86 2007/04/30 15:03:28 fabiankeil
+ * - Introduce dynamic pcrs jobs that can resolve variables.
+ * - Don't run redirect functions more than once,
+ * unless they are activated more than once.
+ *
+ * Revision 1.85 2007/03/21 12:24:47 fabiankeil
+ * - Log the content size after decompression in decompress_iob()
+ * instead of pcrs_filter_response().
+ *
+ * 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
#include "list.h"
#include "deanimate.h"
#include "urlmatch.h"
+#include "loaders.h"
#ifdef _WIN32
#include "win32.h"
#ifdef FEATURE_FORCE_LOAD
err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
- if (csp->http->ssl != 0 || 0 == strcmpic(csp->http->gpc, "connect"))
+ /*
+ * 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");
}
/*
- * 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
- if (0 == strcmpic(csp->http->gpc, "connect"))
+ if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+ || (0 == strcmpic(csp->http->gpc, "connect")))
{
- err = map_block_killer(exports, "force-support");
+ err = map_block_killer(exports, "force-support");
}
else
{
}
#endif /* def FEATURE_TRUST */
+
/*********************************************************************
*
- * Function : execute_single_pcrs_command
+ * Function : compile_dynamic_pcrs_job_list
*
- * 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.
+ * Description : Compiles a dynamic pcrs job list (one with variables
+ * resolved at request time)
*
* 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
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : b = The filter list to compile
*
* Returns : NULL in case of errors, otherwise the
- * result of the pcrs command.
+ * pcrs job list.
*
*********************************************************************/
-char *execute_single_pcrs_command(char *subject, const char *pcrs_command, int *hits)
+pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
{
- int error;
- size_t size;
- char *result = NULL;
- pcrs_job *job;
-
- assert(subject);
- assert(pcrs_command);
-
- *hits = 0;
- size = strlen(subject);
+ struct list_entry *pattern;
+ pcrs_job *job_list = NULL;
+ pcrs_job *dummy = NULL;
+ pcrs_job *lastjob = NULL;
+ int error = 0;
- 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)
+ const struct pcrs_variable variables[] =
{
- log_error(LOG_LEVEL_ERROR, "Failed to execute pcrs command: %s", pcrs_strerror(*hits));
- *hits = 0;
- freez(result);
- }
+ {"url", csp->http->url, 1},
+ {"path", csp->http->path, 1},
+ {"host", csp->http->host, 1},
+ {"origin", csp->ip_addr_str, 1},
+ {NULL, NULL, 1}
+ };
- if (job)
+ for (pattern = b->patterns->first; pattern != NULL; pattern = pattern->next)
{
- job = pcrs_free_job(job);
- }
+ assert(pattern->str != NULL);
- return result;
+ dummy = pcrs_compile_dynamic_command(pattern->str, variables, &error);
+ if (NULL == dummy)
+ {
+ assert(error < 0);
+ log_error(LOG_LEVEL_ERROR,
+ "Adding filter job \'%s\' to dynamic filter %s failed: %s",
+ pattern->str, b->name, pcrs_strerror(error));
+ continue;
+ }
+ else
+ {
+ if (error == PCRS_WARN_TRUNCATION)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "At least one of the variables in \'%s\' had to "
+ "be truncated before compilation", pattern->str);
+ }
+ if (job_list == NULL)
+ {
+ job_list = dummy;
+ }
+ else
+ {
+ lastjob->next = dummy;
+ }
+ lastjob = dummy;
+ }
+ }
+ return job_list;
}
+
/*********************************************************************
*
* Function : rewrite_url
assert(old_url);
assert(pcrs_command);
- new_url = execute_single_pcrs_command(old_url, pcrs_command, &hits);
+ new_url = pcrs_execute_single_command(old_url, pcrs_command, &hits);
if (hits == 0)
{
log_error(LOG_LEVEL_REDIRECTS,
"pcrs command \"%s\" didn't change \"%s\".",
- pcrs_command, old_url, new_url);
+ pcrs_command, old_url);
+ freez(new_url);
+ }
+ else if (hits < 0)
+ {
+ log_error(LOG_LEVEL_REDIRECTS,
+ "executing pcrs command \"%s\" to rewrite %s failed: %s",
+ pcrs_command, old_url, pcrs_strerror(hits));
freez(new_url);
}
else if (strncmpic(new_url, "http://", 7) && strncmpic(new_url, "https://", 8))
new_url = get_last_url(old_url, redirect_mode);
freez(old_url);
}
+
+ /*
+ * Disable redirect checkers, so that they
+ * will be only run more than once if the user
+ * also enables them through tags.
+ *
+ * From a performance point of view
+ * it doesn't matter, but the duplicated
+ * log messages are annoying.
+ */
+ csp->action->flags &= ~ACTION_FAST_REDIRECTS;
#endif /* def FEATURE_FAST_REDIRECTS */
+ csp->action->flags &= ~ACTION_REDIRECT;
/* Did any redirect action trigger? */
if (new_url)
csp->content_type &= ~CT_DEFLATE;
return(NULL);
}
- log_error(LOG_LEVEL_RE_FILTER,
- "Decompression successful. Old size: %d, new size: %d.",
- size, csp->iob->eod - csp->iob->cur);
/*
* Decompression gives us a completely new iob,
*/
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)
{
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 */
+ pcrs_job *joblist = b->joblist;
+
+ if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
- if ( NULL == b->joblist )
+ if (NULL == joblist)
{
log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
continue;
prev_size = size;
/* Apply all jobs from the joblist */
- for (job = b->joblist; NULL != job; job = job->next)
+ for (job = joblist; NULL != job; job = job->next)
{
job_number++;
job_hits = pcrs_execute(job, old, size, &new, &size);
}
}
+ if (b->dynamic) pcrs_free_joblist(joblist);
+
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);
}
+/*********************************************************************
+ *
+ * Function : get_forward_override_settings
+ *
+ * Description : Returns forward settings as specified with the
+ * forward-override{} action. forward-override accepts
+ * forward lines similar to the one used in the
+ * configuration file, but without the URL pattern.
+ *
+ * For example:
+ *
+ * forward / .
+ *
+ * in the configuration file can be replaced with
+ * the action section:
+ *
+ * {+forward-override{forward .}}
+ * /
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Pointer to forwarding structure in case of success.
+ * Invalid syntax is fatal.
+ *
+ *********************************************************************/
+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];
+ char *http_parent = NULL;
+ /* variable names were chosen for consistency reasons. */
+ struct forward_spec *fwd = NULL;
+ int vec_count;
+ char *vec[3];
+
+ assert(csp->action->flags & ACTION_FORWARD_OVERRIDE);
+ /* Should be enforced by load_one_actions_file() */
+ assert(strlen(forward_override_line) < sizeof(forward_settings) - 1);
+
+ /* Create a copy ssplit can modify */
+ strlcpy(forward_settings, forward_override_line, sizeof(forward_settings));
+
+ if (NULL != csp->fwd)
+ {
+ /*
+ * XXX: Currently necessary to prevent memory
+ * leaks when the show-url-info cgi page is visited.
+ */
+ unload_forward_spec(csp->fwd);
+ }
+
+ /*
+ * allocate a new forward node, valid only for
+ * 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 */
+ }
+
+ vec_count = ssplit(forward_settings, " \t", vec, SZ(vec), 1, 1);
+ if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
+ {
+ fwd->type = SOCKS_NONE;
+
+ /* Parse the parent HTTP proxy host:port */
+ http_parent = vec[1];
+
+ }
+ else if (vec_count == 3)
+ {
+ char *socks_proxy = NULL;
+
+ if (!strcasecmp(vec[0], "forward-socks4"))
+ {
+ fwd->type = SOCKS_4;
+ socks_proxy = vec[1];
+ }
+ else if (!strcasecmp(vec[0], "forward-socks4a"))
+ {
+ fwd->type = SOCKS_4A;
+ socks_proxy = vec[1];
+ }
+
+ if (NULL != socks_proxy)
+ {
+ /* Parse the SOCKS proxy host[:port] */
+ fwd->gateway_host = strdup(socks_proxy);
+
+ if (NULL != (socks_proxy = strchr(fwd->gateway_host, ':')))
+ {
+ *socks_proxy++ = '\0';
+ fwd->gateway_port = strtol(socks_proxy, NULL, 0);
+ }
+
+ if (fwd->gateway_port <= 0)
+ {
+ fwd->gateway_port = 1080;
+ }
+
+ http_parent = vec[2];
+ }
+ }
+
+ if (NULL == http_parent)
+ {
+ log_error(LOG_LEVEL_FATAL,
+ "Invalid forward-override syntax in: %s", forward_override_line);
+ /* Never get here - LOG_LEVEL_FATAL causes program exit */
+ }
+
+ /* Parse http forwarding settings */
+ if (strcmp(http_parent, ".") != 0)
+ {
+ fwd->forward_host = strdup(http_parent);
+
+ if (NULL != (http_parent = strchr(fwd->forward_host, ':')))
+ {
+ *http_parent++ = '\0';
+ fwd->forward_port = strtol(http_parent, NULL, 0);
+ }
+
+ if (fwd->forward_port <= 0)
+ {
+ fwd->forward_port = 8000;
+ }
+ }
+
+ assert (NULL != fwd);
+
+ log_error(LOG_LEVEL_CONNECT,
+ "Overriding forwarding settings based on \'%s\'", forward_override_line);
+
+ return fwd;
+}
+
/*********************************************************************
*
* Function : forward_url
*
* Description : Should we forward this to another proxy?
*
+ * XXX: Should be changed to make use of csp->fwd.
+ *
* Parameters :
* 1 : http = http_request request for current URL
* 2 : csp = Current client state (buffers, headers, etc...)
static const struct forward_spec fwd_default[1] = { FORWARD_SPEC_INITIALIZER };
struct forward_spec *fwd = csp->config->forward;
+ if (csp->action->flags & ACTION_FORWARD_OVERRIDE)
+ {
+ return get_forward_override_settings(csp);
+ }
+
if (fwd == NULL)
{
return fwd_default;