-const char parsers_rcs[] = "$Id: parsers.c,v 1.282 2013/12/24 13:34:45 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.296 2014/11/03 14:41:12 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/parsers.c,v $
*
* Purpose : Declares functions to parse/crunch headers and pages.
*
- * Copyright : Written by and Copyright (C) 2001-2012 the
+ * Copyright : Written by and Copyright (C) 2001-2014 the
* Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
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 client_expect (struct client_state *csp, char **header);
static jb_err server_set_cookie (struct client_state *csp, char **header);
static jb_err server_connection (struct client_state *csp, char **header);
static jb_err server_content_type (struct client_state *csp, char **header);
#if 0
{ "Transfer-Encoding:", 18, client_transfer_encoding },
#endif
+ { "Expect:", 7, client_expect },
{ "*", 0, crunch_client_header },
{ "*", 0, filter_header },
{ NULL, 0, NULL }
*
* Function : normalize_lws
*
- * Description : Reduces unquoted linear white space in headers
- * to a single space in accordance with RFC 2616 2.2.
+ * Description : Reduces unquoted linear whitespace in headers to
+ * a single space in accordance with RFC 7230 3.2.4.
* This simplifies parsing and filtering later on.
*
- * XXX: Remove log messages before
- * the next stable release?
- *
* Parameters :
- * 1 : header = A header with linear white space to reduce.
+ * 1 : header = A header with linear whitespace to reduce.
*
* Returns : N/A
*
{
q++;
}
- log_error(LOG_LEVEL_HEADER, "Reducing white space in '%s'", header);
+ log_error(LOG_LEVEL_HEADER, "Reducing whitespace in '%s'", header);
string_move(p+1, q);
}
* server and header filtering.
*
* Returns : JB_ERR_OK in case off success, or
- * JB_ERR_MEMORY on out-of-memory error.
+ * JB_ERR_MEMORY on some out-of-memory errors, or
+ * JB_ERR_PARSE in case of fatal parse errors.
*
*********************************************************************/
jb_err sed(struct client_state *csp, int filter_server_headers)
check_negative_tag_patterns(csp, PATTERN_SPEC_NO_REQUEST_TAG_PATTERN);
}
- while ((err == JB_ERR_OK) && (v->str != NULL))
+ while (v->str != NULL)
{
- for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL); p = p->next)
+ for (p = csp->headers->first; p != NULL; p = p->next)
{
/* Header crunch()ed in previous run? -> ignore */
if (p->str == NULL) continue;
(v->len == CHECK_EVERY_HEADER_REMAINING))
{
err = v->parser(csp, &(p->str));
+ if (err != JB_ERR_OK)
+ {
+ return err;
+ }
}
}
v++;
*********************************************************************/
static jb_err header_tagger(struct client_state *csp, char *header)
{
- int wanted_filter_type;
+ enum filter_type wanted_filter_type;
int multi_action_index;
pcrs_job *job;
struct re_filterfile_spec *b;
struct list_entry *filtername;
- int wanted_filter_type;
+ enum filter_type wanted_filter_type;
int multi_action_index;
if (csp->flags & CSP_FLAG_NO_FILTERING)
csp->flags |= CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET;
}
+ freez(*header);
+
return JB_ERR_OK;
}
log_error(LOG_LEVEL_HEADER,
"Couldn't parse: '%s'. Using default timeout %u",
*header, csp->config->keep_alive_timeout);
+ freez(*header);
return JB_ERR_OK;
}
log_error(LOG_LEVEL_HEADER,
"Client keep-alive timeout is %u. Sticking with %u.",
keep_alive_timeout, csp->config->keep_alive_timeout);
+ freez(*header);
}
return JB_ERR_OK;
}
+/*********************************************************************
+ *
+ * Function : client_expect
+ *
+ * Description : Raise the CSP_FLAG_UNSUPPORTED_CLIENT_EXPECTATION
+ * if the Expect header value is unsupported.
+ *
+ * Rejecting unsupported expectations is a RFC 7231 5.1.1
+ * MAY and a RFC 2616 (obsolete) MUST.
+ *
+ * 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 client_expect(struct client_state *csp, char **header)
+{
+ if (0 != strcmpic(*header, "Expect: 100-continue"))
+ {
+ csp->flags |= CSP_FLAG_UNSUPPORTED_CLIENT_EXPECTATION;
+ log_error(LOG_LEVEL_HEADER,
+ "Unsupported client expectaction: %s", *header);
+ }
+
+ return JB_ERR_OK;
+
+}
+
+
/*********************************************************************
*
* Function : crumble
*/
if ((csp->content_type & CT_TEXT) || (csp->action->flags & ACTION_FORCE_TEXT_MODE))
{
+ jb_err err;
freez(*header);
*header = strdup_or_die("Content-Type: ");
- string_append(header, csp->action->string[ACTION_STRING_CONTENT_TYPE]);
- if (header == NULL)
+ err = string_append(header, csp->action->string[ACTION_STRING_CONTENT_TYPE]);
+ if (JB_ERR_OK != err)
{
log_error(LOG_LEVEL_HEADER, "Insufficient memory to replace Content-Type!");
return JB_ERR_MEMORY;
/*
* Log a warning if the user expects the content to be filtered.
*/
- if ((csp->rlist != NULL) &&
- (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
+ if (content_filters_enabled(csp->action))
{
log_error(LOG_LEVEL_INFO,
"SDCH-compressed content detected, content filtering disabled. "
* is a partial range (HTTP status 206)
* - Rewrite HTTP/1.1 answers to HTTP/1.0 if +downgrade
* action applies.
+ * - Normalize the HTTP-version.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
* original string if necessary.
*
* Returns : JB_ERR_OK on success, or
- * JB_ERR_MEMORY on out-of-memory error.
+ * JB_ERR_PARSE on fatal parse errors.
*
*********************************************************************/
static jb_err server_http(struct client_state *csp, char **header)
{
- sscanf(*header, "HTTP/%*d.%*d %d", &(csp->http->status));
+ char *reason_phrase = NULL;
+ char *new_response_line;
+ char *p;
+ size_t length;
+ unsigned int major_version;
+ unsigned int minor_version;
+
+ /* Get the reason phrase which start after the second whitespace */
+ p = strchr(*header, ' ');
+ if (NULL != p)
+ {
+ p++;
+ reason_phrase = strchr(p, ' ');
+ }
+
+ if (reason_phrase != NULL)
+ {
+ reason_phrase++;
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Response line lacks reason phrase: %s", *header);
+ reason_phrase="";
+ }
+
+ if (3 != sscanf(*header, "HTTP/%u.%u %d", &major_version,
+ &minor_version, &(csp->http->status)))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to parse the response line: %s", *header);
+ return JB_ERR_PARSE;
+ }
+
if (csp->http->status == 206)
{
csp->content_type = CT_TABOO;
}
- if ((csp->action->flags & ACTION_DOWNGRADE) != 0)
+ if (major_version != 1 || (minor_version != 0 && minor_version != 1))
{
- /* 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.");
- }
+ /*
+ * According to RFC 7230 2.6 intermediaries MUST send
+ * their own HTTP-version in forwarded messages.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "Unsupported HTTP version. Downgrading to 1.1.");
+ major_version = 1;
+ minor_version = 1;
+ }
+
+ if (((csp->action->flags & ACTION_DOWNGRADE) != 0) && (minor_version == 1))
+ {
+ log_error(LOG_LEVEL_HEADER, "Downgrading answer to HTTP/1.0");
+ minor_version = 0;
}
+ /* Rebuild response line. */
+ length = sizeof("HTTP/1.1 200 ") + strlen(reason_phrase) + 1;
+ new_response_line = malloc_or_die(length);
+
+ snprintf(new_response_line, length, "HTTP/%u.%u %d %s",
+ major_version, minor_version, csp->http->status, reason_phrase);
+
+ if (0 != strcmp(*header, new_response_line))
+ {
+ log_error(LOG_LEVEL_HEADER, "Response line '%s' changed to '%s'",
+ *header, new_response_line);
+ }
+
+ freez(*header);
+ *header = new_response_line;
+
return JB_ERR_OK;
}