Ignore nested comments in tokenize().
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index 9a05b30..7ca0f46 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.230 2009/03/07 13:09:17 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.244 2009/04/17 11:34:34 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -33,6 +33,61 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.230 2009/03/07 13:09:17 fabiankeil Exp $"
  *
  * Revisions   :
  *    $Log: jcc.c,v $
+ *    Revision 1.244  2009/04/17 11:34:34  fabiankeil
+ *    Style cosmetics for the IPv6 code.
+ *
+ *    Revision 1.243  2009/04/17 11:27:49  fabiankeil
+ *    Petr Pisar's privoxy-3.0.12-ipv6-3.diff.
+ *
+ *    Revision 1.242  2009/04/11 10:44:47  fabiankeil
+ *    Update a comment. We're not in Kansas anymore.
+ *
+ *    Revision 1.241  2009/04/11 10:37:23  fabiankeil
+ *    When dropping connections due to ACL, don't leak csp->ip_addr_str.
+ *
+ *    Revision 1.240  2009/04/09 10:12:54  fabiankeil
+ *    Fix two cases in which an invalid server response would result
+ *    in the client connection being closed without sending an error
+ *    message first.
+ *
+ *    Revision 1.239  2009/04/07 11:43:50  fabiankeil
+ *    If the server rudely resets the connection directly after sending the
+ *    headers, pass the mess to the client instead of sending an incorrect
+ *    connect-failed message. Fixes #2698674 reported by mybugaccount.
+ *
+ *    Revision 1.238  2009/03/27 14:42:30  fabiankeil
+ *    Correct the status code for CONNECTION_TIMEOUT_RESPONSE.
+ *
+ *    Revision 1.237  2009/03/27 14:32:04  fabiankeil
+ *    If spawning a child in listen_loop() fails, send a real
+ *    HTTP response to the client and continue listening for
+ *    new connections without artificial delay.
+ *
+ *    Revision 1.236  2009/03/25 17:30:24  fabiankeil
+ *    In serve(), keep the client socket open until we marked the
+ *    server socket as unused. This should increase the chances
+ *    that we reuse the connection for the client's next request
+ *    to the same destination.
+ *
+ *    Revision 1.235  2009/03/18 21:01:20  fabiankeil
+ *    Comment fix. Spotted by Roland.
+ *
+ *    Revision 1.234  2009/03/18 20:48:42  fabiankeil
+ *    If the --no-daemon option is used, enable LOG_LEVEL_INFO
+ *    before the config file has been parsed (as we always did).
+ *
+ *    Revision 1.233  2009/03/13 14:10:07  fabiankeil
+ *    Fix some more harmless warnings on amd64.
+ *
+ *    Revision 1.232  2009/03/08 19:29:16  fabiankeil
+ *    Reinitialize the timeout structure every time before passing
+ *    it to select(). Apparently some implementations mess with it.
+ *    Probably fixes #2669131 reported by cyberpatrol.
+ *
+ *    Revision 1.231  2009/03/08 14:19:23  fabiankeil
+ *    Fix justified (but harmless) compiler warnings
+ *    on platforms where sizeof(int) < sizeof(long).
+ *
  *    Revision 1.230  2009/03/07 13:09:17  fabiankeil
  *    Change csp->expected_content and_csp->expected_content_length from
  *    size_t to unsigned long long to reduce the likelihood of integer
@@ -1449,9 +1504,16 @@ static const char MESSED_UP_REQUEST_RESPONSE[] =
    "Connection: close\r\n\r\n"
    "Bad request. Messed up with header filters.\r\n";
 
+static const char TOO_MANY_CONNECTIONS_RESPONSE[] =
+   "HTTP/1.0 503 Too many open connections\r\n"
+   "Proxy-Agent: Privoxy " VERSION "\r\n"
+   "Content-Type: text/plain\r\n"
+   "Connection: close\r\n\r\n"
+   "Maximum number of open connections reached.\r\n";
+
 /* XXX: should be a template */
 static const char CONNECTION_TIMEOUT_RESPONSE[] =
-   "HTTP/1.0 502 Connection timeout\r\n"
+   "HTTP/1.0 504 Connection timeout\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "Content-Type: text/plain\r\n"
    "Connection: close\r\n\r\n"
@@ -2586,7 +2648,7 @@ static void chat(struct client_state *csp)
    int max_forwarded_connect_retries = csp->config->forwarded_connect_retries;
    const struct forward_spec *fwd;
    struct http_request *http;
-   int len = 0; /* for buffer sizes (and negative error codes) */
+   long len = 0; /* for buffer sizes (and negative error codes) */
 
    /* Function that does the content filtering for the current request */
    filter_function_ptr content_filter = NULL;
@@ -2596,8 +2658,6 @@ static void chat(struct client_state *csp)
    struct timeval timeout;
 
    memset(buf, 0, sizeof(buf));
-   memset(&timeout, 0, sizeof(timeout));
-   timeout.tv_sec = csp->config->socket_timeout;
 
    http = csp->http;
 
@@ -2693,7 +2753,7 @@ static void chat(struct client_state *csp)
 
    if (fwd->forward_host)
    {
-      log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s",
+      log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s",
          fwd->forward_host, fwd->forward_port, http->hostport);
    }
    else
@@ -2839,6 +2899,8 @@ static void chat(struct client_state *csp)
       }
 #endif  /* FEATURE_CONNECTION_KEEP_ALIVE */
 
+      timeout.tv_sec = csp->config->socket_timeout;
+      timeout.tv_usec = 0;
       n = select((int)maxfd+1, &rfds, NULL, NULL, &timeout);
 
       if (n == 0)
@@ -2923,14 +2985,11 @@ static void chat(struct client_state *csp)
                mark_server_socket_tainted(csp);
                return;
             }
-
-            rsp = error_response(csp, "connect-failed", errno);
-            if (rsp)
-            {
-               send_crunch_response(csp, rsp);
-            }
-
-            return;
+            /*
+             * XXX: Consider handling the cases above the same.
+             */
+            mark_server_socket_tainted(csp);
+            len = 0;
          }
 
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
@@ -3060,7 +3119,7 @@ static void chat(struct client_state *csp)
                if (add_to_iob(csp, buf, len))
                {
                   size_t hdrlen;
-                  int flushed;
+                  long flushed;
 
                   log_error(LOG_LEVEL_INFO,
                      "Flushing header and buffers. Stepping back from filtering.");
@@ -3143,9 +3202,14 @@ static void chat(struct client_state *csp)
                    * The header is incomplete and there isn't anything
                    * we can do about it.
                    */
-                  log_error(LOG_LEVEL_INFO,
-                     "MS IIS5 hack didn't produce valid headers.");
-                  break;
+                  log_error(LOG_LEVEL_ERROR, "Invalid server headers. "
+                     "Applying the MS IIS5 hack didn't help.");
+                  log_error(LOG_LEVEL_CLF,
+                     "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+                  write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+                     strlen(INVALID_SERVER_HEADERS_RESPONSE));
+                  mark_server_socket_tainted(csp);
+                  return;
                }
                else
                {
@@ -3282,9 +3346,15 @@ static void chat(struct client_state *csp)
              */
             if (ms_iis5_hack)
             {
-               log_error(LOG_LEVEL_INFO,
-                  "Closed server connection detected with MS IIS5 hack enabled.");
-               break;
+               log_error(LOG_LEVEL_ERROR,
+                  "Closed server connection detected. "
+                  "Applying the MS IIS5 hack didn't help.");
+               log_error(LOG_LEVEL_CLF,
+                  "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+               write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+                  strlen(INVALID_SERVER_HEADERS_RESPONSE));
+               mark_server_socket_tainted(csp);
+               return;
             }
          }
          continue;
@@ -3323,7 +3393,8 @@ static void chat(struct client_state *csp)
  * Function    :  serve
  *
  * Description :  This is little more than chat.  We only "serve" to
- *                to close any socket that chat may have opened.
+ *                to close (or remember) any socket that chat may have
+ *                opened.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -3338,7 +3409,6 @@ static void serve(struct client_state *csp)
 #endif /* def AMIGA */
 {
    chat(csp);
-   close_socket(csp->cfd);
 
    if (csp->sfd != JB_INVALID_SOCKET)
    {
@@ -3349,6 +3419,8 @@ static void serve(struct client_state *csp)
        && (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE))
       {
          remember_connection(csp->sfd, csp->http, forward_url(csp, csp->http));
+         close_socket(csp->cfd);
+         csp->cfd = JB_INVALID_SOCKET;
          privoxy_mutex_lock(&connection_reuse_mutex);
          if (!monitor_thread_running)
          {
@@ -3370,6 +3442,11 @@ static void serve(struct client_state *csp)
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    }
 
+   if (csp->cfd != JB_INVALID_SOCKET)
+   {
+      close_socket(csp->cfd);
+   }
+
    csp->flags &= ~CSP_FLAG_ACTIVE;
 
 }
@@ -3678,6 +3755,7 @@ int main(int argc, const char *argv[])
 
       else if (strcmp(argv[argc_pos], "--no-daemon" ) == 0)
       {
+         set_debug_level(LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);
          no_daemon = 1;
       }
 
@@ -3893,8 +3971,8 @@ int main(int argc, const char *argv[])
       }
 #endif /* 1 */
       /*
-       * stderr (fd 2) will be closed later on, when the
-       * log file has been parsed.
+       * stderr (fd 2) will be closed later on,
+       * when the config file has been parsed.
        */
 
       close( 0 );
@@ -4105,7 +4183,8 @@ static void listen_loop(void)
 {
    struct client_state *csp = NULL;
    jb_socket bfd;
-   struct configuration_spec * config;
+   struct configuration_spec *config;
+   unsigned int active_threads = 0;
 
    config = load_config();
 
@@ -4135,7 +4214,7 @@ static void listen_loop(void)
       /*
        * Free data that was used by died threads
        */
-      sweep();
+      active_threads = sweep();
 
 #if defined(unix)
       /*
@@ -4166,7 +4245,7 @@ static void listen_loop(void)
       {
          /*
           * Since we were listening to the "old port", we will not see
-          * a "listen" param change until the next IJB request.  So, at
+          * a "listen" param change until the next request.  So, at
           * least 1 more request must be made for us to find the new
           * setting.  I am simply closing the old socket and binding the
           * new one.
@@ -4220,11 +4299,26 @@ static void listen_loop(void)
       {
          log_error(LOG_LEVEL_CONNECT, "Connection from %s dropped due to ACL", csp->ip_addr_str);
          close_socket(csp->cfd);
+         freez(csp->ip_addr_str);
          freez(csp);
          continue;
       }
 #endif /* def FEATURE_ACL */
 
+      if ((0 != config->max_client_connections)
+         && (active_threads >= config->max_client_connections))
+      {
+         log_error(LOG_LEVEL_CONNECT,
+            "Rejecting connection from %s. Maximum number of connections reached.",
+            csp->ip_addr_str);
+         write_socket(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE,
+            strlen(TOO_MANY_CONNECTIONS_RESPONSE));
+         close_socket(csp->cfd);
+         freez(csp->ip_addr_str);
+         freez(csp);
+         continue;
+      }
+
       /* add it to the list of clients */
       csp->next = clients->next;
       clients->next = csp;
@@ -4391,19 +4485,19 @@ static void listen_loop(void)
 #undef SELECTED_ONE_OPTION
 /* end of cpp switch () */
 
-         if (child_id < 0) /* failed */
+         if (child_id < 0)
          {
-            char buf[BUFFER_SIZE];
-
-            log_error(LOG_LEVEL_ERROR, "can't fork: %E");
-
-            snprintf(buf , sizeof(buf), "Privoxy: can't fork: errno = %d", errno);
-
-            write_socket(csp->cfd, buf, strlen(buf));
+            /*
+             * Spawning the child failed, assume it's because
+             * there are too many children running already.
+             * XXX: If you assume ...
+             */
+            log_error(LOG_LEVEL_ERROR,
+               "Unable to take any additional connections: %E");
+            write_socket(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE,
+               strlen(TOO_MANY_CONNECTIONS_RESPONSE));
             close_socket(csp->cfd);
             csp->flags &= ~CSP_FLAG_ACTIVE;
-            sleep(5);
-            continue;
          }
       }
       else