- Remove filter_client_header() and filter_client_header(),
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index 1cf56e4..320858a 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-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 $
@@ -33,6 +33,34 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.121 2007/01/27 10:52:56 fabiankeil Exp $"
  *
  * 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.
@@ -957,6 +985,13 @@ const char NO_SERVER_DATA_RESPONSE[] =
    "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)
 /*********************************************************************
  *
@@ -1373,6 +1408,65 @@ void send_crunch_response(struct client_state *csp, struct http_response *rsp)
 }
 
 
+/*********************************************************************
+ *
+ * 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
@@ -1454,6 +1548,8 @@ static void chat(struct client_state *csp)
 
    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!
@@ -1461,10 +1557,16 @@ static void chat(struct client_state *csp)
 
    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
@@ -1498,15 +1600,23 @@ static void chat(struct client_state *csp)
       }
 
 #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 */
@@ -1530,7 +1640,7 @@ static void chat(struct client_state *csp)
       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);
@@ -1985,6 +2095,18 @@ static void chat(struct client_state *csp)
          {
             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)
@@ -3084,7 +3206,7 @@ static void listen_loop(void)
 #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;