-const char jcc_rcs[] = "$Id: jcc.c,v 1.121 2007/01/27 10:52:56 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.126 2007/03/17 15:20:05 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
*
* Revisions :
* $Log: jcc.c,v $
+ * Revision 1.126 2007/03/17 15:20:05 fabiankeil
+ * New config option: enforce-blocks.
+ *
+ * Revision 1.125 2007/03/09 14:12:00 fabiankeil
+ * - Move null byte check into separate function.
+ * - Don't confuse the client with error pages
+ * if a CONNECT request was already confirmed.
+ *
+ * Revision 1.124 2007/02/23 14:59:54 fabiankeil
+ * Speed up NULL byte escaping and only log the complete
+ * NULL byte requests with header debugging enabled.
+ *
+ * Revision 1.123 2007/02/21 18:42:10 fabiankeil
+ * Answer requests that contain NULL bytes with
+ * a custom response instead of waiting for more
+ * data until the client eventually hangs up.
+ *
+ * Revision 1.122 2007/02/07 11:12:02 fabiankeil
+ * - Move delivery and logging of crunched responses
+ * from chat() into send_crunch_response().
+ * - Display the reason for generating http_responses.
+ * - Log the content length for LOG_LEVEL_CLF correctly
+ * (still incorrect for some fixed responses).
+ * - Reword an incorrect comment about
+ * treat-forbidden-connects-like-blocks violating
+ * the specs.
+ * - Add some log messages.
+ *
* Revision 1.121 2007/01/27 10:52:56 fabiankeil
* Move mutex initialization into separate
* function and exit in case of errors.
"Empty server or forwarder response.\r\n"
"The connection was closed without sending any data.\r\n";
+/* XXX: should be a template */
+const char NULL_BYTE_RESPONSE[] =
+ "HTTP/1.0 400 Bad request received from browser\r\n"
+ "Proxy-Agent: Privoxy " VERSION "\r\n"
+ "Connection: close\r\n\r\n"
+ "Bad request. Null byte(s) before end of request.\r\n";
+
#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA)
/*********************************************************************
*
}
+/*********************************************************************
+ *
+ * Function : request_contains_null_bytes
+ *
+ * Description : Checks for NULL bytes in the request and sends
+ * an error message to the client if any were found.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : buf = Data from the client's request to check.
+ * 3 : len = The data length.
+ *
+ * Returns : TRUE if the request contained one or more NULL bytes, or
+ * FALSE otherwise.
+ *
+ *********************************************************************/
+int request_contains_null_bytes(const struct client_state *csp, char *buf, int len)
+{
+ size_t c_len; /* Request lenght when treated as C string */
+
+ c_len = strlen(buf);
+
+ if (c_len < len)
+ {
+ /*
+ * Null byte(s) found. Log the request,
+ * return an error response and hang up.
+ */
+ size_t tmp_len = c_len;
+
+ do
+ {
+ /*
+ * Replace NULL byte(s) with '°' characters
+ * so the request can be logged as string.
+ * XXX: Is there a better replacement character?
+ */
+ buf[tmp_len]='°';
+ tmp_len += strlen(buf+tmp_len);
+ } while (tmp_len < len);
+
+ log_error(LOG_LEVEL_ERROR, "%s\'s request contains at least one NULL byte "
+ "(length=%d, strlen=%d).", csp->ip_addr_str, len, c_len);
+ log_error(LOG_LEVEL_HEADER,
+ "Offending request data with NULL bytes turned into \'°\' characters: %s", buf);
+
+ strcpy(buf, NULL_BYTE_RESPONSE);
+ write_socket(csp->cfd, buf, strlen(buf));
+
+ /* XXX: Log correct size */
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
/*********************************************************************
*
* Function : chat
http = csp->http;
+ memset(buf, 0, sizeof(buf));
+
/*
* Read the client's request. Note that since we're not using select() we
* could get blocked here if a client connected, then didn't say anything!
for (;;)
{
- len = read_socket(csp->cfd, buf, sizeof(buf));
+ len = read_socket(csp->cfd, buf, sizeof(buf)-1);
if (len <= 0) break; /* error! */
-
+
+ if (request_contains_null_bytes(csp, buf, len))
+ {
+ /* NULL bytes found and dealt with, just hang up. */
+ return;
+ }
+
/*
* If there is no memory left for buffering the
* request, there is nothing we can do but hang up
}
#ifdef FEATURE_FORCE_LOAD
- /* If this request contains the FORCE_PREFIX,
- * better get rid of it now and set the force flag --oes
+ /*
+ * If this request contains the FORCE_PREFIX and blocks
+ * aren't enforced, get rid of it and set the force flag.
*/
-
if (strstr(req, FORCE_PREFIX))
{
- strclean(req, FORCE_PREFIX);
- log_error(LOG_LEVEL_FORCE, "Enforcing request \"%s\".\n", req);
- csp->flags |= CSP_FLAG_FORCED;
+ if (csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+ {
+ log_error(LOG_LEVEL_FORCE,
+ "Ignored force prefix in request: \"%s\".", req);
+ }
+ else
+ {
+ strclean(req, FORCE_PREFIX);
+ log_error(LOG_LEVEL_FORCE, "Enforcing request: \"%s\".", req);
+ csp->flags |= CSP_FLAG_FORCED;
+ }
}
#endif /* def FEATURE_FORCE_LOAD */
strcpy(buf, CHEADER);
write_socket(csp->cfd, buf, strlen(buf));
/* XXX: Use correct size */
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 400 0", csp->ip_addr_str);
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str);
log_error(LOG_LEVEL_ERROR, "Invalid header received from %s.", csp->ip_addr_str);
free_http_request(http);
{
log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
+ if (http->ssl && (fwd->forward_host == NULL))
+ {
+ /*
+ * Just hang up. We already confirmed the client's CONNECT
+ * request with status code 200 and unencrypted content is
+ * no longer welcome.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "CONNECT already confirmed. Unable to tell the client about the problem.");
+ return;
+ }
+
rsp = error_response(csp, "connect-failed", errno);
if(rsp)
#ifdef FEATURE_ACL
if (block_acl(NULL,csp))
{
- log_error(LOG_LEVEL_CONNECT, "Connection dropped due to ACL");
+ log_error(LOG_LEVEL_CONNECT, "Connection from %s dropped due to ACL", csp->ip_addr_str);
close_socket(csp->cfd);
freez(csp);
continue;