-const char parsers_rcs[] = "$Id: parsers.c,v 1.90 2007/02/08 19:12:35 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.93 2007/03/20 15:21:44 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/parsers.c,v $
* `client_uagent', `client_x_forwarded',
* `client_x_forwarded_adder', `client_xtra_adder',
* `content_type', `crumble', `destroy_list', `enlist',
- * `flush_socket', ``get_header', `sed', `filter_server_header'
- * `filter_client_header', `filter_header', `crunch_server_header',
+ * `flush_socket', ``get_header', `sed', `filter_header'
* `server_content_encoding', `server_content_disposition',
* `server_last_modified', `client_accept_language',
* `crunch_client_header', `client_if_modified_since',
* `client_if_none_match', `get_destination_from_headers',
- * `parse_header_time' and `server_set_cookie'.
+ * `parse_header_time', `decompress_iob' and `server_set_cookie'.
*
* Copyright : Written by and Copyright (C) 2001-2007 the SourceForge
* Privoxy team. http://www.privoxy.org/
*
* Revisions :
* $Log: parsers.c,v $
+ * Revision 1.93 2007/03/20 15:21:44 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".
+ * - Remove filter_client_header() and filter_client_header(),
+ * filter_header() now checks the shiny new
+ * CSP_FLAG_CLIENT_HEADER_PARSING_DONE flag instead.
+ *
+ * Revision 1.92 2007/03/05 13:25:32 fabiankeil
+ * - Cosmetical changes for LOG_LEVEL_RE_FILTER messages.
+ * - Handle "Cookie:" and "Connection:" headers a bit smarter
+ * (don't crunch them just to recreate them later on).
+ * - Add another non-standard time format for the cookie
+ * expiration date detection.
+ * - Fix a valgrind warning.
+ *
+ * Revision 1.91 2007/02/24 12:27:32 fabiankeil
+ * Improve cookie expiration date detection.
+ *
* Revision 1.90 2007/02/08 19:12:35 fabiankeil
* Don't run server_content_length() the first time
* sed() parses server headers; only adjust the
{ "Host:", 5, client_host },
{ "if-modified-since:", 18, client_if_modified_since },
{ "Keep-Alive:", 11, crumble },
- { "connection:", 11, crumble },
+ { "connection:", 11, connection },
{ "proxy-connection:", 17, crumble },
{ "max-forwards:", 13, client_max_forwards },
{ "Accept-Language:", 16, client_accept_language },
{ "if-none-match:", 14, client_if_none_match },
{ "X-Filter:", 9, client_x_filter },
{ "*", 0, crunch_client_header },
- { "*", 0, filter_client_header },
+ { "*", 0, filter_header },
{ NULL, 0, NULL }
};
const struct parsers server_patterns[] = {
{ "HTTP", 4, server_http },
{ "set-cookie:", 11, server_set_cookie },
- { "connection:", 11, crumble },
+ { "connection:", 11, connection },
{ "Content-Type:", 13, server_content_type },
{ "Content-MD5:", 12, server_content_md5 },
{ "Content-Encoding:", 17, server_content_encoding },
{ "content-disposition:", 20, server_content_disposition },
{ "Last-Modified:", 14, server_last_modified },
{ "*", 0, crunch_server_header },
- { "*", 0, filter_server_header },
+ { "*", 0, filter_header },
{ NULL, 0, NULL }
};
char *cur; /* Current iob position (to keep the original
* iob->cur unmodified if we return early) */
size_t bufsize; /* allocated size of the new buffer */
+ size_t old_size; /* Content size before decompression */
size_t skip_size; /* Number of bytes at the beginning of the iob
that we should NOT decompress. */
int status; /* return status of the inflate() call */
z_stream zstr; /* used by calls to zlib */
+ assert(csp->iob->cur - csp->iob->buf > 0);
+ assert(csp->iob->eod - csp->iob->cur > 0);
+
bufsize = csp->iob->size;
skip_size = (size_t)(csp->iob->cur - csp->iob->buf);
+ old_size = (size_t)(csp->iob->eod - csp->iob->cur);
cur = csp->iob->cur;
skip_bytes = *cur++;
skip_bytes = *cur++ << 8;
- assert(skip_bytes == *csp->iob->cur-2 + ((*csp->iob->cur-1) << 8));
+ assert(skip_bytes == *csp->iob->cur - 2 + ((*csp->iob->cur - 1) << 8));
/*
* The number of bytes to skip should be positive
/* Skip the filename if necessary. */
if (flags & 0x08)
{
- /* A null-terminated string follows. */
- while (*cur++);
+ /* A null-terminated string is supposed to follow. */
+ while (*cur++ && (cur < csp->iob->eod));
+
}
/* Skip the comment if necessary. */
if (flags & 0x10)
{
- while (*cur++);
+ /* A null-terminated string is supposed to follow. */
+ while (*cur++ && (cur < csp->iob->eod));
}
/* Skip the CRC if necessary. */
{
cur += 2;
}
+
+ if (cur >= csp->iob->eod)
+ {
+ /*
+ * If the current position pointer reached or passed
+ * the buffer end, we were obviously tricked to skip
+ * too much.
+ */
+ log_error (LOG_LEVEL_ERROR,
+ "Malformed gzip header detected. Aborting decompression.");
+ return JB_ERR_COMPRESS;
+ }
}
}
else if (csp->content_type & CT_DEFLATE)
&& (csp->iob->cur <= csp->iob->eod)
&& (csp->iob->eod <= csp->iob->buf + csp->iob->size))
{
- char t = csp->iob->cur[100];
- csp->iob->cur[100] = '\0';
- /*
- * XXX: The debug level should be lowered
- * before the next stable release.
- */
- log_error(LOG_LEVEL_INFO, "Sucessfully decompressed: %s", csp->iob->cur);
- csp->iob->cur[100] = t;
- return JB_ERR_OK;
+ const size_t new_size = (size_t)(csp->iob->eod - csp->iob->cur);
+ if (new_size > 0)
+ {
+ log_error(LOG_LEVEL_RE_FILTER,
+ "Decompression successful. Old size: %d, new size: %d.",
+ old_size, new_size);
+ }
+ else
+ {
+ /* zlib thinks this is OK, so lets do the same. */
+ log_error(LOG_LEVEL_INFO, "Decompression didn't result in any content.");
+ }
}
else
{
return JB_ERR_COMPRESS;
}
+ return JB_ERR_OK;
+
}
#endif /* defined(FEATURE_ZLIB) */
/* here begins the family of parser functions that reformat header lines */
-/*********************************************************************
- *
- * Function : filter_server_header
- *
- * Description : Checks if server header filtering is enabled.
- * If it is, filter_header is called to do the work.
- *
- * 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 on success and always succeeds
- *
- *********************************************************************/
-jb_err filter_server_header(struct client_state *csp, char **header)
-{
- if (csp->action->flags & ACTION_FILTER_SERVER_HEADERS)
- {
- filter_header(csp, header);
- }
- return(JB_ERR_OK);
-}
-
-/*********************************************************************
- *
- * Function : filter_client_header
- *
- * Description : Checks if client header filtering is enabled.
- * If it is, filter_header is called to do the work.
- *
- * 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 on success and always succeeds
- *
- *********************************************************************/
-jb_err filter_client_header(struct client_state *csp, char **header)
-{
- if (csp->action->flags & ACTION_FILTER_CLIENT_HEADERS)
- {
- filter_header(csp, header);
- }
- return(JB_ERR_OK);
-}
-
/*********************************************************************
*
* Function : filter_header
*
* Description : Executes all text substitutions from all applying
- * +filter actions on the header.
+ * +(server|client)-header-filter actions on the header.
* Most of the code was copied from pcrs_filter_response,
* including the rather short variable names
*
struct list_entry *filtername;
int i, found_filters = 0;
+ int wanted_filter_type;
+ int multi_action_index;
+
+ if (csp->flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE)
+ {
+ wanted_filter_type = FT_SERVER_HEADER_FILTER;
+ multi_action_index = ACTION_MULTI_SERVER_HEADER_FILTER;
+ }
+ else
+ {
+ wanted_filter_type = FT_CLIENT_HEADER_FILTER;
+ multi_action_index = ACTION_MULTI_CLIENT_HEADER_FILTER;
+ }
/*
* Need to check the set of re_filterfiles...
*/
for (b = fl->f; b; b = b->next)
{
- for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
+ if (b->type != wanted_filter_type)
+ {
+ /* Skip other filter types */
+ continue;
+ }
+
+ for (filtername = csp->action->multi[multi_action_index]->first;
filtername ; filtername = filtername->next)
{
if (strcmp(b->name, filtername->str) == 0)
continue;
}
- log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s (size %d) with filter %s...",
+ log_error(LOG_LEVEL_RE_FILTER, "filtering \'%s\' (size %d) with \'%s\' ...",
*header, size, b->name);
/* Apply all jobs from the joblist */
}
}
}
- log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size);
+ log_error(LOG_LEVEL_RE_FILTER, "... produced %d hits (new size %d).", current_hits, size);
hits += current_hits;
}
}
}
+/*********************************************************************
+ *
+ * Function : connection
+ *
+ * Description : Makes sure that the value of the Connection: header
+ * is "close" and signals connection_close_adder
+ * to do nothing.
+ *
+ * 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 on success, or
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err connection(struct client_state *csp, char **header)
+{
+ char *old_header = *header;
+
+ /* Do we have a 'Connection: close' header? */
+ if (strcmpic(*header, "Connection: close"))
+ {
+ /* No, create one */
+ *header = strdup("Connection: close");
+ if (header == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+ log_error(LOG_LEVEL_HEADER, "Replaced: \'%s\' with \'%s\'", old_header, *header);
+ freez(old_header);
+ }
+
+ /* Signal connection_close_adder() to return early. */
+ if (csp->flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE)
+ {
+ csp->flags |= CSP_FLAG_SERVER_CONNECTION_CLOSE_SET;
+ }
+ else
+ {
+ csp->flags |= CSP_FLAG_CLIENT_CONNECTION_CLOSE_SET;
+ }
+
+ return JB_ERR_OK;
+}
+
+
/*********************************************************************
*
* Function : crumble
jb_err crunch_server_header(struct client_state *csp, char **header)
{
const char *crunch_pattern;
- /*Is there a header to crunch*/
+ /* Do we feel like crunching? */
if ((csp->action->flags & ACTION_CRUNCH_SERVER_HEADER))
{
crunch_pattern = csp->action->string[ACTION_STRING_SERVER_HEADER];
- /*Is the current header the lucky one?*/
+ /* Is the current header the lucky one? */
if (strstr(*header, crunch_pattern))
{
log_error(LOG_LEVEL_HEADER, "Crunching server header: %s (contains: %s)", *header, crunch_pattern);
return JB_ERR_OK;
}
+
/*********************************************************************
*
* Function : client_accept_encoding
jb_err crunch_client_header(struct client_state *csp, char **header)
{
const char *crunch_pattern;
- /*Is there a header to crunch*/
-
+
+ /* Do we feel like crunching? */
if ((csp->action->flags & ACTION_CRUNCH_CLIENT_HEADER))
{
crunch_pattern = csp->action->string[ACTION_STRING_CLIENT_HEADER];
- /*Is the current header the lucky one?*/
+ /* Is the current header the lucky one? */
if (strstr(*header, crunch_pattern))
{
log_error(LOG_LEVEL_HEADER, "Crunching client header: %s (contains: %s)", *header, crunch_pattern);
*
* Function : client_send_cookie
*
- * Description : Handle the "cookie" header properly. Called from `sed'.
- * If cookie is accepted, add it to the cookie_list,
- * else we crunch it. Mmmmmmmmmmm ... cookie ......
+ * Description : Crunches the "cookie" header if necessary.
+ * Called from `sed'.
+ *
+ * XXX: Stupid name, doesn't send squat.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*********************************************************************/
jb_err client_send_cookie(struct client_state *csp, char **header)
{
- jb_err result = JB_ERR_OK;
-
- if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0)
+ if (csp->action->flags & ACTION_NO_COOKIE_READ)
{
- /* strlen("cookie: ") == 8 */
- result = enlist(csp->cookie_list, *header + 8);
- }
- else
- {
- log_error(LOG_LEVEL_HEADER, "Crunched outgoing cookie -- yum!");
+ log_error(LOG_LEVEL_HEADER, "Crunched outgoing cookie: %s", *header);
+ freez(*header);
}
- /*
- * Always remove the cookie here. The cookie header
- * will be sent at the end of the header.
- */
- freez(*header);
-
- return result;
+ return JB_ERR_OK;
}
csp->http->hostport, csp->http->host, csp->http->port);
}
+ /* Signal client_host_adder() to return right away */
+ csp->flags |= CSP_FLAG_HOST_HEADER_IS_SET;
+
return JB_ERR_OK;
}
char *p;
jb_err err;
+ if (csp->flags & CSP_FLAG_HOST_HEADER_IS_SET)
+ {
+ /* Header already set by the client, nothing to do. */
+ return JB_ERR_OK;
+ }
+
if ( !csp->http->hostport || !*(csp->http->hostport))
{
+ /* XXX: When does this happen and why is it OK? */
+ log_error(LOG_LEVEL_INFO, "Weirdness in client_host_adder detected and ignored.");
return JB_ERR_OK;
}
p = csp->http->hostport;
}
+ /* XXX: Just add it, we already made sure that it will be unique */
log_error(LOG_LEVEL_HEADER, "addh-unique: Host: %s", p);
err = enlist_unique_header(csp->headers, "Host", p);
return err;
*
* Description : Used in the add_client_headers list. Called from `sed'.
*
+ * XXX: Remove csp->cookie_list which is no longer used.
+ *
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*
*********************************************************************/
jb_err client_accept_encoding_adder(struct client_state *csp)
{
+ assert(0); /* Not in use */
+
if ( ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
&& (!strcmpic(csp->http->ver, "HTTP/1.1")) )
{
*
* Function : connection_close_adder
*
- * Description : Adds a "Connection: close" header to csp->headers
- * as a temporary fix for the needed but missing HTTP/1.1
- * support. Called from `sed'.
+ * Description : "Temporary" fix for the needed but missing HTTP/1.1
+ * support. Adds a "Connection: close" header to csp->headers
+ * unless the header was already present. Called from `sed'.
+ *
* FIXME: This whole function shouldn't be neccessary!
*
* Parameters :
*********************************************************************/
jb_err connection_close_adder(struct client_state *csp)
{
+ const unsigned int flags = csp->flags;
+
+ /*
+ * Return right away if
+ *
+ * - we're parsing server headers and the server header
+ * "Connection: close" is already set, or if
+ *
+ * - we're parsing client headers and the client header
+ * "Connection: close" is already set.
+ */
+ if ((flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE
+ && flags & CSP_FLAG_SERVER_CONNECTION_CLOSE_SET)
+ ||(!(flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE)
+ && flags & CSP_FLAG_CLIENT_CONNECTION_CLOSE_SET))
+ {
+ return JB_ERR_OK;
+ }
+
log_error(LOG_LEVEL_HEADER, "Adding: Connection: close");
+
return enlist(csp->headers, "Connection: close");
}
*********************************************************************/
jb_err server_http(struct client_state *csp, char **header)
{
+ /* Signal that were now parsing server headers. */
+ csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE;
+
sscanf(*header, "HTTP/%*d.%*d %d", &(csp->http->status));
if (csp->http->status == 206)
{
tm_now = *localtime_r(&now, &tm_now);
#elif FEATURE_PTHREAD
pthread_mutex_lock(&localtime_mutex);
- tm_now = *localtime (&now);
+ tm_now = *localtime (&now);
pthread_mutex_unlock(&localtime_mutex);
#else
- tm_now = *localtime (&now);
+ tm_now = *localtime (&now);
#endif
strftime(tempbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now);
{
char *match;
const char *expiration_date = cur_tag + 8; /* Skip "[Ee]xpires=" */
-
+ memset(&tm_cookie, 0, sizeof(tm_cookie));
/*
* Try the valid time formats we know about.
*
- * XXX: This should be factored out into a separate function.
+ * XXX: This should be moved to parse_header_time().
*
* XXX: Maybe the log messages should be removed
* for the next stable release. They just exist to
"cookie \'%s\' send by %s appears to be using time format 4.",
*header, csp->http->url);
}
+ else if (NULL != (match = strptime(expiration_date, "%A %b %e %H:%M:%S %Y", &tm_cookie)))
+ {
+ /* Thu Mar 08 23:00:00 2007 GMT */
+ log_error(LOG_LEVEL_HEADER,
+ "cookie \'%s\' send by %s appears to be using time format 5.",
+ *header, csp->http->url);
+ }
/* Did any of them match? */
if (NULL == match)
}
else
{
- log_error(LOG_LEVEL_HEADER,
- "Cookie \'%s\' is still valid and has to be rewritten.", *header);
-
/*
- * Delete the tag by copying the rest of the string over it.
+ * Still valid, delete expiration date by copying
+ * the rest of the string over it.
+ *
* (Note that we cannot just use "strcpy(cur_tag, next_tag)",
* since the behaviour of strcpy is undefined for overlapping
* strings.)