block another omniture tracking domain
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index 048a3f4..722d7fd 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.306 2009/12/22 13:04:10 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.319 2010/05/24 11:40:27 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -6,7 +6,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.306 2009/12/22 13:04:10 fabiankeil Exp $"
  * Purpose     :  Main file.  Contains main() method, main loop, and
  *                the main connection-handling function.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2009 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2010 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -119,7 +119,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.306 2009/12/22 13:04:10 fabiankeil Exp $"
 const char jcc_h_rcs[] = JCC_H_VERSION;
 const char project_h_rcs[] = PROJECT_H_VERSION;
 
-int no_daemon = 0;
+int daemon_mode = 1;
 struct client_state  clients[1];
 struct file_list     files[1];
 
@@ -625,39 +625,39 @@ static const char *crunch_reason(const struct http_response *rsp)
       return "Internal error while searching for crunch reason";
    }
 
-   switch (rsp->reason)
+   switch (rsp->crunch_reason)
    {
-      case RSP_REASON_UNSUPPORTED:
+      case UNSUPPORTED:
          reason = "Unsupported HTTP feature";
          break;
-      case RSP_REASON_BLOCKED:
+      case BLOCKED:
          reason = "Blocked";
          break;
-      case RSP_REASON_UNTRUSTED:
+      case UNTRUSTED:
          reason = "Untrusted";
          break;
-      case RSP_REASON_REDIRECTED:
+      case REDIRECTED:
          reason = "Redirected";
          break;
-      case RSP_REASON_CGI_CALL:
+      case CGI_CALL:
          reason = "CGI Call";
          break;
-      case RSP_REASON_NO_SUCH_DOMAIN:
+      case NO_SUCH_DOMAIN:
          reason = "DNS failure";
          break;
-      case RSP_REASON_FORWARDING_FAILED:
+      case FORWARDING_FAILED:
          reason = "Forwarding failed";
          break;
-      case RSP_REASON_CONNECT_FAILED:
+      case CONNECT_FAILED:
          reason = "Connection failure";
          break;
-      case RSP_REASON_OUT_OF_MEMORY:
+      case OUT_OF_MEMORY:
          reason = "Out of memory (may mask other reasons)";
          break;
-      case RSP_REASON_CONNECTION_TIMEOUT:
+      case CONNECTION_TIMEOUT:
          reason = "Connection timeout";
          break;
-      case RSP_REASON_NO_SERVER_DATA:
+      case NO_SERVER_DATA:
          reason = "No server data received";
          break;
       default:
@@ -694,24 +694,12 @@ static void send_crunch_response(const struct client_state *csp, struct http_res
 
       if (rsp == NULL)
       {
-         /*
-          * Not supposed to happen. If it does
-          * anyway, treat it as an unknown error.
-          */
-         cgi_error_unknown(csp, rsp, RSP_REASON_INTERNAL_ERROR);
-         /* return code doesn't matter */
-      }
-
-      if (rsp == NULL)
-      {
-         /* If rsp is still NULL, we have serious internal problems. */
-         log_error(LOG_LEVEL_FATAL,
-            "NULL response in send_crunch_response and cgi_error_unknown failed as well.");
+         log_error(LOG_LEVEL_FATAL, "NULL response in send_crunch_response.");
       }
 
       /*
        * Extract the status code from the actual head
-       * that was send to the client. It is the only
+       * that will be send to the client. It is the only
        * way to get it right for all requests, including
        * the fixed ones for out-of-memory problems.
        *
@@ -840,6 +828,7 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun
    {
       /* Deliver, log and free the interception response. */
       send_crunch_response(csp, rsp);
+      csp->flags |= CSP_FLAG_CRUNCHED;
       return TRUE;
    }
 
@@ -859,6 +848,7 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun
          {
             /* Deliver, log and free the interception response. */
             send_crunch_response(csp, rsp);
+            csp->flags |= CSP_FLAG_CRUNCHED;
 #ifdef FEATURE_STATISTICS
             if (c->flags & CF_COUNT_AS_REJECT)
             {
@@ -1567,11 +1557,16 @@ static jb_err parse_client_request(struct client_state *csp)
  *
  * Function    :  chat
  *
- * Description :  Once a connection to the client has been accepted,
+ * Description :  Once a connection from the client has been accepted,
  *                this function is called (via serve()) to handle the
- *                main business of the communication.  When this
- *                function returns, the caller must close the client
- *                socket handle.
+ *                main business of the communication.  This function
+ *                returns after dealing with a single request. It can
+ *                be called multiple times witht the same client socket
+ *                if the client is keeping the connection alive.
+ *
+ *                The decision whether or not a client connection will
+ *                be kept alive is up to the caller which also must
+ *                close the client socket when done.
  *
  *                FIXME: chat is nearly thousand lines long.
  *                Ridiculous.
@@ -1717,7 +1712,7 @@ static void chat(struct client_state *csp)
 
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
    if ((csp->server_connection.sfd != JB_INVALID_SOCKET)
-      && socket_is_still_usable(csp->server_connection.sfd)
+      && socket_is_still_alive(csp->server_connection.sfd)
       && connection_destination_matches(&csp->server_connection, http, fwd))
    {
       log_error(LOG_LEVEL_CONNECT,
@@ -2021,7 +2016,7 @@ static void chat(struct client_state *csp)
       if (FD_ISSET(csp->server_connection.sfd, &rfds))
       {
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
-         if (!socket_is_still_usable(csp->cfd))
+         if (!socket_is_still_alive(csp->cfd))
          {
 #ifdef _WIN32
             log_error(LOG_LEVEL_CONNECT,
@@ -2319,7 +2314,7 @@ static void chat(struct client_state *csp)
                {
                   log_error(LOG_LEVEL_ERROR,
                      "Empty server or forwarder response received on socket %d. "
-                     "Closing client connection %d without sending data.",
+                     "Closing client socket %d without sending data.",
                      csp->server_connection.sfd, csp->cfd);
                }
                else
@@ -2477,6 +2472,46 @@ static void chat(struct client_state *csp)
 }
 
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function    :  prepare_csp_for_next_request
+ *
+ * Description :  Put the csp in a mostly vergin state.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void prepare_csp_for_next_request(struct client_state *csp)
+{
+   csp->content_type = 0;
+   csp->content_length = 0;
+   csp->expected_content_length = 0;
+   csp->expected_client_content_length = 0;
+   list_remove_all(csp->headers);
+   freez(csp->iob->buf);
+   memset(csp->iob, 0, sizeof(csp->iob));
+   freez(csp->error_message);
+   free_http_request(csp->http);
+   destroy_list(csp->headers);
+   destroy_list(csp->tags);
+   free_current_action(csp->action);
+   if (NULL != csp->fwd)
+   {
+      unload_forward_spec(csp->fwd);
+      csp->fwd = NULL;
+   }
+   /* XXX: Store per-connection flags someplace else. */
+   csp->flags &= CSP_FLAG_TOGGLED_ON;
+   csp->flags |= CSP_FLAG_ACTIVE;
+   csp->flags |= CSP_FLAG_REUSED_CLIENT_CONNECTION;
+}
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+
 /*********************************************************************
  *
  * Function    :  serve
@@ -2509,71 +2544,64 @@ static void serve(struct client_state *csp)
 
       chat(csp);
 
+      /*
+       * If the request has been crunched,
+       * the calculated latency is zero.
+       */
       latency = (unsigned)(csp->server_connection.response_received -
          csp->server_connection.request_sent) / 2;
 
       continue_chatting = (csp->config->feature_flags
          & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
-         && (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)
-         && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)
-         && (csp->cfd != JB_INVALID_SOCKET)
-         && (csp->server_connection.sfd != JB_INVALID_SOCKET)
-         && socket_is_still_usable(csp->server_connection.sfd);
+         && (((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)
+               && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
+            || (csp->flags & CSP_FLAG_CRUNCHED))
+         && (csp->cfd != JB_INVALID_SOCKET);
 
-      if (continue_chatting)
+      if (continue_chatting && !(csp->flags & CSP_FLAG_CRUNCHED))
       {
-         if (!(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET))
+         continue_chatting = (csp->server_connection.sfd != JB_INVALID_SOCKET)
+            && socket_is_still_alive(csp->server_connection.sfd);
+         if (continue_chatting)
          {
-            csp->server_connection.keep_alive_timeout = csp->config->default_server_timeout;
-            log_error(LOG_LEVEL_CONNECT,
-               "The server didn't specify how long the connection will stay open. "
-               "Assumed timeout is: %u.", csp->server_connection.keep_alive_timeout);
+            if (!(csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET))
+            {
+               csp->server_connection.keep_alive_timeout = csp->config->default_server_timeout;
+               log_error(LOG_LEVEL_CONNECT,
+                  "The server didn't specify how long the connection will stay open. "
+                  "Assumed timeout is: %u.", csp->server_connection.keep_alive_timeout);
+            }
+            continue_chatting = (latency < csp->server_connection.keep_alive_timeout);
          }
-         continue_chatting = (latency < csp->server_connection.keep_alive_timeout);
       }
 
       if (continue_chatting)
       {
          unsigned int client_timeout;
 
-         client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency;
-
-         log_error(LOG_LEVEL_CONNECT,
-            "Waiting for the next client request on socket %d. "
-            "Keeping the server socket %d to %s open.",
-            csp->cfd, csp->server_connection.sfd, csp->server_connection.host);
+         if (csp->server_connection.sfd != JB_INVALID_SOCKET)
+         {
+            client_timeout = (unsigned)csp->server_connection.keep_alive_timeout - latency;
+            log_error(LOG_LEVEL_CONNECT,
+               "Waiting for the next client request on socket %d. "
+               "Keeping the server socket %d to %s open.",
+               csp->cfd, csp->server_connection.sfd, csp->server_connection.host);
+         }
+         else
+         {
+            client_timeout = 1; /* XXX: Use something else here? */
+            log_error(LOG_LEVEL_CONNECT,
+               "Waiting for the next client request on socket %d. "
+               "No server socket to keep open.", csp->cfd);
+         }
          if ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)
             && data_is_available(csp->cfd, (int)client_timeout)
-            && socket_is_still_usable(csp->cfd))
+            && socket_is_still_alive(csp->cfd))
          {
             log_error(LOG_LEVEL_CONNECT, "Client request arrived in "
                "time or the client closed the connection on socket %d.",
                 csp->cfd);
-            /*
-             * Get the csp in a mostly vergin state again.
-             * XXX: Should be done elsewhere.
-             */
-            csp->content_type = 0;
-            csp->content_length = 0;
-            csp->expected_content_length = 0;
-            csp->expected_client_content_length = 0;
-            list_remove_all(csp->headers);
-            freez(csp->iob->buf);
-            memset(csp->iob, 0, sizeof(csp->iob));
-            freez(csp->error_message);
-            free_http_request(csp->http);
-            destroy_list(csp->headers);
-            destroy_list(csp->tags);
-            free_current_action(csp->action);
-            if (NULL != csp->fwd)
-            {
-               unload_forward_spec(csp->fwd);
-               csp->fwd = NULL;
-            }
-
-            /* XXX: Store per-connection flags someplace else. */
-            csp->flags = CSP_FLAG_ACTIVE |
-               (csp->flags & CSP_FLAG_TOGGLED_ON) | CSP_FLAG_REUSED_CLIENT_CONNECTION;
+            prepare_csp_for_next_request(csp);
          }
          else
          {
@@ -2582,7 +2610,7 @@ static void serve(struct client_state *csp)
                 csp->cfd);
 #ifdef FEATURE_CONNECTION_SHARING
             if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
-               && (socket_is_still_usable(csp->server_connection.sfd)))
+               && (socket_is_still_alive(csp->server_connection.sfd)))
             {
                time_t time_open = time(NULL) - csp->server_connection.timestamp;
 
@@ -2951,7 +2979,7 @@ int main(int argc, 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;
+         daemon_mode = 0;
       }
 
       else if (strcmp(argv[argc_pos], "--pidfile" ) == 0)
@@ -3117,11 +3145,10 @@ int main(int argc, char **argv)
     */
 #if defined(unix)
 {
-   pid_t pid = 0;
-
-   if (!no_daemon)
+   if (daemon_mode)
    {
-      pid  = fork();
+      int fd;
+      pid_t pid = fork();
 
       if ( pid < 0 ) /* error */
       {
@@ -3152,12 +3179,43 @@ int main(int argc, char **argv)
        * stderr (fd 2) will be closed later on,
        * when the config file has been parsed.
        */
+      close(0);
+      close(1);
+
+      /*
+       * Reserve fd 0 and 1 to prevent abort() and friends
+       * from sending stuff to the clients or servers.
+       */
+      fd = open("/dev/null", O_RDONLY);
+      if (fd == -1)
+      {
+         log_error(LOG_LEVEL_FATAL, "Failed to open /dev/null: %E");
+      }
+      else if (fd != 0)
+      {
+         if (dup2(fd, 0) == -1)
+         {
+            log_error(LOG_LEVEL_FATAL, "Failed to reserve fd 0: %E");
+         }
+         close(fd);
+      }
+      fd = open("/dev/null", O_WRONLY);
+      if (fd == -1)
+      {
+         log_error(LOG_LEVEL_FATAL, "Failed to open /dev/null: %E");
+      }
+      else if (fd != 1)
+      {
+         if (dup2(fd, 1) == -1)
+         {
+            log_error(LOG_LEVEL_FATAL, "Failed to reserve fd 1: %E");
+         }
+         close(fd);
+      }
 
-      close( 0 );
-      close( 1 );
       chdir("/");
 
-   } /* -END- if (!no_daemon) */
+   } /* -END- if (daemon_mode) */
 
    /*
     * As soon as we have written the PID file, we can switch