Initial keep-alive support for the client socket.
[privoxy.git] / parsers.c
index 7fbed89..bef06ea 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.148 2008/11/16 12:43:49 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.154 2009/03/13 14:10:07 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -17,7 +17,7 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.148 2008/11/16 12:43:49 fabiankei
  *                   `client_if_none_match', `get_destination_from_headers',
  *                   `parse_header_time', `decompress_iob' and `server_set_cookie'.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2008 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2009 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -44,6 +44,30 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.148 2008/11/16 12:43:49 fabiankei
  *
  * Revisions   :
  *    $Log: parsers.c,v $
+ *    Revision 1.154  2009/03/13 14:10:07  fabiankeil
+ *    Fix some more harmless warnings on amd64.
+ *
+ *    Revision 1.153  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
+ *    overflows that would let us close the connection prematurely.
+ *    Bug found while investigating #2669131, reported by cyberpatrol.
+ *
+ *    Revision 1.152  2009/03/01 18:43:48  fabiankeil
+ *    Help clang understand that we aren't dereferencing
+ *    NULL pointers here.
+ *
+ *    Revision 1.151  2009/02/15 14:46:35  fabiankeil
+ *    Don't let hide-referrer{conditional-*}} pass
+ *    Referer headers without http URLs.
+ *
+ *    Revision 1.150  2008/12/04 18:12:19  fabiankeil
+ *    Fix some cparser warnings.
+ *
+ *    Revision 1.149  2008/11/21 18:39:53  fabiankeil
+ *    In case of CONNECT requests there's no point
+ *    in trying to keep the connection alive.
+ *
  *    Revision 1.148  2008/11/16 12:43:49  fabiankeil
  *    Turn keep-alive support into a runtime feature
  *    that is disabled by setting keep-alive-timeout
@@ -968,7 +992,10 @@ static jb_err client_host_adder       (struct client_state *csp);
 static jb_err client_xtra_adder       (struct client_state *csp);
 static jb_err client_x_forwarded_for_adder(struct client_state *csp);
 static jb_err client_connection_header_adder(struct client_state *csp);
-static jb_err server_connection_close_adder(struct client_state *csp);
+static jb_err server_connection_adder(struct client_state *csp);
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+static jb_err server_proxy_connection_adder(struct client_state *csp);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
 static jb_err create_forged_referrer(char **header, const char *hostport);
 static jb_err create_fake_referrer(char **header, const char *fake_referrer);
@@ -1002,7 +1029,9 @@ static const struct parsers client_patterns[] = {
    { "TE:",                       3,   client_te },
    { "Host:",                     5,   client_host },
    { "if-modified-since:",       18,   client_if_modified_since },
+#ifndef FEATURE_CONNECTION_KEEP_ALIVE
    { "Keep-Alive:",              11,   crumble },
+#endif
    { "connection:",              11,   client_connection },
    { "proxy-connection:",        17,   crumble },
    { "max-forwards:",            13,   client_max_forwards },
@@ -1026,9 +1055,10 @@ static const struct parsers server_patterns[] = {
    { "Content-Encoding:",        17, server_content_encoding },
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
    { "Content-Length:",          15, server_save_content_length },
+#else
+   { "Keep-Alive:",              11, crumble },
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    { "Transfer-Encoding:",       18, server_transfer_coding },
-   { "Keep-Alive:",              11, crumble },
    { "content-disposition:",     20, server_content_disposition },
    { "Last-Modified:",           14, server_last_modified },
    { "*",                         0, crunch_server_header },
@@ -1046,7 +1076,10 @@ static const add_header_func_ptr add_client_headers[] = {
 };
 
 static const add_header_func_ptr add_server_headers[] = {
-   server_connection_close_adder,
+   server_connection_adder,
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   server_proxy_connection_adder,
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    NULL
 };
 
@@ -1068,9 +1101,9 @@ static const add_header_func_ptr add_server_headers[] = {
  *                file, the results are not portable.
  *
  *********************************************************************/
-int flush_socket(jb_socket fd, struct iob *iob)
+long flush_socket(jb_socket fd, struct iob *iob)
 {
-   int len = iob->eod - iob->cur;
+   long len = iob->eod - iob->cur;
 
    if (len <= 0)
    {
@@ -1103,7 +1136,7 @@ int flush_socket(jb_socket fd, struct iob *iob)
  *                or buffer limit reached.
  *
  *********************************************************************/
-jb_err add_to_iob(struct client_state *csp, char *buf, int n)
+jb_err add_to_iob(struct client_state *csp, char *buf, long n)
 {
    struct iob *iob = csp->iob;
    size_t used, offset, need, want;
@@ -1204,7 +1237,7 @@ jb_err decompress_iob(struct client_state *csp)
 
    cur = csp->iob->cur;
 
-   if (bufsize < 10)
+   if (bufsize < (size_t)10)
    {
       /*
        * This is to protect the parsing of gzipped data,
@@ -1459,7 +1492,7 @@ jb_err decompress_iob(struct client_state *csp)
           */
          assert(zstr.avail_out == tmpbuf + bufsize - (char *)zstr.next_out);
          assert((char *)zstr.next_out == tmpbuf + ((char *)oldnext_out - buf));
-         assert(zstr.avail_out > 0);
+         assert(zstr.avail_out > 0U);
 
          buf = tmpbuf;
       }
@@ -1511,7 +1544,7 @@ jb_err decompress_iob(struct client_state *csp)
     && (csp->iob->eod <= csp->iob->buf + csp->iob->size))
    {
       const size_t new_size = (size_t)(csp->iob->eod - csp->iob->cur);
-      if (new_size > 0)
+      if (new_size > (size_t)0)
       {
          log_error(LOG_LEVEL_RE_FILTER,
             "Decompression successful. Old size: %d, new size: %d.",
@@ -1744,6 +1777,7 @@ static char *get_header_line(struct iob *iob)
       /* FIXME No way to handle error properly */
       log_error(LOG_LEVEL_FATAL, "Out of memory in get_header_line()");
    }
+   assert(ret != NULL);
 
    iob->cur = p+1;
 
@@ -2091,6 +2125,7 @@ static jb_err header_tagger(struct client_state *csp, char *header)
                      if (0 > hits)
                      {
                         /* Regex failure, log it but continue anyway. */
+                        assert(NULL != header);
                         log_error(LOG_LEVEL_ERROR,
                            "Problems with tagger \'%s\' and header \'%s\': %s",
                            b->name, *header, pcrs_strerror(hits));
@@ -2348,9 +2383,9 @@ static jb_err filter_header(struct client_state *csp, char **header)
  *
  * Function    :  server_connection
  *
- * Description :  Makes sure that the value of the Connection: header
- *                is "close" and signals server_connection_close_adder 
- *                to do nothing.
+ * Description :  Makes sure a proper "Connection:" header is
+ *                set and signals connection_header_adder to
+ *                do nothing.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -2365,8 +2400,6 @@ static jb_err filter_header(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err server_connection(struct client_state *csp, char **header)
 {
-   char *old_header = *header;
-
    /* Do we have a 'Connection: close' header? */
    if (strcmpic(*header, "Connection: close"))
    {
@@ -2378,7 +2411,10 @@ static jb_err server_connection(struct client_state *csp, char **header)
          /* Remember to keep the connection alive. */
          csp->flags |= CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE;
       }
-#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+      log_error(LOG_LEVEL_HEADER,
+         "Keeping the server header '%s' around.", *header);
+#else
+      char *old_header = *header;
 
       *header = strdup("Connection: close");
       if (header == NULL)
@@ -2387,10 +2423,11 @@ static jb_err server_connection(struct client_state *csp, char **header)
       }
       log_error(LOG_LEVEL_HEADER, "Replaced: \'%s\' with \'%s\'", old_header, *header);
       freez(old_header);
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
    }
 
-   /* Signal server_connection_close_adder() to return early. */
-   csp->flags |= CSP_FLAG_SERVER_CONNECTION_CLOSE_SET;
+   /* Signal server_connection_adder() to return early. */
+   csp->flags |= CSP_FLAG_SERVER_CONNECTION_HEADER_SET;
 
    return JB_ERR_OK;
 }
@@ -2416,11 +2453,18 @@ static jb_err server_connection(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err client_connection(struct client_state *csp, char **header)
 {
-   char *old_header = *header;
    const char *wanted_header = get_appropiate_connection_header(csp);
 
    if (strcmpic(*header, wanted_header))
    {
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+      log_error(LOG_LEVEL_HEADER,
+         "Keeping the client header '%s' around. "
+         "The connection will not be kept alive.",
+         *header);
+#else
+      char *old_header = *header;
+
       *header = strdup(wanted_header);
       if (header == NULL)
       { 
@@ -2429,9 +2473,20 @@ static jb_err client_connection(struct client_state *csp, char **header)
       log_error(LOG_LEVEL_HEADER,
          "Replaced: \'%s\' with \'%s\'", old_header, *header);
       freez(old_header);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
    }
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   else
+   {
+      log_error(LOG_LEVEL_HEADER,
+         "Keeping the client header '%s' around. "
+         "The server connection will be kept alive if possible.",
+         *header);
+      csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+   }
+#endif  /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
-   /* Signal client_connection_close_adder() to return early. */
+   /* Signal client_connection_adder() to return early. */
    csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET;
 
    return JB_ERR_OK;
@@ -2457,6 +2512,7 @@ static jb_err client_connection(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err crumble(struct client_state *csp, char **header)
 {
+   (void)csp;
    log_error(LOG_LEVEL_HEADER, "crumble crunched: %s!", *header);
    freez(*header);
    return JB_ERR_OK;
@@ -2812,11 +2868,11 @@ static jb_err server_adjust_content_length(struct client_state *csp, char **head
  *********************************************************************/
 static jb_err server_save_content_length(struct client_state *csp, char **header)
 {
-   unsigned int content_length = 0;
+   unsigned long long content_length = 0;
 
    assert(*(*header+14) == ':');
 
-   if (1 != sscanf(*header+14, ": %u", &content_length))
+   if (1 != sscanf(*header+14, ": %llu", &content_length))
    {
       log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header);
       freez(*header);
@@ -3563,12 +3619,13 @@ static jb_err client_max_forwards(struct client_state *csp, char **header)
        (0 == strcmpic(csp->http->gpc, "options")))
    {
       assert(*(*header+12) == ':');
-      if (1 == sscanf(*header+12, ": %u", &max_forwards))
+      if (1 == sscanf(*header+12, ": %d", &max_forwards))
       {
          if (max_forwards > 0)
          {
-            snprintf(*header, strlen(*header)+1, "Max-Forwards: %u", --max_forwards);
-            log_error(LOG_LEVEL_HEADER, "Max-Forwards value for %s request reduced to %u.",
+            snprintf(*header, strlen(*header)+1, "Max-Forwards: %d", --max_forwards);
+            log_error(LOG_LEVEL_HEADER,
+               "Max-Forwards value for %s request reduced to %d.",
                csp->http->gpc, max_forwards);
          }
          else if (max_forwards < 0)
@@ -4062,14 +4119,11 @@ static jb_err client_x_forwarded_for_adder(struct client_state *csp)
 
 /*********************************************************************
  *
- * Function    :  server_connection_close_adder
+ * Function    :  server_connection_adder
  *
- * Description :  "Temporary" fix for the needed but missing HTTP/1.1
- *                support. Adds a "Connection: close" header to csp->headers
+ * Description :  Adds an appropiate "Connection:" header to csp->headers
  *                unless the header was already present. Called from `sed'.
  *
- *                FIXME: This whole function shouldn't be neccessary!
- *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *
@@ -4077,13 +4131,14 @@ static jb_err client_x_forwarded_for_adder(struct client_state *csp)
  *                JB_ERR_MEMORY on out-of-memory error.
  *
  *********************************************************************/
-static jb_err server_connection_close_adder(struct client_state *csp)
+static jb_err server_connection_adder(struct client_state *csp)
 {
    const unsigned int flags = csp->flags;
    const char *response_status_line = csp->headers->first->str;
+   const char *wanted_header = get_appropiate_connection_header(csp);
 
    if ((flags & CSP_FLAG_CLIENT_HEADER_PARSING_DONE)
-    && (flags & CSP_FLAG_SERVER_CONNECTION_CLOSE_SET))
+    && (flags & CSP_FLAG_SERVER_CONNECTION_HEADER_SET))
    {
       return JB_ERR_OK;
    }
@@ -4101,12 +4156,36 @@ static jb_err server_connection_close_adder(struct client_state *csp)
       csp->flags |= CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE;
    }
 
-   log_error(LOG_LEVEL_HEADER, "Adding: Connection: close");
+   log_error(LOG_LEVEL_HEADER, "Adding: %s", wanted_header);
 
-   return enlist(csp->headers, "Connection: close");
+   return enlist(csp->headers, wanted_header);
 }
 
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function    :  server_proxy_connection_adder
+ *
+ * Description :  Adds a "Proxy-Connection: keep-alive" header to
+ *                csp->headers. XXX: We should reuse existant ones.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+static jb_err server_proxy_connection_adder(struct client_state *csp)
+{
+   static const char proxy_connection_header[] = "Proxy-Connection: keep-alive";
+   log_error(LOG_LEVEL_HEADER, "Adding: %s", proxy_connection_header);
+   return enlist(csp->headers, proxy_connection_header);
+}
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
+
 /*********************************************************************
  *
  * Function    :  client_connection_header_adder
@@ -4638,6 +4717,7 @@ static jb_err handle_conditional_hide_referrer_parameter(char **header,
 {
    char *referer = strdup(*header);
    const size_t hostlenght = strlen(host);
+   const char *referer_url = NULL;
 
    if (NULL == referer)
    {
@@ -4646,7 +4726,7 @@ static jb_err handle_conditional_hide_referrer_parameter(char **header,
    }
 
    /* referer begins with 'Referer: http[s]://' */
-   if (hostlenght < (strlen(referer)-17))
+   if ((hostlenght+17) < strlen(referer))
    {
       /*
        * Shorten referer to make sure the referer is blocked
@@ -4655,9 +4735,10 @@ static jb_err handle_conditional_hide_referrer_parameter(char **header,
        */
       referer[hostlenght+17] = '\0';
    }
-   if (NULL == strstr(referer, host))
+   referer_url = strstr(referer, "http://");
+   if ((NULL == referer_url) || (NULL == strstr(referer_url, host)))
    {
-      /* Host has changed */
+      /* Host has changed, Referer is invalid or a https URL. */
       if (parameter_conditional_block)
       {
          log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header);