*
* Purpose : Declares functions to parse/crunch headers and pages.
*
- * Copyright : Written by and Copyright (C) 2001-2020 the
+ * Copyright : Written by and Copyright (C) 2001-2021 the
* Privoxy team. https://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
#include <brotli/decode.h>
#endif
-#if !defined(_WIN32) && !defined(__OS2__)
+#if !defined(_WIN32)
#include <unistd.h>
#endif
}
+/*********************************************************************
+ *
+ * Function : can_add_to_iob
+ *
+ * Description : Checks if the given number of bytes can be added to the given iob
+ * without exceeding the given buffer limit.
+ *
+ * Parameters :
+ * 1 : iob = Destination buffer.
+ * 2 : buffer_limit = Limit to which the destination may grow
+ * 3 : n = number of bytes to be added
+ *
+ * Returns : TRUE if the given iob can handle given number of bytes
+ * FALSE buffer limit will be exceeded
+ *
+ *********************************************************************/
+int can_add_to_iob(const struct iob *iob, const size_t buffer_limit, size_t n)
+{
+ return ((size_t)(iob->eod - iob->buf) + n + 1) > buffer_limit ? FALSE : TRUE;
+}
+
/*********************************************************************
*
* Function : add_to_iob
* or buffer limit reached.
*
*********************************************************************/
-jb_err add_to_iob(struct iob *iob, const size_t buffer_limit, char *src, long n)
+jb_err add_to_iob(struct iob *iob, const size_t buffer_limit, const char *src, long n)
{
size_t used, offset, need;
char *p;
if (need > buffer_limit)
{
log_error(LOG_LEVEL_INFO,
- "Buffer limit reached while extending the buffer (iob). Needed: %d. Limit: %d",
+ "Buffer limit reached while extending the buffer (iob). Needed: %lu. Limit: %lu",
need, buffer_limit);
return JB_ERR_MEMORY;
}
if (decoded_buffer == NULL)
{
log_error(LOG_LEVEL_ERROR,
- "Failed to allocate %d bytes for Brotli decompression",
+ "Failed to allocate %lu bytes for Brotli decompression",
decoded_buffer_size);
return JB_ERR_MEMORY;
}
csp->iob->size = decoded_buffer_size;
log_error(LOG_LEVEL_RE_FILTER,
- "Decompression successful. Old size: %d, new size: %d.",
+ "Decompression successful. Old size: %lu, new size: %lu.",
encoded_size, decoded_size);
return JB_ERR_OK;
cur = csp->iob->cur;
- if (bufsize < (size_t)10)
+ if (old_size < (size_t)10)
{
/*
* This is to protect the parsing of gzipped data,
* but it should(?) be valid for deflated data also.
*/
log_error(LOG_LEVEL_ERROR,
- "Insufficient data to start decompression. Bytes in buffer: %d",
+ "Insufficient data to start decompression. Bytes in buffer: %ld",
csp->iob->eod - csp->iob->cur);
return JB_ERR_COMPRESS;
}
|| ((*cur++ & 0xff) != GZIP_IDENTIFIER_2)
|| (*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 & GZIP_FLAG_RESERVED_BITS)
{
/* 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;
}
* XXX: this code is untested and should probably be removed.
*/
int skip_bytes;
+
+ if (cur + 2 >= csp->iob->eod)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "gzip extra field flag set but insufficient data available.");
+ return JB_ERR_COMPRESS;
+ }
+
skip_bytes = *cur++;
- skip_bytes += *cur++ << 8;
+ skip_bytes += (unsigned char)*cur++ << 8;
/*
* The number of bytes to skip should be positive
if ((skip_bytes < 0) || (skip_bytes >= (csp->iob->eod - cur)))
{
log_error(LOG_LEVEL_ERROR,
- "Unreasonable amount of bytes to skip (%d). Stopping decompression",
+ "Unreasonable amount of bytes to skip (%d). "
+ "Stopping decompression.",
skip_bytes);
return JB_ERR_COMPRESS;
}
log_error(LOG_LEVEL_INFO,
- "Skipping %d bytes for gzip compression. Does this sound right?",
+ "Skipping %d bytes for gzip compression. "
+ "Does this sound right?",
skip_bytes);
cur += skip_bytes;
}
if (flags & GZIP_FLAG_FILE_NAME)
{
/* A null-terminated string is supposed to follow. */
- while (*cur++ && (cur < csp->iob->eod));
+ while ((cur < csp->iob->eod) && *cur++);
}
/* Skip the comment if necessary. */
if (flags & GZIP_FLAG_COMMENT)
{
/* A null-terminated string is supposed to follow. */
- while (*cur++ && (cur < csp->iob->eod));
+ while ((cur < csp->iob->eod) && *cur++);
}
/* Skip the CRC if necessary. */
else
{
log_error(LOG_LEVEL_ERROR,
- "Unable to determine compression format for decompression");
+ "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;
}
/*
* Next, we allocate new storage for the inflated data.
* We don't modify the existing iob yet, so in case there
- * is error in decompression we can recover gracefully.
+ * is an error in decompression we can recover gracefully.
*/
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;
}
*/
if (bufsize >= csp->config->buffer_limit)
{
- log_error(LOG_LEVEL_ERROR, "Buffer limit reached while decompressing iob");
+ log_error(LOG_LEVEL_ERROR,
+ "Buffer limit reached while decompressing iob.");
freez(buf);
+ inflateEnd(&zstr);
return JB_ERR_MEMORY;
}
tmpbuf = realloc(buf, bufsize);
if (NULL == tmpbuf)
{
- log_error(LOG_LEVEL_ERROR, "Out of memory decompressing iob");
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory decompressing iob.");
freez(buf);
+ inflateEnd(&zstr);
return JB_ERR_MEMORY;
}
else
{
+#ifndef NDEBUG
char *oldnext_out = (char *)zstr.next_out;
-
+#endif
/*
* Update the fields for inflate() to use the new
* buffer, which may be in a location different from
log_error(LOG_LEVEL_ERROR,
"Unexpected error while decompressing to the buffer (iob): %s",
zstr.msg);
+ freez(buf);
return JB_ERR_COMPRESS;
}
* Make sure the new uncompressed iob obeys some minimal
* consistency conditions.
*/
- if ((csp->iob->buf <= csp->iob->cur)
+ if ((csp->iob->buf <= csp->iob->cur)
&& (csp->iob->cur <= csp->iob->eod)
&& (csp->iob->eod <= csp->iob->buf + csp->iob->size))
{
if (new_size > (size_t)0)
{
log_error(LOG_LEVEL_RE_FILTER,
- "Decompression successful. Old size: %d, new size: %d.",
+ "Decompression successful. Old size: %lu, new size: %lu.",
old_size, new_size);
}
else
{
/* It seems that zlib did something weird. */
log_error(LOG_LEVEL_ERROR,
- "Unexpected error decompressing the buffer (iob): %d==%d, %d>%d, %d<%d",
- csp->iob->cur, csp->iob->buf + skip_size, csp->iob->eod, csp->iob->buf,
- csp->iob->eod, csp->iob->buf + csp->iob->size);
+ "Inconsistent buffer after decompression.");
return JB_ERR_COMPRESS;
}
}
list_remove_all(headers);
- list_duplicate(headers, new_headers);
- list_remove_all(new_headers);
+ headers->first = new_headers->first;
+ headers->last = new_headers->last;
return;
}
v++;
}
+ if (filter_server_headers &&
+ (csp->flags & CSP_FLAG_SERVER_CONTENT_LENGTH_SET) &&
+ (csp->flags & CSP_FLAG_CHUNKED))
+ {
+ /* RFC 2616 4.4 3 */
+ log_error(LOG_LEVEL_HEADER, "Ignoring the Content-Length header "
+ "sent by the server as the response is chunk-encoded.");
+ csp->flags &= ~CSP_FLAG_CONTENT_LENGTH_SET;
+ csp->expected_content_length = 0;
+ }
+
/* place additional headers on the csp->headers list */
while ((err == JB_ERR_OK) && (*f))
{
f++;
}
- if (!filter_server_headers && !list_is_empty(csp->config->ordered_client_headers))
+ if (!filter_server_headers &&
+ !list_is_empty(csp->config->ordered_client_headers) &&
+ csp->headers->first->str != NULL)
{
enforce_header_order(csp->headers, csp->config->ordered_client_headers);
}
csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE;
/*
- * Update the last header which may have changed
- * due to header additions,
+ * Update the https headers list which may have
+ * been modified due to header additions or header
+ * reordering.
*/
+ csp->https_headers->first = csp->headers->first;
csp->https_headers->last = csp->headers->last;
csp->headers->first = headers.first;
if (NULL == joblist)
{
- log_error(LOG_LEVEL_RE_FILTER,
+ log_error(LOG_LEVEL_TAGGING,
"Tagger %s has empty joblist. Nothing to do.", b->name);
continue;
}
assert(NULL != header);
log_error(LOG_LEVEL_ERROR,
"Problems with tagger \'%s\' and header \'%s\': %s",
- b->name, *header, pcrs_strerror(hits));
+ b->name, header, pcrs_strerror(hits));
}
freez(modified_tag);
}
* no one would do it intentionally.
*/
freez(tag);
- log_error(LOG_LEVEL_INFO,
+ log_error(LOG_LEVEL_TAGGING,
"Tagger \'%s\' created an empty tag. Ignored.", b->name);
continue;
}
+ if (list_contains_item(csp->action->multi[ACTION_MULTI_SUPPRESS_TAG], tag))
+ {
+ log_error(LOG_LEVEL_TAGGING,
+ "Tagger \'%s\' didn't add tag \'%s\': suppressed",
+ b->name, tag);
+ freez(tag);
+ continue;
+ }
+
if (!list_contains_item(csp->tags, tag))
{
if (JB_ERR_OK != enlist(csp->tags, tag))
log_error(LOG_LEVEL_ERROR,
"Insufficient memory to add tag \'%s\', "
"based on tagger \'%s\' and header \'%s\'",
- tag, b->name, *header);
+ tag, b->name, header);
}
else
{
action_message = "No action bits update necessary.";
}
- log_error(LOG_LEVEL_HEADER,
+ log_error(LOG_LEVEL_TAGGING,
"Tagger \'%s\' added tag \'%s\'. %s",
b->name, tag, action_message);
}
else
{
/* XXX: Is this log-worthy? */
- log_error(LOG_LEVEL_HEADER,
+ log_error(LOG_LEVEL_TAGGING,
"Tagger \'%s\' didn't add tag \'%s\'. Tag already present",
b->name, tag);
}
if (NULL == joblist)
{
- log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
+ log_error(LOG_LEVEL_RE_FILTER,
+ "Filter %s has empty joblist. Nothing to do.", b->name);
continue;
}
- log_error(LOG_LEVEL_RE_FILTER, "filtering \'%s\' (size %d) with \'%s\' ...",
+ log_error(LOG_LEVEL_RE_FILTER, "filtering \'%s\' (size %lu) with \'%s\' ...",
*header, size, b->name);
/* Apply all jobs from the joblist */
if (0 < matches)
{
current_hits += matches;
- log_error(LOG_LEVEL_HEADER, "Transforming \"%s\" to \"%s\"", *header, newheader);
+ log_error(LOG_LEVEL_HEADER,
+ "Transforming \"%s\" to \"%s\"", *header, newheader);
freez(*header);
*header = newheader;
}
else
{
/* RegEx failure */
- log_error(LOG_LEVEL_ERROR, "Filtering \'%s\' with \'%s\' didn't work out: %s",
+ log_error(LOG_LEVEL_ERROR,
+ "Filtering \'%s\' with \'%s\' didn't work out: %s",
*header, b->name, pcrs_strerror(matches));
if (newheader != NULL)
{
if (b->dynamic) pcrs_free_joblist(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 %lu).", current_hits, size);
hits += current_hits;
}
*
* 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.
+ * 2 : header = pointer to the Content-Length header
*
* Returns : JB_ERR_OK on success, or
* JB_ERR_MEMORY on out-of-memory error.
#endif /* defined(FEATURE_ZLIB) */
+/*********************************************************************
+ *
+ * Function : header_adjust_content_length
+ *
+ * Description : Replace given header with new Content-Length header.
+ *
+ * Parameters :
+ * 1 : 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.
+ * 2 : content_length = content length value to set
+ *
+ * Returns : JB_ERR_OK on success, or
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err header_adjust_content_length(char **header, size_t content_length)
+{
+ const size_t header_length = 50;
+ freez(*header);
+ *header = malloc(header_length);
+ if (*header == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
+ create_content_length_header(content_length, *header, header_length);
+ return JB_ERR_OK;
+}
+
+
/*********************************************************************
*
* Function : server_adjust_content_length
/* Regenerate header if the content was modified. */
if (csp->flags & CSP_FLAG_MODIFIED)
{
- const size_t header_length = 50;
- freez(*header);
- *header = malloc(header_length);
- if (*header == NULL)
+ if (JB_ERR_OK != header_adjust_content_length(header, csp->content_length))
{
return JB_ERR_MEMORY;
}
- create_content_length_header(csp->content_length, *header, header_length);
log_error(LOG_LEVEL_HEADER,
"Adjusted Content-Length to %llu", csp->content_length);
}
seconds = rtime % 60;
log_error(LOG_LEVEL_HEADER,
- "Randomized: %s (added %d da%s %d hou%s %d minut%s %d second%s",
+ "Randomized: %s (added %ld da%s %ld hou%s %ld minut%s %ld second%s",
*header, days, (days == 1) ? "y" : "ys", hours, (hours == 1) ? "r" : "rs",
minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)");
}
if (rtime)
{
- log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d minut%s)",
+ log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %ld minut%s)",
*header, rtime, (rtime == 1 || rtime == -1) ? "e": "es");
if (negative_range)
{
}
else
{
- log_error(LOG_LEVEL_ERROR, "Random range is 0. Assuming time transformation test.",
- *header);
+ log_error(LOG_LEVEL_ERROR,
+ "Random range is 0. Assuming time transformation test.");
}
tm += rtime * (negative_range ? -1 : 1);
timeptr = privoxy_gmtime_r(&tm, &gmt);
seconds = rtime % 60;
log_error(LOG_LEVEL_HEADER,
- "Randomized: %s (%s %d hou%s %d minut%s %d second%s",
+ "Randomized: %s (%s %ld hou%s %ld minut%s %ld second%s",
*header, (negative_range) ? "subtracted" : "added", hours,
(hours == 1) ? "r" : "rs", minutes, (minutes == 1) ? "e" : "es",
seconds, (seconds == 1) ? ")" : "s)");
return JB_ERR_PARSE;
}
- if (csp->http->status == 206)
+ if (csp->http->status == 101 ||
+ csp->http->status == 206)
{
csp->content_type = CT_TABOO;
}
if (*result != result2)
{
log_error(LOG_LEVEL_ERROR, "strftime() and strptime() disagree. "
- "Format: '%s'. In: '%s', out: '%s'. %d != %d. Rejecting.",
+ "Format: '%s'. In: '%s', out: '%s'. %ld != %ld. Rejecting.",
time_formats[i], header_time, recreated_date, *result, result2);
continue;
}
return JB_ERR_PARSE;
}
- p = strdup_or_die(host);
+ p = string_tolower(host);
+ if (p == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
chomp(p);
q = strdup_or_die(p);
return JB_ERR_PARSE;
}
- p = strdup_or_die(host);
+ p = string_tolower(host);
+ if (p == NULL)
+ {
+ return JB_ERR_MEMORY;
+ }
chomp(p);
q = strdup_or_die(p);
referer[hostlength+17] = '\0';
}
referer_url = strstr(referer, "http://");
+ if (NULL == referer_url)
+ {
+ referer_url = strstr(referer, "https://");
+ }
if ((NULL == referer_url) || (NULL == strstr(referer_url, host)))
{
/* Host has changed, Referer is invalid or a https URL. */