-const char filters_rcs[] = "$Id: filters.c,v 1.200 2016/02/26 12:29:38 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-2016 the
- * Privoxy team. http://www.privoxy.org/
+ * Copyright : Written by and Copyright (C) 2001-2020 the
+ * Privoxy team. https://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
* by and Copyright (C) 1997 Anonymous Coders and
#include <assert.h>
#ifndef _WIN32
-#ifndef __OS2__
#include <unistd.h>
-#endif /* ndef __OS2__ */
#include <netinet/in.h>
#else
#include <winsock2.h>
#endif /* ndef _WIN32 */
-#ifdef __OS2__
-#include <utils.h>
-#endif /* def __OS2__ */
-
#include "project.h"
#include "filters.h"
#include "encode.h"
#include "win32.h"
#endif
-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,
#endif
struct url_actions *b);
+#ifdef FEATURE_EXTENDED_STATISTICS
+static void increment_block_reason_counter(const char *block_reason);
+#endif
+
#ifdef FEATURE_ACL
#ifdef HAVE_RFC2553
/*********************************************************************
* 3 : len = length of IP address in octets
* 4 : port = port number in network order;
*
- * Returns : 0 = no errror; -1 otherwise.
+ * Returns : void
*
*********************************************************************/
-static int sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
- uint8_t **ip, unsigned int *len,
- in_port_t **port)
+static void sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
+ uint8_t **ip, unsigned int *len,
+ in_port_t **port)
{
- if (NULL == addr)
- {
- return(-1);
- }
+ assert(NULL != addr);
+ assert(addr->ss_family == AF_INET || addr->ss_family == AF_INET6);
switch (addr->ss_family)
{
}
break;
- default:
- /* Unsupported address family */
- return(-1);
}
-
- return(0);
}
return 0;
}
- /* TODO: Optimize by checking by words insted of octets */
+ /* TODO: Optimize by checking by words instead of octets */
for (i = 0; (i < addr_len) && netmask_addr[i]; i++)
{
if ((network_addr[i] & netmask_addr[i]) !=
}
aca->mask.ss_family = aca->addr.ss_family;
- if (sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port))
- {
- return(-1);
- }
+ sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port);
if (p)
{
*
* Description : Check to see if CONNECT requests to the destination
* port of this request are forbidden. The check is
- * independend of the actual request method.
+ * independent of the actual request method.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
return cgi_error_memory();
}
+#ifdef FEATURE_EXTENDED_STATISTICS
+ if (csp->action->string[ACTION_STRING_BLOCK] != NULL)
+ {
+ increment_block_reason_counter(csp->action->string[ACTION_STRING_BLOCK]);
+ }
+#endif
+
/*
* If it's an image-url, send back an image or redirect
* as specified by the relevant +image action
{"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}
};
* the last URL found.
*
*********************************************************************/
-char *get_last_url(char *subject, const char *redirect_mode)
+static char *get_last_url(char *subject, const char *redirect_mode)
{
char *new_url = NULL;
char *tmp;
}
if (0 == strcmpic(redirect_mode, "check-decoded-url") && strchr(subject, '%'))
- {
+ {
char *url_segment = NULL;
char **url_segments;
size_t max_segments;
*
* Function : is_imageurl
*
- * Description : Given a URL, decide whether it is an image or not,
- * using either the info from a previous +image action
- * or, #ifdef FEATURE_IMAGE_DETECT_MSIE, and the browser
- * is MSIE and not on a Mac, tell from the browser's accept
- * header.
+ * Description : Given a URL, decide whether it should be treated
+ * as image URL or not.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*
- * Returns : True (nonzero) if URL is an image, false (0)
+ * Returns : True (nonzero) if URL is an image URL, false (0)
* otherwise
*
*********************************************************************/
int is_imageurl(const struct client_state *csp)
{
-#ifdef FEATURE_IMAGE_DETECT_MSIE
- char *tmp;
-
- tmp = get_header_value(csp->headers, "User-Agent:");
- if (tmp && strstr(tmp, "MSIE") && !strstr(tmp, "Mac_"))
- {
- tmp = get_header_value(csp->headers, "Accept:");
- if (tmp && strstr(tmp, "image/gif"))
- {
- /* Client will accept HTML. If this seems counterintuitive,
- * blame Microsoft.
- */
- return(0);
- }
- else
- {
- return(1);
- }
- }
-#endif /* def FEATURE_IMAGE_DETECT_MSIE */
-
return ((csp->action->flags & ACTION_IMAGE) != 0);
}
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).",
+ "filtering %s%s (size %lu) with \'%s\' produced %d hits (new size %lu).",
csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
-
+#ifdef FEATURE_EXTENDED_STATISTICS
+ update_filter_statistics(b->name, current_hits);
+#endif
hits += current_hits;
}
*/
if (!hits)
{
+ if (old != csp->iob->cur && old != new)
+ {
+ freez(old);
+ }
freez(new);
return(NULL);
}
{ "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++)
*/
if ((*size != 0) && fwrite(content, *size, 1, fp) != 1)
{
- log_error(LOG_LEVEL_ERROR, "fwrite(..., %d, 1, ..) failed: %E", *size);
+ log_error(LOG_LEVEL_ERROR, "fwrite(..., %lu, 1, ..) failed: %E", *size);
unlink(file_name);
fclose(fp);
return NULL;
{
log_error(LOG_LEVEL_RE_FILTER,
"Executing '%s' resulted in return value %d. "
- "Read %d of up to %d bytes.", name, (ret >> 8), new_size, *size);
+ "Read %lu of up to %lu bytes.", name, (ret >> 8), new_size, *size);
}
unlink(file_name);
* 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;
}
else
{
- log_error(LOG_LEVEL_DEANIMATE, "Success! GIF shrunk from %d bytes to %d.", size, out->offset);
+ log_error(LOG_LEVEL_DEANIMATE,
+ "Success! GIF shrunk from %lu bytes to %lu.", size, out->offset);
}
csp->content_length = out->offset;
csp->flags |= CSP_FLAG_MODIFIED;
* 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;
{
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);
+ "Already digested %lu of %lu buffered bytes.",
+ chunksize, newsize, *size);
return JB_ERR_PARSE;
}
}
/* XXX: Should get its own loglevel. */
- log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d", *size, newsize);
+ log_error(LOG_LEVEL_RE_FILTER,
+ "De-chunking successful. Shrunk from %lu to %lu", *size, newsize);
*size = newsize;
* If the body has a supported transfer-encoding,
* decompress it, adjusting size and iob->eod.
*/
- if (csp->content_type & (CT_GZIP|CT_DEFLATE))
+ if ((csp->content_type & (CT_GZIP|CT_DEFLATE))
+#ifdef FEATURE_BROTLI
+ || (csp->content_type & CT_BROTLI)
+#endif
+ )
{
if (0 == csp->iob->eod - csp->iob->cur)
{
else
{
/*
- * Unset CT_GZIP and CT_DEFLATE to remember not
- * to modify the Content-Encoding header later.
+ * Unset content types to remember not to
+ * modify the Content-Encoding header later.
*/
csp->content_type &= ~CT_GZIP;
csp->content_type &= ~CT_DEFLATE;
+#ifdef FEATURE_BROTLI
+ csp->content_type &= ~CT_BROTLI;
+#endif
}
}
#endif
if (NULL != socks_proxy)
{
- /* Parse the SOCKS proxy host[:port] */
+ /* Parse the SOCKS proxy [user:pass@]host[:port] */
fwd->gateway_port = 1080;
parse_forwarder_address(socks_proxy,
- &fwd->gateway_host, &fwd->gateway_port);
+ &fwd->gateway_host, &fwd->gateway_port,
+ &fwd->auth_username, &fwd->auth_password);
http_parent = vec[2];
}
{
fwd->forward_port = 8000;
parse_forwarder_address(http_parent,
- &fwd->forward_host, &fwd->forward_port);
+ &fwd->forward_host, &fwd->forward_port,
+ NULL, NULL);
}
assert (NULL != fwd);
* The server didn't bother to declare a MIME-Type.
* Assume it's text that can be filtered.
*
- * This also regulary happens with 304 responses,
+ * This also regularly happens with 304 responses,
* therefore logging anything here would cause
* too much noise.
*/
return FALSE;
}
+#ifdef FEATURE_EXTENDED_STATISTICS
+
+struct filter_statistics_entry
+{
+ char *filter;
+ unsigned long long executions;
+ unsigned long long response_bodies_modified;
+ unsigned long long hits;
+
+ struct filter_statistics_entry *next;
+};
+
+static struct filter_statistics_entry *filter_statistics = NULL;
+
+
+/*********************************************************************
+ *
+ * Function : register_filter_for_statistics
+ *
+ * Description : Registers a filter so we can gather statistics for
+ * it unless the filter has already been registered
+ * before.
+ *
+ * Parameters :
+ * 1 : filter = Name of the filter to register
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+void register_filter_for_statistics(const char *filter)
+{
+ struct filter_statistics_entry *entry;
+
+ privoxy_mutex_lock(&filter_statistics_mutex);
+
+ if (filter_statistics == NULL)
+ {
+ filter_statistics = zalloc_or_die(sizeof(struct filter_statistics_entry));
+ entry = filter_statistics;
+ entry->filter = strdup_or_die(filter);
+ privoxy_mutex_unlock(&filter_statistics_mutex);
+ return;
+ }
+ entry = filter_statistics;
+ while (entry != NULL)
+ {
+ if (!strcmp(entry->filter, filter))
+ {
+ /* Already registered, nothing to do. */
+ break;
+ }
+ if (entry->next == NULL)
+ {
+ entry->next = zalloc_or_die(sizeof(struct filter_statistics_entry));
+ entry->next->filter = strdup_or_die(filter);
+ break;
+ }
+ entry = entry->next;
+ }
+
+ privoxy_mutex_unlock(&filter_statistics_mutex);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : update_filter_statistics
+ *
+ * Description : Updates the statistics for a filter.
+ *
+ * Parameters :
+ * 1 : filter = Name of the filter to update
+ * 2 : hits = Hit count.
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+void update_filter_statistics(const char *filter, int hits)
+{
+ struct filter_statistics_entry *entry;
+
+ privoxy_mutex_lock(&filter_statistics_mutex);
+
+ entry = filter_statistics;
+ while (entry != NULL)
+ {
+ if (!strcmp(entry->filter, filter))
+ {
+ entry->executions++;
+ if (hits != 0)
+ {
+ entry->response_bodies_modified++;
+ entry->hits += (unsigned)hits;
+ }
+ break;
+ }
+ entry = entry->next;
+ }
+
+ privoxy_mutex_unlock(&filter_statistics_mutex);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_filter_statistics
+ *
+ * Description : Gets the statistics for a filter.
+ *
+ * Parameters :
+ * 1 : filter = Name of the filter to get statistics for.
+ * 2 : executions = Storage for the execution count.
+ * 3 : response_bodies_modified = Storage for the number
+ * of modified response bodies.
+ * 4 : hits = Storage for the number of hits.
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+void get_filter_statistics(const char *filter, unsigned long long *executions,
+ unsigned long long *response_bodies_modified,
+ unsigned long long *hits)
+{
+ struct filter_statistics_entry *entry;
+
+ privoxy_mutex_lock(&filter_statistics_mutex);
+
+ entry = filter_statistics;
+ while (entry != NULL)
+ {
+ if (!strcmp(entry->filter, filter))
+ {
+ *executions = entry->executions;
+ *response_bodies_modified = entry->response_bodies_modified;
+ *hits = entry->hits;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ privoxy_mutex_unlock(&filter_statistics_mutex);
+
+}
+
+
+struct block_statistics_entry
+{
+ char *block_reason;
+ unsigned long long count;
+
+ struct block_statistics_entry *next;
+};
+
+static struct block_statistics_entry *block_statistics = NULL;
+
+/*********************************************************************
+ *
+ * Function : register_block_reason_for_statistics
+ *
+ * Description : Registers a block reason so we can gather statistics
+ * for it unless the block reason has already been
+ * registered before.
+ *
+ * Parameters :
+ * 1 : block_reason = Block reason to register
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+void register_block_reason_for_statistics(const char *block_reason)
+{
+ struct block_statistics_entry *entry;
+
+ privoxy_mutex_lock(&block_statistics_mutex);
+
+ if (block_statistics == NULL)
+ {
+ block_statistics = zalloc_or_die(sizeof(struct block_statistics_entry));
+ entry = block_statistics;
+ entry->block_reason = strdup_or_die(block_reason);
+ privoxy_mutex_unlock(&block_statistics_mutex);
+ return;
+ }
+ entry = block_statistics;
+ while (entry != NULL)
+ {
+ if (!strcmp(entry->block_reason, block_reason))
+ {
+ /* Already registered, nothing to do. */
+ break;
+ }
+ if (entry->next == NULL)
+ {
+ entry->next = zalloc_or_die(sizeof(struct block_statistics_entry));
+ entry->next->block_reason = strdup_or_die(block_reason);
+ break;
+ }
+ entry = entry->next;
+ }
+
+ privoxy_mutex_unlock(&block_statistics_mutex);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : increment_block_reason_counter
+ *
+ * Description : Updates the counter for a block reason.
+ *
+ * Parameters :
+ * 1 : block_reason = Block reason to count
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+static void increment_block_reason_counter(const char *block_reason)
+{
+ struct block_statistics_entry *entry;
+
+ privoxy_mutex_lock(&block_statistics_mutex);
+
+ entry = block_statistics;
+ while (entry != NULL)
+ {
+ if (!strcmp(entry->block_reason, block_reason))
+ {
+ entry->count++;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ privoxy_mutex_unlock(&block_statistics_mutex);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_block_reason_count
+ *
+ * Description : Gets number of times a block reason was used.
+ *
+ * Parameters :
+ * 1 : block_reason = Block reason to get statistics for.
+ * 2 : count = Storage for the number of times the block
+ * reason was used.
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+void get_block_reason_count(const char *block_reason, unsigned long long *count)
+{
+ struct block_statistics_entry *entry;
+
+ privoxy_mutex_lock(&block_statistics_mutex);
+
+ entry = block_statistics;
+ while (entry != NULL)
+ {
+ if (!strcmp(entry->block_reason, block_reason))
+ {
+ *count = entry->count;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ privoxy_mutex_unlock(&block_statistics_mutex);
+
+}
+
+#endif /* def FEATURE_EXTENDED_STATISTICS */
/*
Local Variables: