From: Fabian Keil Date: Fri, 27 Jul 2012 17:36:06 +0000 (+0000) Subject: Implement the client-header-order directive X-Git-Tag: v_3_0_20~297 X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=commitdiff_plain;h=948738eaf2189c352d181e81c0cd13d3973fdebf Implement the client-header-order directive It can be used to forward client headers in a different order than they arrived. --- diff --git a/doc/source/p-config.sgml b/doc/source/p-config.sgml index 8e171f1a..c8d2c8da 100644 --- a/doc/source/p-config.sgml +++ b/doc/source/p-config.sgml @@ -3,7 +3,7 @@ Purpose : Used with other docs and files only. - $Id: p-config.sgml,v 2.80 2012/03/19 12:56:26 fabiankeil Exp $ + $Id: p-config.sgml,v 2.81 2012/03/19 12:56:41 fabiankeil Exp $ Copyright (C) 2001-2011 Privoxy Developers http://www.privoxy.org/ See LICENSE. @@ -97,7 +97,7 @@ Sample Configuration File for Privoxy v&p-version; - $Id: p-config.sgml,v 2.80 2012/03/19 12:56:26 fabiankeil Exp $ + $Id: p-config.sgml,v 2.81 2012/03/19 12:56:41 fabiankeil Exp $ Copyright (C) 2001-2011 Privoxy Developers http://www.privoxy.org/ @@ -3120,6 +3120,70 @@ forward-socks4, forward-socks4a and forward-socks5 +client-header-order + + + Specifies: + + + The order in which client headers are sorted before forwarding them. + + + + + Type of value: + + + Client header names delimited by spaces or tabs + + + + + Default value: + + None + + + + Notes: + + + By default &my-app; leaves the client headers in the order they + were sent by the client. Headers are modified in-place, new headers + are added at the end of the already existing headers. + + + The header order can be used to fingerprint client requests + independently of other headers like the User-Agent. + + + This directive allows to sort the headers differently to better + mimic a different User-Agent. Client headers will be emitted + in the order given, headers whose name isn't explicitly specified + are added at the end. + + + Note that sorting headers in an uncommon way will make fingerprinting + actually easier. Encrypted headers are not affected by this directive. + + + + +@@#client-header-order Host \ +# User-Agent \ +# Accept \ +# Accept-Language \ +# Accept-Encoding \ +# Proxy-Connection,\ +# Referer,Cookie \ +# If-Modified-Since \ +# Cache-Control \ +# Content-Length \ +# Content-Type +]]> + + + diff --git a/loadcfg.c b/loadcfg.c index 6c329ec9..9a7a52d7 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -1,4 +1,4 @@ -const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.128 2012/05/24 14:58:16 fabiankeil Exp $"; +const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.129 2012/06/08 15:15:11 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $ @@ -123,6 +123,7 @@ static struct file_list *current_configfile = NULL; #define hash_admin_address 4112573064U /* "admin-address" */ #define hash_allow_cgi_request_crunching 258915987U /* "allow-cgi-request-crunching" */ #define hash_buffer_limit 1881726070U /* "buffer-limit */ +#define hash_client_header_order 2701453514U /* "client-header-order" */ #define hash_compression_level 2464423563U /* "compression-level" */ #define hash_confdir 1978389U /* "confdir" */ #define hash_connection_sharing 1348841265U /* "connection-sharing" */ @@ -233,6 +234,8 @@ static void unload_configfile (void * data) freez(config->re_filterfile[i]); } + list_remove_all(config->ordered_client_headers); + freez(config->admin_address); freez(config->proxy_info_url); freez(config->proxy_args); @@ -316,6 +319,75 @@ static int parse_toggle_state(const char *name, const char *value) } +/********************************************************************* + * + * Function : parse_client_header_order + * + * Description : Parse the value of the header-order directive + * + * Parameters : + * 1 : ordered_header_list: List to insert the ordered + * headers into. + * 2 : ordered_headers: The ordered header names separated + * by spaces or tabs. + * + * + * Returns : N/A + * + *********************************************************************/ +static void parse_client_header_order(struct list *ordered_header_list, const char *ordered_headers) +{ + char *original_headers_copy; + char **vector; + int number_of_headers; + int i; + + assert(ordered_header_list != NULL); + assert(ordered_headers != NULL); + + if (ordered_headers == NULL) + { + log_error(LOG_LEVEL_FATAL, "header-order used without argument"); + } + + /* + * XXX: This estimate is guaranteed to be high enough as we + * let ssplit() ignore empty fields, but also a bit wasteful. + * The same hack is used in get_last_url() so it looks like + * a real solution is needed. + */ + size_t max_segments = strlen(ordered_headers) / 2; + if (max_segments == 0) + { + max_segments = 1; + } + vector = malloc_or_die(max_segments * sizeof(char *)); + + original_headers_copy = strdup_or_die(ordered_headers); + + number_of_headers = ssplit(original_headers_copy, "\t ", vector, max_segments); + if (number_of_headers == -1) + { + log_error(LOG_LEVEL_FATAL, "Failed to split ordered headers"); + } + + for (i = 0; i < number_of_headers; i++) + { + if (JB_ERR_OK != enlist(ordered_header_list, vector[i])) + { + log_error(LOG_LEVEL_FATAL, + "Failed to enlist ordered header: %s", vector[i]); + } + } + + freez(vector); + freez(original_headers_copy); + + return; + +} + + /********************************************************************* * * Function : load_config @@ -539,6 +611,14 @@ struct configuration_spec * load_config(void) config->buffer_limit = (size_t)(1024 * atoi(arg)); break; +/* ************************************************************************* + * client-header-order header-1 header-2 ... header-n + * *************************************************************************/ + case hash_client_header_order: + list_remove_all(config->ordered_client_headers); + parse_client_header_order(config->ordered_client_headers, arg); + break; + /* ************************************************************************* * confdir directory-name * *************************************************************************/ diff --git a/parsers.c b/parsers.c index e29c451d..09a12e4b 100644 --- a/parsers.c +++ b/parsers.c @@ -1,4 +1,4 @@ -const char parsers_rcs[] = "$Id: parsers.c,v 1.245 2012/04/06 15:17:10 fabiankeil Exp $"; +const char parsers_rcs[] = "$Id: parsers.c,v 1.246 2012/07/23 12:40:30 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/parsers.c,v $ @@ -1025,6 +1025,79 @@ static jb_err scan_headers(struct client_state *csp) } +/********************************************************************* + * + * Function : enforce_header_order + * + * Description : Enforces a given header order. + * + * Parameters : + * 1 : headers = List of headers to order. + * 2 : ordered_headers = List of ordered header names. + * + * Returns : N/A + * + *********************************************************************/ +static void enforce_header_order(struct list *headers, const struct list *ordered_headers) +{ + struct list_entry *sorted_header; + struct list new_headers[1]; + struct list_entry *header; + + init_list(new_headers); + + /* The request line is always the first "header" */ + + assert(NULL != headers->first->str); + enlist(new_headers, headers->first->str); + freez(headers->first->str) + + /* Enlist the specified headers in the given order */ + + for (sorted_header = ordered_headers->first; sorted_header != NULL; + sorted_header = sorted_header->next) + { + const size_t sorted_header_length = strlen(sorted_header->str); + for (header = headers->first; header != NULL; header = header->next) + { + /* Header enlisted in previous run? -> ignore */ + if (header->str == NULL) continue; + + if (0 == strncmpic(sorted_header->str, header->str, sorted_header_length) + && (header->str[sorted_header_length] == ':')) + { + log_error(LOG_LEVEL_HEADER, "Enlisting sorted header %s", header->str); + if (JB_ERR_OK != enlist(new_headers, header->str)) + { + log_error(LOG_LEVEL_HEADER, "Failed to enlist %s", header->str); + } + freez(header->str); + } + } + } + + /* Enlist the rest of the headers behind the ordered ones */ + for (header = headers->first; header != NULL; header = header->next) + { + /* Header enlisted in previous run? -> ignore */ + if (header->str == NULL) continue; + + log_error(LOG_LEVEL_HEADER, + "Enlisting left-over header %s", header->str); + if (JB_ERR_OK != enlist(new_headers, header->str)) + { + log_error(LOG_LEVEL_HEADER, "Failed to enlist %s", header->str); + } + freez(header->str); + } + + list_remove_all(headers); + list_duplicate(headers, new_headers); + list_remove_all(new_headers); + + return; +} + /********************************************************************* * * Function : sed @@ -1091,6 +1164,11 @@ jb_err sed(struct client_state *csp, int filter_server_headers) f++; } + if (!filter_server_headers && !list_is_empty(csp->config->ordered_client_headers)) + { + enforce_header_order(csp->headers, csp->config->ordered_client_headers); + } + return err; } diff --git a/project.h b/project.h index 398dc1a9..98c46af0 100644 --- a/project.h +++ b/project.h @@ -1,7 +1,7 @@ #ifndef PROJECT_H_INCLUDED #define PROJECT_H_INCLUDED /** Version string. */ -#define PROJECT_H_VERSION "$Id: project.h,v 1.176 2012/06/19 12:50:22 fabiankeil Exp $" +#define PROJECT_H_VERSION "$Id: project.h,v 1.177 2012/07/23 12:43:42 fabiankeil Exp $" /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/project.h,v $ @@ -1261,6 +1261,9 @@ struct configuration_spec /** The short names of the pcre filter files. */ const char *re_filterfile_short[MAX_AF_FILES]; + /**< List of ordered client header names. */ + struct list ordered_client_headers[1]; + /** The hostname to show on CGI pages, or NULL to use the real one. */ const char *hostname;