Change csp->expected_content and_csp->expected_content_length from
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index 58f1a5a..07f97de 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.215 2008/12/24 17:06:19 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.229 2009/03/07 11:17:01 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -6,7 +6,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.215 2008/12/24 17:06:19 fabiankeil Exp $"
  * Purpose     :  Main file.  Contains main() method, main loop, and
  *                the main connection-handling function.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2008 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2009 the SourceForge
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -33,6 +33,61 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.215 2008/12/24 17:06:19 fabiankeil Exp $"
  *
  * Revisions   :
  *    $Log: jcc.c,v $
+ *    Revision 1.229  2009/03/07 11:17:01  fabiankeil
+ *    Fix compiler warning.
+ *
+ *    Revision 1.228  2009/03/06 20:30:13  fabiankeil
+ *    Log unsigned values as such.
+ *
+ *    Revision 1.227  2009/03/02 19:18:11  fabiankeil
+ *    Streamline parse_http_request()'s prototype. As
+ *    cparser pointed out it doesn't actually use csp.
+ *
+ *    Revision 1.226  2009/03/01 18:28:24  fabiankeil
+ *    Help clang understand that we aren't dereferencing
+ *    NULL pointers here.
+ *
+ *    Revision 1.225  2009/02/19 18:09:32  fabiankeil
+ *    Unbreak build without FEATURE_CONNECTION_KEEP_ALIVE.
+ *    Noticed by David.
+ *
+ *    Revision 1.224  2009/02/14 15:32:04  fabiankeil
+ *    Add the request URL to the timeout message in chat().
+ *    Suggested by Lee.
+ *
+ *    Revision 1.223  2009/02/09 21:21:16  fabiankeil
+ *    Now that init_log_module() is called earlier, call show_version()
+ *    later on from main() directly so it doesn't get called for --help
+ *    or --version.
+ *
+ *    Revision 1.222  2009/02/08 12:56:51  fabiankeil
+ *    Call initialize_mutexes() before init_log_module() again.
+ *    Broken since r220, might be the cause of Lee's #2579448.
+ *
+ *    Revision 1.221  2009/02/06 18:02:58  fabiankeil
+ *    When dropping privileges, also give up membership in supplementary
+ *    groups. Thanks to Matthias Drochner for reporting the problem,
+ *    providing the initial patch and testing the final version.
+ *
+ *    Revision 1.220  2009/02/04 18:29:07  fabiankeil
+ *    Initialize the log module before parsing arguments.
+ *    Thanks to Matthias Drochner for the report.
+ *
+ *    Revision 1.219  2009/01/31 16:08:21  fabiankeil
+ *    Remove redundant error check in receive_client_request().
+ *
+ *    Revision 1.218  2009/01/31 12:25:54  fabiankeil
+ *    Flatten indentation in receive_client_request().
+ *
+ *    Revision 1.217  2009/01/07 19:50:09  fabiankeil
+ *    - If the socket-timeout has been reached and the client
+ *      hasn't received any data yet, send an explanation before
+ *      closing the connection.
+ *    - In get_request_line(), signal timeouts the right way.
+ *
+ *    Revision 1.216  2008/12/24 22:13:11  ler762
+ *    fix GCC 3.4.4 warning
+ *
  *    Revision 1.215  2008/12/24 17:06:19  fabiankeil
  *    Keep a thread around to timeout alive connections
  *    even if no new requests are coming in.
@@ -1388,6 +1443,14 @@ static const char MESSED_UP_REQUEST_RESPONSE[] =
    "Connection: close\r\n\r\n"
    "Bad request. Messed up with header filters.\r\n";
 
+/* XXX: should be a template */
+static const char CONNECTION_TIMEOUT_RESPONSE[] =
+   "HTTP/1.0 502 Connection timeout\r\n"
+   "Proxy-Agent: Privoxy " VERSION "\r\n"
+   "Content-Type: text/plain\r\n"
+   "Connection: close\r\n\r\n"
+   "The connection timed out.\r\n";
+
 /* A function to crunch a response */
 typedef struct http_response *(*crunch_func_ptr)(struct client_state *);
 
@@ -1838,7 +1901,7 @@ static void send_crunch_response(const struct client_state *csp, struct http_res
 
       /* Log that the request was crunched and why. */
       log_error(LOG_LEVEL_CRUNCH, "%s: %s", crunch_reason(rsp), http->url);
-      log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %d",
+      log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %u",
          csp->ip_addr_str, http->ocmd, status_code, rsp->content_length);
 
       /* Clean up and return */
@@ -1895,7 +1958,7 @@ static int request_contains_null_bytes(const struct client_state *csp, char *buf
       } 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);
+         "(length=%d, strlen=%u).", csp->ip_addr_str, len, c_len);
       log_error(LOG_LEVEL_HEADER, 
          "Offending request data with NULL bytes turned into \'°\' characters: %s", buf);
 
@@ -2064,7 +2127,7 @@ static jb_err change_request_destination(struct client_state *csp)
 
    log_error(LOG_LEVEL_INFO, "Rewrite detected: %s", csp->headers->first->str);
    free_http_request(http);
-   err = parse_http_request(csp->headers->first->str, http, csp);
+   err = parse_http_request(csp->headers->first->str, http);
    if (JB_ERR_OK != err)
    {
       log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.",
@@ -2101,9 +2164,10 @@ static jb_err change_request_destination(struct client_state *csp)
  *                FALSE otherwise.
  *
  *********************************************************************/
-static int server_response_is_complete(struct client_state *csp, size_t content_length)
+static int server_response_is_complete(struct client_state *csp,
+   unsigned long long content_length)
 {
-   int content_length_known = (csp->flags & CSP_FLAG_CONTENT_LENGTH_SET);
+   int content_length_known = !!(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET);
 
    if (!strcmpic(csp->http->gpc, "HEAD"))
    {
@@ -2127,8 +2191,38 @@ static int server_response_is_complete(struct client_state *csp, size_t content_
    return (content_length_known && ((0 == csp->expected_content_length)
             || (csp->expected_content_length <= content_length)));
 }
+
+
+/*********************************************************************
+ *
+ * Function    :  wait_for_alive_connections
+ *
+ * Description :  Waits for alive connections to timeout.
+ *
+ * Parameters  :  N/A
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void wait_for_alive_connections()
+{
+   int connections_alive = close_unusable_connections();
+
+   while (0 < connections_alive)
+   {
+      log_error(LOG_LEVEL_CONNECT,
+         "Waiting for %d connections to timeout.",
+         connections_alive);
+      sleep(60);
+      connections_alive = close_unusable_connections();
+   }
+
+   log_error(LOG_LEVEL_CONNECT, "No connections to wait for left.");
+
+}
 #endif /* FEATURE_CONNECTION_KEEP_ALIVE */
 
+
 /*********************************************************************
  *
  * Function    :  mark_server_socket_tainted
@@ -2178,7 +2272,9 @@ static char *get_request_line(struct client_state *csp)
       {
          log_error(LOG_LEVEL_ERROR,
             "Stopped waiting for the request line.");
-         return '\0';
+         write_socket(csp->cfd, CONNECTION_TIMEOUT_RESPONSE,
+            strlen(CONNECTION_TIMEOUT_RESPONSE));
+         return NULL;
       }
 
       len = read_socket(csp->cfd, buf, sizeof(buf) - 1);
@@ -2238,58 +2334,48 @@ static jb_err receive_client_request(struct client_state *csp)
    memset(buf, 0, sizeof(buf));
 
    req = get_request_line(csp);
-
-   if ((NULL != req) && ('\0' != *req))
+   if (req == NULL)
    {
-      /* Request received. Validate and parse it. */
+      return JB_ERR_PARSE;
+   }
+   assert(*req != '\0');
 
-      /* Does the request line look invalid? */
-      if (client_protocol_is_unsupported(csp, req))
-      {
-         /* 
-          * Yes. The request has already been
-          * answered with a error response, the buffers
-          * were freed and we're done with chatting.
-          */
-         return JB_ERR_PARSE;
-      }
+   if (client_protocol_is_unsupported(csp, req))
+   {
+      return JB_ERR_PARSE;
+   }
 
 #ifdef FEATURE_FORCE_LOAD
-      /*
-       * 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))
+   /*
+    * 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))
+   {
+      if (csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
       {
-         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;
-         }
+         log_error(LOG_LEVEL_FORCE,
+            "Ignored force prefix in request: \"%s\".", req);
       }
-#endif /* def FEATURE_FORCE_LOAD */
-
-      err = parse_http_request(req, http, csp);
-      if (JB_ERR_OK != err)
+      else
       {
-         log_error(LOG_LEVEL_ERROR, "Couldn't parse request: %s.", jb_err_to_string(err));
+         strclean(req, FORCE_PREFIX);
+         log_error(LOG_LEVEL_FORCE, "Enforcing request: \"%s\".", req);
+         csp->flags |= CSP_FLAG_FORCED;
       }
-
-      freez(req);
    }
+#endif /* def FEATURE_FORCE_LOAD */
 
-   if (http->cmd == NULL)
+   err = parse_http_request(req, http);
+   freez(req);
+   if (JB_ERR_OK != err)
    {
       write_socket(csp->cfd, CHEADER, strlen(CHEADER));
       /* XXX: Use correct size */
       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);
+      log_error(LOG_LEVEL_ERROR,
+         "Couldn't parse request line received from %s: %s",
+         csp->ip_addr_str, jb_err_to_string(err));
 
       free_http_request(http);
       return JB_ERR_PARSE;
@@ -2489,7 +2575,7 @@ static void chat(struct client_state *csp)
    jb_socket maxfd;
    int server_body;
    int ms_iis5_hack = 0;
-   size_t byte_count = 0;
+   unsigned long long byte_count = 0;
    int forwarded_connect_retries = 0;
    int max_forwarded_connect_retries = csp->config->forwarded_connect_retries;
    const struct forward_spec *fwd;
@@ -2524,6 +2610,7 @@ static void chat(struct client_state *csp)
    {
       log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!?  This can't happen!");
       /* Never get here - LOG_LEVEL_FATAL causes program exit */
+      return;
    }
 
    /*
@@ -2727,15 +2814,15 @@ static void chat(struct client_state *csp)
          log_error(LOG_LEVEL_CONNECT,
             "Looks like we read the last chunk together with "
             "the server headers. We better stop reading.");
-         byte_count = (size_t)(csp->iob->eod - csp->iob->cur);
+         byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur);
          csp->expected_content_length = byte_count;
          csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
       }
       if (server_body && server_response_is_complete(csp, byte_count))
       {
          log_error(LOG_LEVEL_CONNECT,
-            "Done reading from server. Expected content length: %d. "
-            "Actual content length: %d. Most recently received: %d.",
+            "Done reading from server. Expected content length: %llu. "
+            "Actual content length: %llu. Most recently received: %d.",
             csp->expected_content_length, byte_count, len);
          len = 0;
          /*
@@ -2750,7 +2837,13 @@ static void chat(struct client_state *csp)
 
       if (n == 0)
       {
-         log_error(LOG_LEVEL_ERROR, "Didn't receive data in time.");
+         log_error(LOG_LEVEL_ERROR,
+            "Didn't receive data in time: %s", http->url);
+         if ((byte_count == 0) && (http->ssl == 0))
+         {
+            write_socket(csp->cfd, CONNECTION_TIMEOUT_RESPONSE,
+               strlen(CONNECTION_TIMEOUT_RESPONSE));
+         }
          mark_server_socket_tainted(csp);
          return;
       }
@@ -2843,7 +2936,7 @@ static void chat(struct client_state *csp)
                log_error(LOG_LEVEL_CONNECT,
                   "Looks like we reached the end of the last chunk. "
                   "We better stop reading.");
-               csp->expected_content_length = byte_count + (size_t)len;
+               csp->expected_content_length = byte_count + (unsigned long long)len;
                csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
             }
          }
@@ -2912,7 +3005,8 @@ static void chat(struct client_state *csp)
                   }
 
                   if (write_socket(csp->cfd, hdr, strlen(hdr))
-                   || write_socket(csp->cfd, p != NULL ? p : csp->iob->cur, csp->content_length))
+                   || write_socket(csp->cfd,
+                         ((p != NULL) ? p : csp->iob->cur), (size_t)csp->content_length))
                   {
                      log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
                      freez(hdr);
@@ -2996,7 +3090,7 @@ static void chat(struct client_state *csp)
                    * we just flushed. len will be added a few lines below,
                    * hdrlen doesn't matter for LOG_LEVEL_CLF.
                    */
-                  byte_count = (size_t)flushed;
+                  byte_count = (unsigned long long)flushed;
                   freez(hdr);
                   content_filter = NULL;
                   server_body = 1;
@@ -3011,7 +3105,7 @@ static void chat(struct client_state *csp)
                   return;
                }
             }
-            byte_count += (size_t)len;
+            byte_count += (unsigned long long)len;
             continue;
          }
          else
@@ -3055,9 +3149,9 @@ static void chat(struct client_state *csp)
                    */
                   int header_offset = csp->iob->cur - header_start;
                   assert(csp->iob->cur >= header_start);
-                  byte_count += (size_t)(len - header_offset);
+                  byte_count += (unsigned long long)(len - header_offset);
                   log_error(LOG_LEVEL_CONNECT, "Continuing buffering headers. "
-                     "byte_count: %d. header_offset: %d. len: %d.",
+                     "byte_count: %llu. header_offset: %d. len: %d.",
                      byte_count, header_offset, len);
                   continue;
                }
@@ -3157,7 +3251,7 @@ static void chat(struct client_state *csp)
                   return;
                }
 
-               byte_count += (size_t)len;
+               byte_count += (unsigned long long)len;
             }
             else
             {
@@ -3167,7 +3261,7 @@ static void chat(struct client_state *csp)
                 */
                int header_length = csp->iob->cur - header_start;
                assert(csp->iob->cur > header_start);
-               byte_count += (size_t)(len - header_length);
+               byte_count += (unsigned long long)(len - header_length);
             }
 
             /* we're finished with the server's header */
@@ -3202,48 +3296,22 @@ static void chat(struct client_state *csp)
       csp->content_length = byte_count;
    }
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
    if ((csp->flags & CSP_FLAG_CONTENT_LENGTH_SET)
       && (csp->expected_content_length != byte_count))
    {
       log_error(LOG_LEVEL_CONNECT,
-         "Received %d bytes while expecting %d.",
+         "Received %llu bytes while expecting %llu.",
          byte_count, csp->expected_content_length);
       mark_server_socket_tainted(csp);
    }
+#endif
 
-   log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %d",
+   log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %llu",
       csp->ip_addr_str, http->ocmd, csp->content_length);
 }
 
 
-/*********************************************************************
- *
- * Function    :  wait_for_alive_connections
- *
- * Description :  Waits for alive connections to timeout.
- *
- * Parameters  :  N/A
- *
- * Returns     :  N/A
- *
- *********************************************************************/
-static void wait_for_alive_connections()
-{
-   int connections_alive = close_unusable_connections();
-
-   while (0 < connections_alive)
-   {
-      log_error(LOG_LEVEL_CONNECT,
-         "Waiting for %d connections to timeout.",
-         connections_alive);
-      sleep(60);
-      connections_alive = close_unusable_connections();
-   }
-
-   log_error(LOG_LEVEL_CONNECT, "No connections to wait for left.");
-
-}
-
 /*********************************************************************
  *
  * Function    :  serve
@@ -3547,6 +3615,12 @@ int main(int argc, const char *argv[])
 #endif
       ;
 
+   /* Prepare mutexes if supported and necessary. */
+   initialize_mutexes();
+
+   /* Enable logging until further notice. */
+   init_log_module();
+
    /*
     * Parse the command line arguments
     *
@@ -3658,6 +3732,8 @@ int main(int argc, const char *argv[])
 
    } /* -END- while (more arguments) */
 
+   show_version(Argv[0]);
+
 #if defined(unix)
    if ( *configfile != '/' )
    {
@@ -3700,12 +3776,6 @@ int main(int argc, const char *argv[])
    InitWin32();
 #endif
 
-   /* Prepare mutexes if supported and necessary. */
-   initialize_mutexes();
-
-   /* Enable logging until further notice. */
-   init_log_module(Argv[0]);
-
    random_seed = (unsigned int)time(NULL);
 #ifdef HAVE_RANDOM
    srandom(random_seed);
@@ -3839,6 +3909,17 @@ int main(int argc, const char *argv[])
       {
          log_error(LOG_LEVEL_FATAL, "Cannot setgid(): Insufficient permissions.");
       }
+      if (NULL != grp)
+      {
+         if (setgroups(1, &grp->gr_gid))
+         {
+            log_error(LOG_LEVEL_FATAL, "setgroups() failed: %E");
+         }
+      }
+      else if (initgroups(pw->pw_name, pw->pw_gid))
+      {
+         log_error(LOG_LEVEL_FATAL, "initgroups() failed: %E");
+      }
       if (do_chroot)
       {
          if (!pw->pw_dir)