-const char parsers_rcs[] = "$Id: parsers.c,v 1.105 2007/08/11 14:49:49 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.126 2008/05/03 16:40:45 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/parsers.c,v $
*
* Revisions :
* $Log: parsers.c,v $
+ * Revision 1.126 2008/05/03 16:40:45 fabiankeil
+ * Change content_filters_enabled()'s parameter from
+ * csp->action to action so it can be also used in the
+ * CGI code. Don't bother checking if there are filters
+ * loaded, as that's somewhat besides the point.
+ *
+ * Revision 1.125 2008/04/17 14:40:49 fabiankeil
+ * Provide get_http_time() with the buffer size so it doesn't
+ * have to blindly assume that the buffer is big enough.
+ *
+ * Revision 1.124 2008/04/16 16:38:21 fabiankeil
+ * Don't pass the whole csp structure to flush_socket()
+ * when it only needs a file descriptor and a buffer.
+ *
+ * Revision 1.123 2008/03/29 12:13:46 fabiankeil
+ * Remove send-wafer and send-vanilla-wafer actions.
+ *
+ * Revision 1.122 2008/03/28 15:13:39 fabiankeil
+ * Remove inspect-jpegs action.
+ *
+ * Revision 1.121 2008/01/05 21:37:03 fabiankeil
+ * Let client_range() also handle Request-Range headers
+ * which apparently are still supported by many servers.
+ *
+ * Revision 1.120 2008/01/04 17:43:45 fabiankeil
+ * Improve the warning messages that get logged if the action files
+ * "enable" filters but no filters of that type have been loaded.
+ *
+ * Revision 1.119 2007/12/28 18:32:51 fabiankeil
+ * In server_content_type():
+ * - Don't require leading white space when detecting image content types.
+ * - Change '... not replaced ...' message to sound less crazy if the text
+ * type actually is 'text/plain'.
+ * - Mark the 'text/plain == binary data' assumption for removal.
+ * - Remove a bunch of trailing white space.
+ *
+ * Revision 1.118 2007/12/28 16:56:35 fabiankeil
+ * Minor server_content_disposition() changes:
+ * - Don't regenerate the header name all lower-case.
+ * - Some white space fixes.
+ * - Remove useless log message in case of ENOMEM.
+ *
+ * Revision 1.117 2007/12/06 18:11:50 fabiankeil
+ * Garbage-collect the code to add a X-Forwarded-For
+ * header as it seems to be mostly used by accident.
+ *
+ * Revision 1.116 2007/12/01 13:04:22 fabiankeil
+ * Fix a crash on mingw32 with some Last Modified times in the future.
+ *
+ * Revision 1.115 2007/11/02 16:52:50 fabiankeil
+ * Remove a "can't happen" error block which, over
+ * time, mutated into a "guaranteed to happen" block.
+ *
+ * Revision 1.114 2007/10/19 16:56:26 fabiankeil
+ * - Downgrade "Buffer limit reached" message to LOG_LEVEL_INFO.
+ * - Use shiny new content_filters_enabled() in client_range().
+ *
+ * Revision 1.113 2007/10/10 17:29:57 fabiankeil
+ * I forgot about Poland.
+ *
+ * Revision 1.112 2007/10/09 16:38:40 fabiankeil
+ * Remove Range and If-Range headers if content filtering is enabled.
+ *
+ * Revision 1.111 2007/10/04 18:07:00 fabiankeil
+ * Move ACTION_VANILLA_WAFER handling from jcc's chat() into
+ * client_cookie_adder() to make sure send-vanilla-wafer can be
+ * controlled through tags (and thus regression-tested).
+ *
+ * Revision 1.110 2007/09/29 10:42:37 fabiankeil
+ * - Remove "scanning headers for" log message again.
+ * - Some more whitespace fixes.
+ *
+ * Revision 1.109 2007/09/08 14:25:48 fabiankeil
+ * Refactor client_referrer() and add conditional-forge parameter.
+ *
+ * Revision 1.108 2007/08/28 18:21:03 fabiankeil
+ * A bunch of whitespace fixes, pointy hat to me.
+ *
+ * Revision 1.107 2007/08/28 18:16:32 fabiankeil
+ * Fix possible memory corruption in server_http, make sure it's not
+ * executed for ordinary server headers and mark some problems for later.
+ *
+ * Revision 1.106 2007/08/18 14:30:32 fabiankeil
+ * Let content-type-overwrite{} honour force-text-mode again.
+ *
* Revision 1.105 2007/08/11 14:49:49 fabiankeil
* - Add prototpyes for the header parsers and make them static.
* - Comment out client_accept_encoding_adder() which isn't used right now.
static jb_err client_if_none_match (struct client_state *csp, char **header);
static jb_err crunch_client_header (struct client_state *csp, char **header);
static jb_err client_x_filter (struct client_state *csp, char **header);
+static jb_err client_range (struct client_state *csp, char **header);
static jb_err server_set_cookie (struct client_state *csp, char **header);
static jb_err server_content_type (struct client_state *csp, char **header);
static jb_err server_content_length (struct client_state *csp, char **header);
static jb_err server_content_disposition(struct client_state *csp, char **header);
static jb_err client_host_adder (struct client_state *csp);
-static jb_err client_cookie_adder (struct client_state *csp);
static jb_err client_xtra_adder (struct client_state *csp);
-static jb_err client_x_forwarded_adder(struct client_state *csp);
static jb_err connection_close_adder (struct client_state *csp);
+static jb_err create_forged_referrer(char **header, const char *hostport);
+static jb_err create_fake_referrer(char **header, const char *fake_referrer);
+static jb_err handle_conditional_hide_referrer_parameter(char **header,
+ const char *host, const int parameter_conditional_block);
+
const struct parsers client_patterns[] = {
{ "referer:", 8, client_referrer },
{ "user-agent:", 11, client_uagent },
{ "max-forwards:", 13, client_max_forwards },
{ "Accept-Language:", 16, client_accept_language },
{ "if-none-match:", 14, client_if_none_match },
+ { "Range:", 6, client_range },
+ { "Request-Range:", 14, client_range },
+ { "If-Range:", 9, client_range },
{ "X-Filter:", 9, client_x_filter },
{ "*", 0, crunch_client_header },
{ "*", 0, filter_header },
};
const struct parsers server_patterns[] = {
- { "HTTP", 4, server_http },
+ { "HTTP/", 5, server_http },
{ "set-cookie:", 11, server_set_cookie },
{ "connection:", 11, connection },
{ "Content-Type:", 13, server_content_type },
const add_header_func_ptr add_client_headers[] = {
client_host_adder,
- client_cookie_adder,
- client_x_forwarded_adder,
client_xtra_adder,
/* Temporarily disabled: client_accept_encoding_adder, */
connection_close_adder,
NULL
};
-
const add_header_func_ptr add_server_headers[] = {
connection_close_adder,
NULL
*
* Parameters :
* 1 : fd = file descriptor of the socket to read
- * 2 : csp = Current client state (buffers, headers, etc...)
+ * 2 : iob = The I/O buffer to flush, usually csp->iob.
*
* Returns : On success, the number of bytes written are returned (zero
* indicates nothing was written). On error, -1 is returned,
* file, the results are not portable.
*
*********************************************************************/
-int flush_socket(jb_socket fd, struct client_state *csp)
+int flush_socket(jb_socket fd, struct iob *iob)
{
- struct iob *iob = csp->iob;
int len = iob->eod - iob->cur;
if (len <= 0)
*/
if (need > csp->config->buffer_limit)
{
- log_error(LOG_LEVEL_ERROR, "Buffer limit reached while extending the buffer (iob)");
+ log_error(LOG_LEVEL_INFO, "Buffer limit reached while extending the buffer (iob)");
return JB_ERR_MEMORY;
}
* This is to protect the parsing of gzipped data,
* but it should(?) be valid for deflated data also.
*/
- log_error (LOG_LEVEL_ERROR, "Buffer too small decompressing iob");
+ log_error(LOG_LEVEL_ERROR, "Buffer too small decompressing iob");
return JB_ERR_COMPRESS;
}
|| (*cur++ != (char)0x8b)
|| (*cur++ != Z_DEFLATED))
{
- log_error (LOG_LEVEL_ERROR, "Invalid gzip header when decompressing");
+ log_error(LOG_LEVEL_ERROR, "Invalid gzip header when decompressing");
return JB_ERR_COMPRESS;
}
else
if (flags & 0xe0)
{
/* The gzip header has reserved bits set; bail out. */
- log_error (LOG_LEVEL_ERROR, "Invalid gzip header flags when decompressing");
+ log_error(LOG_LEVEL_ERROR, "Invalid gzip header flags when decompressing");
return JB_ERR_COMPRESS;
}
cur += 6;
* The number of bytes to skip should be positive
* and we'd like to stay in the buffer.
*/
- if((skip_bytes < 0) || (skip_bytes >= (csp->iob->eod - cur)))
+ if ((skip_bytes < 0) || (skip_bytes >= (csp->iob->eod - cur)))
{
- log_error (LOG_LEVEL_ERROR,
+ log_error(LOG_LEVEL_ERROR,
"Unreasonable amount of bytes to skip (%d). Stopping decompression",
skip_bytes);
return JB_ERR_COMPRESS;
}
- log_error (LOG_LEVEL_INFO,
+ log_error(LOG_LEVEL_INFO,
"Skipping %d bytes for gzip compression. Does this sound right?",
skip_bytes);
cur += skip_bytes;
* the buffer end, we were obviously tricked to skip
* too much.
*/
- log_error (LOG_LEVEL_ERROR,
+ log_error(LOG_LEVEL_ERROR,
"Malformed gzip header detected. Aborting decompression.");
return JB_ERR_COMPRESS;
}
* XXX: The debug level should be lowered
* before the next stable release.
*/
- log_error (LOG_LEVEL_INFO, "Decompressing deflated iob: %d", *cur);
+ log_error(LOG_LEVEL_INFO, "Decompressing deflated iob: %d", *cur);
/*
* In theory (that is, according to RFC 1950), deflate-compressed
* data should begin with a two-byte zlib header and have an
}
else
{
- log_error (LOG_LEVEL_ERROR,
+ log_error(LOG_LEVEL_ERROR,
"Unable to determine compression format for decompression");
return JB_ERR_COMPRESS;
}
*/
if (inflateInit2 (&zstr, -MAX_WBITS) != Z_OK)
{
- log_error (LOG_LEVEL_ERROR, "Error initializing decompression");
+ log_error(LOG_LEVEL_ERROR, "Error initializing decompression");
return JB_ERR_COMPRESS;
}
* We don't modify the existing iob yet, so in case there
* is error in decompression we can recover gracefully.
*/
- buf = zalloc (bufsize);
+ buf = zalloc(bufsize);
if (NULL == buf)
{
- log_error (LOG_LEVEL_ERROR, "Out of memory decompressing iob");
+ log_error(LOG_LEVEL_ERROR, "Out of memory decompressing iob");
return JB_ERR_MEMORY;
}
* Description : This (odd) routine will parse the csp->iob
*
* Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
+ * 1 : iob = The I/O buffer to parse, usually csp->iob.
*
* Returns : Any one of the following:
*
* a complete header line.
*
*********************************************************************/
-char *get_header(struct client_state *csp)
+char *get_header(struct iob *iob)
{
- struct iob *iob;
char *p, *q, *ret;
- iob = csp->iob;
if ((iob->cur == NULL)
|| ((p = strchr(iob->cur, '\n')) == NULL))
struct list_entry *h; /* Header */
jb_err err = JB_ERR_OK;
- log_error(LOG_LEVEL_HEADER, "scanning headers for: %s", csp->http->url);
-
for (h = csp->headers->first; (err == JB_ERR_OK) && (h != NULL) ; h = h->next)
{
/* Header crunch()ed in previous run? -> ignore */
* As a side effect it frees the space used by the original
* header lines.
*
+ * XXX: should be split to remove the first_run hack.
+ *
* Parameters :
* 1 : pats = list of patterns to match against headers
* 2 : more_headers = list of functions to add more
if (0 == found_filters)
{
- log_error(LOG_LEVEL_ERROR, "Unable to get current state of regex tagging.");
+ log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: "
+ "tagging enabled, but no taggers available.");
return(JB_ERR_OK);
}
if (0 == found_filters)
{
- log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
+ log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: "
+ "header filtering enabled, but no matching filters available.");
return(JB_ERR_OK);
}
/* RegEx failure */
log_error(LOG_LEVEL_ERROR, "Filtering \'%s\' with \'%s\' didn't work out: %s",
*header, b->name, pcrs_strerror(matches));
- if( newheader != NULL)
+ if (newheader != NULL)
{
log_error(LOG_LEVEL_ERROR, "Freeing what's left: %s", newheader);
freez(newheader);
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : crunch_server_header
if (!(csp->content_type & CT_TABOO))
{
- if ((strstr(*header, " text/") && !strstr(*header, "plain"))
+ /*
+ * XXX: The assumption that text/plain is a sign of
+ * binary data seems to be somewhat unreasonable nowadays
+ * and should be dropped after 3.0.8 is out.
+ */
+ if ((strstr(*header, "text/") && !strstr(*header, "plain"))
|| strstr(*header, "xml")
|| strstr(*header, "application/x-javascript"))
{
csp->content_type |= CT_TEXT;
}
- else if (strstr(*header, " image/gif"))
+ else if (strstr(*header, "image/gif"))
{
csp->content_type |= CT_GIF;
}
- else if (strstr(*header, " image/jpeg"))
- {
- csp->content_type |= CT_JPEG;
- }
}
/*
* Are we messing with the content type?
- */
+ */
if (csp->action->flags & ACTION_CONTENT_TYPE_OVERWRITE)
- {
+ {
/*
* Make sure the user doesn't accidently
* change the content type of binary documents.
- */
+ */
if ((csp->content_type & CT_TEXT) || (csp->action->flags & ACTION_FORCE_TEXT_MODE))
- {
+ {
freez(*header);
*header = strdup("Content-Type: ");
string_append(header, csp->action->string[ACTION_STRING_CONTENT_TYPE]);
if (header == NULL)
- {
+ {
log_error(LOG_LEVEL_HEADER, "Insufficient memory to replace Content-Type!");
return JB_ERR_MEMORY;
}
}
else
{
- log_error(LOG_LEVEL_HEADER, "%s not replaced. It doesn't look like text. "
- "Enable force-text-mode if you know what you're doing.", *header);
+ log_error(LOG_LEVEL_HEADER, "%s not replaced. "
+ "It doesn't look like a content type that should be filtered. "
+ "Enable force-text-mode if you know what you're doing.", *header);
}
- }
+ }
return JB_ERR_OK;
}
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : server_content_disposition
*
- * Description : If enabled, blocks or modifies the "content-disposition" header.
+ * Description : If enabled, blocks or modifies the "Content-Disposition" header.
* Called from `sed'.
*
* Parameters :
const char *newval;
/*
- * Are we messing with the content-disposition header?
+ * Are we messing with the Content-Disposition header?
*/
if ((csp->action->flags & ACTION_HIDE_CONTENT_DISPOSITION) == 0)
{
- /*Me tinks not*/
+ /* Me tinks not */
return JB_ERR_OK;
}
newval = csp->action->string[ACTION_STRING_CONTENT_DISPOSITION];
- if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
+ if ((newval == NULL) || (0 == strcmpic(newval, "block")))
{
/*
* Blocking content-disposition header
else
{
/*
- * Replacing content-disposition header
+ * Replacing Content-Disposition header
*/
freez(*header);
- *header = strdup("content-disposition: ");
- string_append(header, newval);
+ *header = strdup("Content-Disposition: ");
+ string_append(header, newval);
- if (*header == NULL)
+ if (*header != NULL)
{
- log_error(LOG_LEVEL_HEADER, "Insufficent memory. content-disposition header not fully replaced.");
- }
- else
- {
- log_error(LOG_LEVEL_HEADER, "content-disposition header crunched and replaced with: %s", *header);
+ log_error(LOG_LEVEL_HEADER,
+ "Content-Disposition header crunched and replaced with: %s", *header);
}
}
return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : server_last_modified
/*
* Setting Last-Modified Header to now.
*/
- get_http_time(0, buf);
+ get_http_time(0, buf, sizeof(buf));
freez(*header);
*header = strdup("Last-Modified: ");
string_append(header, buf);
rtime = (long int)difftime(now, last_modified);
if (rtime)
{
+ int negative = 0;
+
+ if (rtime < 0)
+ {
+ rtime *= -1;
+ negative = 1;
+ log_error(LOG_LEVEL_HEADER, "Server time in the future.");
+ }
rtime = pick_from_range(rtime);
+ if (negative) rtime *= -1;
last_modified += rtime;
#ifdef HAVE_GMTIME_R
timeptr = gmtime_r(&last_modified, &gmt);
return JB_ERR_MEMORY;
}
- if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
+ if (LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
{
days = rtime / (3600 * 24);
hours = rtime / 3600 % 24;
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : client_referrer
*********************************************************************/
static jb_err client_referrer(struct client_state *csp, char **header)
{
- const char *newval;
- const char *host;
- char *referer;
- size_t hostlenght;
+ const char *parameter;
+ /* booleans for parameters we have to check multiple times */
+ int parameter_conditional_block;
+ int parameter_conditional_forge;
#ifdef FEATURE_FORCE_LOAD
- /* Since the referrer can include the prefix even
+ /*
+ * Since the referrer can include the prefix even
* if the request itself is non-forced, we must
- * clean it unconditionally
+ * clean it unconditionally.
+ *
+ * XXX: strclean is too broad
*/
strclean(*header, FORCE_PREFIX);
#endif /* def FEATURE_FORCE_LOAD */
- /*
- * Are we sending referer?
- */
if ((csp->action->flags & ACTION_HIDE_REFERER) == 0)
{
+ /* Nothing left to do */
return JB_ERR_OK;
}
- newval = csp->action->string[ACTION_STRING_REFERER];
+ parameter = csp->action->string[ACTION_STRING_REFERER];
+ assert(parameter != NULL);
+ parameter_conditional_block = (0 == strcmpic(parameter, "conditional-block"));
+ parameter_conditional_forge = (0 == strcmpic(parameter, "conditional-forge"));
- if ((0 != strcmpic(newval, "conditional-block")))
- {
- freez(*header);
- }
- if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
+ if (!parameter_conditional_block && !parameter_conditional_forge)
{
/*
- * Blocking referer
+ * As conditional-block and conditional-forge are the only
+ * parameters that rely on the original referrer, we can
+ * remove it now for all the others.
*/
+ freez(*header);
+ }
+
+ if (0 == strcmpic(parameter, "block"))
+ {
log_error(LOG_LEVEL_HEADER, "Referer crunched!");
return JB_ERR_OK;
}
- else if (0 == strcmpic(newval, "conditional-block"))
+ else if (parameter_conditional_block || parameter_conditional_forge)
{
- /*
- * Block referer if host has changed.
- */
-
- if (NULL == (host = strdup(csp->http->hostport)))
- {
- freez(*header);
- log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary host copy.");
- return JB_ERR_MEMORY;
- }
- if (NULL == (referer = strdup(*header)))
- {
- freez(*header);
- freez(host);
- log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary referer copy.");
- return JB_ERR_MEMORY;
- }
- hostlenght = strlen(host);
- if ( hostlenght < (strlen(referer)-17) ) /*referer begins with 'Referer: http[s]://'*/
- {
- /*Shorten referer to make sure the referer is blocked
- *if www.example.org/www.example.com-shall-see-the-referer/
- *links to www.example.com/
- */
- referer[hostlenght+17] = '\0';
- }
- if ( 0 == strstr(referer, host)) /*Host has changed*/
- {
- log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header);
- freez(*header);
- }
- else
- {
- log_error(LOG_LEVEL_HEADER, "%s (not modified, still on %s)", *header, host);
- }
- freez(referer);
- freez(host);
- return JB_ERR_OK;
+ return handle_conditional_hide_referrer_parameter(header,
+ csp->http->hostport, parameter_conditional_block);
}
- else if (0 != strcmpic(newval, "forge"))
+ else if (0 == strcmpic(parameter, "forge"))
{
- /*
- * We have a specific (fixed) referer we want to send.
- */
- if ((0 != strncmpic(newval, "http://", 7)) && (0 != strncmpic(newval, "https://", 8)))
- {
- log_error(LOG_LEVEL_HEADER, "Parameter: +referrer{%s} is a bad idea, but I don't care.", newval);
- }
- *header = strdup("Referer: ");
- string_append(header, newval);
- log_error(LOG_LEVEL_HEADER, "Referer overwritten with: %s", *header);
-
- return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
+ return create_forged_referrer(header, csp->http->hostport);
}
else
{
- /*
- * Forge a referer as http://[hostname:port of REQUEST]/
- * to fool stupid checks for in-site links
- */
-
- *header = strdup("Referer: http://");
- string_append(header, csp->http->hostport);
- string_append(header, "/");
- log_error(LOG_LEVEL_HEADER, "Referer forged to: %s", *header);
-
- return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
+ /* interpret parameter as user-supplied referer to fake */
+ return create_fake_referrer(header, parameter);
}
}
+
/*********************************************************************
*
* Function : client_accept_language
return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : client_ua
*********************************************************************/
jb_err client_x_forwarded(struct client_state *csp, char **header)
{
- if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0)
- {
- /* Save it so we can re-add it later */
- freez(csp->x_forwarded);
- csp->x_forwarded = *header;
-
- /*
- * Always set *header = NULL, since this information
- * will be sent at the end of the header.
- */
- *header = NULL;
- }
- else
+ if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
{
freez(*header);
log_error(LOG_LEVEL_HEADER, "crunched x-forwarded-for!");
log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header);
freez(*header);
}
- else
- {
- /*
- * Not supposed to be reached. direct_response() which
- * was already called earlier in chat() should have
- * intercepted the request.
- */
- log_error(LOG_LEVEL_ERROR,
- "Non-intercepted %s request with Max-Forwards zero!", csp->http->gpc);
- assert(max_forwards != 0);
- }
}
else
{
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : client_if_modified_since
else
{
rtime = strtol(newval, &endptr, 0);
- if(rtime)
+ if (rtime)
{
log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d minut%s)",
*header, rtime, (rtime == 1 || rtime == -1) ? "e": "es");
- if(rtime < 0)
+ if (rtime < 0)
{
rtime *= -1;
negative = 1;
return JB_ERR_MEMORY;
}
- if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
+ if (LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
{
hours = rtime / 3600;
minutes = rtime / 60 % 60;
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : client_if_none_match
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : client_x_filter
return JB_ERR_OK;
}
+
+/*********************************************************************
+ *
+ * Function : client_range
+ *
+ * Description : Removes Range, Request-Range and If-Range headers if
+ * content filtering is enabled. If the client's version
+ * of the document has been altered by Privoxy, the server
+ * could interpret the range differently than the client
+ * intended in which case the user could end up with
+ * corrupted content.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : header = On input, pointer to header to modify.
+ * On output, pointer to the modified header, or NULL
+ * to remove the header. This function frees the
+ * original string if necessary.
+ *
+ * Returns : JB_ERR_OK
+ *
+ *********************************************************************/
+static jb_err client_range(struct client_state *csp, char **header)
+{
+ if (content_filters_enabled(csp->action))
+ {
+ log_error(LOG_LEVEL_HEADER, "Content filtering is enabled."
+ " Crunching: \'%s\' to prevent range-mismatch problems.", *header);
+ freez(*header);
+ }
+
+ return JB_ERR_OK;
+}
+
/* the following functions add headers directly to the header list */
/*********************************************************************
}
-/*********************************************************************
- *
- * Function : client_cookie_adder
- *
- * Description : Used in the add_client_headers list to add "wafers".
- * Called from `sed'.
- *
- * Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
- *
- * Returns : JB_ERR_OK on success, or
- * JB_ERR_MEMORY on out-of-memory error.
- *
- *********************************************************************/
-jb_err client_cookie_adder(struct client_state *csp)
-{
- char *tmp;
- struct list_entry *wafer;
- struct list_entry *wafer_list = csp->action->multi[ACTION_MULTI_WAFER]->first;
- jb_err err;
-
- if (NULL == wafer_list)
- {
- /* Nothing to do */
- return JB_ERR_OK;
- }
-
- tmp = strdup("Cookie: ");
-
- for (wafer = wafer_list; (NULL != tmp) && (NULL != wafer); wafer = wafer->next)
- {
- if (wafer != wafer_list)
- {
- /* As this isn't the first wafer, we need a delimiter. */
- string_append(&tmp, "; ");
- }
- string_join(&tmp, cookie_encode(wafer->str));
- }
-
- if (tmp == NULL)
- {
- return JB_ERR_MEMORY;
- }
-
- log_error(LOG_LEVEL_HEADER, "addh: %s", tmp);
- err = enlist(csp->headers, tmp);
- free(tmp);
- return err;
-}
-
#if 0
/*********************************************************************
*
}
#endif
+
/*********************************************************************
*
* Function : client_xtra_adder
}
-/*********************************************************************
- *
- * Function : client_x_forwarded_adder
- *
- * Description : Used in the add_client_headers list. Called from `sed'.
- *
- * Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
- *
- * Returns : JB_ERR_OK on success, or
- * JB_ERR_MEMORY on out-of-memory error.
- *
- *********************************************************************/
-static jb_err client_x_forwarded_adder(struct client_state *csp)
-{
- char *p = NULL;
- jb_err err;
-
- if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
- {
- return JB_ERR_OK;
- }
-
- if (csp->x_forwarded)
- {
- p = strdup(csp->x_forwarded);
- string_append(&p, ", ");
- }
- else
- {
- p = strdup("X-Forwarded-For: ");
- }
- string_append(&p, csp->ip_addr_str);
-
- if (p == NULL)
- {
- return JB_ERR_MEMORY;
- }
-
- log_error(LOG_LEVEL_HEADER, "addh: %s", p);
- err = enlist(csp->headers, p);
- free(p);
-
- return err;
-}
-
-
/*********************************************************************
*
* Function : connection_close_adder
if ((csp->action->flags & ACTION_DOWNGRADE) != 0)
{
- (*header)[7] = '0';
- log_error(LOG_LEVEL_HEADER, "Downgraded answer to HTTP/1.0");
+ /* XXX: Should we do a real validity check here? */
+ if (strlen(*header) > 8)
+ {
+ (*header)[7] = '0';
+ log_error(LOG_LEVEL_HEADER, "Downgraded answer to HTTP/1.0");
+ }
+ else
+ {
+ /*
+ * XXX: Should we block the request or
+ * enlist a valid status code line here?
+ */
+ log_error(LOG_LEVEL_INFO, "Malformed server response detected. "
+ "Downgrading to HTTP/1.0 impossible.");
+ }
}
return JB_ERR_OK;
}
#endif /* def FEATURE_FORCE_LOAD */
+
/*********************************************************************
*
* Function : parse_header_time
}
+
/*********************************************************************
*
* Function : get_destination_from_headers
}
+/*********************************************************************
+ *
+ * Function : create_forged_referrer
+ *
+ * Description : Helper for client_referrer to forge a referer as
+ * 'http://[hostname:port/' to fool stupid
+ * checks for in-site links
+ *
+ * Parameters :
+ * 1 : header = Pointer to header pointer
+ * 2 : hostport = Host and optionally port as string
+ *
+ * Returns : JB_ERR_OK in case of success, or
+ * JB_ERR_MEMORY in case of memory problems.
+ *
+ *********************************************************************/
+static jb_err create_forged_referrer(char **header, const char *hostport)
+{
+ assert(NULL == *header);
+
+ *header = strdup("Referer: http://");
+ string_append(header, hostport);
+ string_append(header, "/");
+
+ if (NULL == *header)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ log_error(LOG_LEVEL_HEADER, "Referer forged to: %s", *header);
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : create_fake_referrer
+ *
+ * Description : Helper for client_referrer to create a fake referrer
+ * based on a string supplied by the user.
+ *
+ * Parameters :
+ * 1 : header = Pointer to header pointer
+ * 2 : hosthost = Referrer to fake
+ *
+ * Returns : JB_ERR_OK in case of success, or
+ * JB_ERR_MEMORY in case of memory problems.
+ *
+ *********************************************************************/
+static jb_err create_fake_referrer(char **header, const char *fake_referrer)
+{
+ assert(NULL == *header);
+
+ if ((0 != strncmpic(fake_referrer, "http://", 7)) && (0 != strncmpic(fake_referrer, "https://", 8)))
+ {
+ log_error(LOG_LEVEL_HEADER,
+ "Parameter: +hide-referrer{%s} is a bad idea, but I don't care.", fake_referrer);
+ }
+ *header = strdup("Referer: ");
+ string_append(header, fake_referrer);
+
+ if (NULL == *header)
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ log_error(LOG_LEVEL_HEADER, "Referer replaced with: %s", *header);
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : handle_conditional_hide_referrer_parameter
+ *
+ * Description : Helper for client_referrer to crunch or forge
+ * the referrer header if the host has changed.
+ *
+ * Parameters :
+ * 1 : header = Pointer to header pointer
+ * 2 : host = The target host (may include the port)
+ * 3 : parameter_conditional_block = Boolean to signal
+ * if we're in conditional-block mode. If not set,
+ * we're in conditional-forge mode.
+ *
+ * Returns : JB_ERR_OK in case of success, or
+ * JB_ERR_MEMORY in case of memory problems.
+ *
+ *********************************************************************/
+static jb_err handle_conditional_hide_referrer_parameter(char **header,
+ const char *host, const int parameter_conditional_block)
+{
+ char *referer = strdup(*header);
+ const size_t hostlenght = strlen(host);
+
+ if (NULL == referer)
+ {
+ freez(*header);
+ return JB_ERR_MEMORY;
+ }
+
+ /* referer begins with 'Referer: http[s]://' */
+ if (hostlenght < (strlen(referer)-17))
+ {
+ /*
+ * Shorten referer to make sure the referer is blocked
+ * if www.example.org/www.example.com-shall-see-the-referer/
+ * links to www.example.com/
+ */
+ referer[hostlenght+17] = '\0';
+ }
+ if (NULL == strstr(referer, host))
+ {
+ /* Host has changed */
+ if (parameter_conditional_block)
+ {
+ log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header);
+ freez(*header);
+ }
+ else
+ {
+ freez(*header);
+ freez(referer);
+ return create_forged_referrer(header, host);
+ }
+ }
+ freez(referer);
+
+ return JB_ERR_OK;
+
+}
+
/*
Local Variables:
tab-width: 3