Remove id tag
[privoxy.git] / filters.c
index a384e1b..d45ff94 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,11 +1,10 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.185 2014/06/12 13:08:25 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2014 the
+ * Copyright   :  Written by and Copyright (C) 2001-2016 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -70,17 +69,23 @@ const char filters_rcs[] = "$Id: filters.c,v 1.185 2014/06/12 13:08:25 fabiankei
 #include "deanimate.h"
 #include "urlmatch.h"
 #include "loaders.h"
+#ifdef FEATURE_CLIENT_TAGS
+#include "client-tags.h"
+#endif
 
 #ifdef _WIN32
 #include "win32.h"
 #endif
 
-const char filters_h_rcs[] = FILTERS_H_VERSION;
-
 typedef char *(*filter_function_ptr)();
 static filter_function_ptr get_filter_function(const struct client_state *csp);
-static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
 static jb_err prepare_for_filtering(struct client_state *csp);
+static void apply_url_actions(struct current_action_spec *action,
+                              struct http_request *http,
+#ifdef FEATURE_CLIENT_TAGS
+                              const struct list *client_tags,
+#endif
+                              struct url_actions *b);
 
 #ifdef FEATURE_ACL
 #ifdef HAVE_RFC2553
@@ -97,17 +102,15 @@ static jb_err prepare_for_filtering(struct client_state *csp);
  *          3  :  len  = length of IP address in octets
  *          4  :  port = port number in network order;
  *
- * Returns     :  0 = no errror; -1 otherwise.
+ * Returns     :  void
  *
  *********************************************************************/
-static int sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
-                                  uint8_t **ip, unsigned int *len,
-                                  in_port_t **port)
+static void sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
+                                   uint8_t **ip, unsigned int *len,
+                                   in_port_t **port)
 {
-   if (NULL == addr)
-   {
-      return(-1);
-   }
+   assert(NULL != addr);
+   assert(addr->ss_family == AF_INET || addr->ss_family == AF_INET6);
 
    switch (addr->ss_family)
    {
@@ -142,12 +145,7 @@ static int sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
          }
          break;
 
-      default:
-         /* Unsupported address family */
-         return(-1);
    }
-
-   return(0);
 }
 
 
@@ -347,12 +345,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
     * Use a temporary acl spec copy so we can log
     * the unmodified original in case of parse errors.
     */
-   acl_spec = strdup(aspec);
-   if (acl_spec == NULL)
-   {
-      /* XXX: This will be logged as parse error. */
-      return(-1);
-   }
+   acl_spec = strdup_or_die(aspec);
 
    if ((p = strchr(acl_spec, '/')) != NULL)
    {
@@ -391,13 +384,19 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
    {
       p = strchr(acl_spec, ':');
    }
+   if (p != NULL)
+   {
+      assert(*p == ':');
+      *p = '\0';
+      p++;
+   }
 
 #ifdef HAVE_RFC2553
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
 
-   i = getaddrinfo(acl_spec, ((p) ? ++p : NULL), &hints, &result);
+   i = getaddrinfo(acl_spec, p, &hints, &result);
 
    if (i != 0)
    {
@@ -416,7 +415,6 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
    {
       char *endptr;
 
-      *p++ = '\0';
       port = strtol(p, &endptr, 10);
 
       if (port <= 0 || port > 65535 || *endptr != '\0')
@@ -447,10 +445,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
    }
 
    aca->mask.ss_family = aca->addr.ss_family;
-   if (sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port))
-   {
-      return(-1);
-   }
+   sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port);
 
    if (p)
    {
@@ -582,12 +577,7 @@ struct http_response *block_url(struct client_state *csp)
       /* and handle accordingly: */
       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
       {
-         rsp->status = strdup("403 Request blocked by Privoxy");
-         if (rsp->status == NULL)
-         {
-            free_http_response(rsp);
-            return cgi_error_memory();
-         }
+         rsp->status = strdup_or_die("403 Request blocked by Privoxy");
          rsp->body = bindup(image_pattern_data, image_pattern_length);
          if (rsp->body == NULL)
          {
@@ -604,12 +594,7 @@ struct http_response *block_url(struct client_state *csp)
       }
       else if (0 == strcmpic(p, "blank"))
       {
-         rsp->status = strdup("403 Request blocked by Privoxy");
-         if (rsp->status == NULL)
-         {
-            free_http_response(rsp);
-            return cgi_error_memory();
-         }
+         rsp->status = strdup_or_die("403 Request blocked by Privoxy");
          rsp->body = bindup(image_blank_data, image_blank_length);
          if (rsp->body == NULL)
          {
@@ -626,12 +611,7 @@ struct http_response *block_url(struct client_state *csp)
       }
       else
       {
-         rsp->status = strdup("302 Local Redirect from Privoxy");
-         if (rsp->status == NULL)
-         {
-            free_http_response(rsp);
-            return cgi_error_memory();
-         }
+         rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
 
          if (enlist_unique_header(rsp->headers, "Location", p))
          {
@@ -651,7 +631,7 @@ struct http_response *block_url(struct client_state *csp)
       new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
 
       freez(rsp->body);
-      rsp->body = strdup(" ");
+      rsp->body = strdup_or_die(" ");
       rsp->content_length = 1;
 
       if (csp->config->feature_flags & RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK)
@@ -662,18 +642,13 @@ struct http_response *block_url(struct client_state *csp)
           * Return a 200 OK status for pages blocked with +handle-as-empty-document
           * if the "handle-as-empty-doc-returns-ok" runtime config option is set.
           */
-         rsp->status = strdup("200 Request blocked by Privoxy");
+         rsp->status = strdup_or_die("200 Request blocked by Privoxy");
       }
       else
       {
-         rsp->status = strdup("403 Request blocked by Privoxy");
+         rsp->status = strdup_or_die("403 Request blocked by Privoxy");
       }
 
-      if (rsp->status == NULL)
-      {
-         free_http_response(rsp);
-         return cgi_error_memory();
-      }
       if (new_content_type != 0)
       {
          log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type);
@@ -693,12 +668,7 @@ struct http_response *block_url(struct client_state *csp)
       jb_err err;
       struct map * exports;
 
-      rsp->status = strdup("403 Request blocked by Privoxy");
-      if (rsp->status == NULL)
-      {
-         free_http_response(rsp);
-         return cgi_error_memory();
-      }
+      rsp->status = strdup_or_die("403 Request blocked by Privoxy");
 
       exports = default_exports(csp, NULL);
       if (exports == NULL)
@@ -802,9 +772,9 @@ struct http_response *trust_url(struct client_state *csp)
       return cgi_error_memory();
    }
 
-   rsp->status = strdup("403 Request blocked by Privoxy");
+   rsp->status = strdup_or_die("403 Request blocked by Privoxy");
    exports = default_exports(csp, NULL);
-   if (exports == NULL || rsp->status == NULL)
+   if (exports == NULL)
    {
       free_http_response(rsp);
       return cgi_error_memory();
@@ -836,7 +806,7 @@ struct http_response *trust_url(struct client_state *csp)
    /*
     * Export the trust list
     */
-   p = strdup("");
+   p = strdup_or_die("");
    for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
    {
       snprintf(buf, sizeof(buf), "<li>%s</li>\n", t->spec);
@@ -858,7 +828,7 @@ struct http_response *trust_url(struct client_state *csp)
    {
       struct list_entry *l;
 
-      p = strdup("");
+      p = strdup_or_die("");
       for (l = csp->config->trust_info->first; l ; l = l->next)
       {
          snprintf(buf, sizeof(buf), "<li> <a href=\"%s\">%s</a><br>\n", l->str, l->str);
@@ -951,6 +921,7 @@ pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const st
       {"path",   csp->http->path,  1},
       {"host",   csp->http->host,  1},
       {"origin", csp->ip_addr_str, 1},
+      {"listen-address", csp->listen_addr_str, 1},
       {NULL,     NULL,             1}
    };
 
@@ -1131,14 +1102,8 @@ char *get_last_url(char *subject, const char *redirect_mode)
          }
          if (NULL != url_segment)
          {
-            url_segment = strdup(url_segment);
+            url_segment = strdup_or_die(url_segment);
             freez(dtoken);
-            if (url_segment == NULL)
-            {
-               log_error(LOG_LEVEL_ERROR,
-                  "Out of memory while searching for redirects.");
-               return NULL;
-            }
             break;
          }
          freez(dtoken);
@@ -1324,8 +1289,8 @@ struct http_response *redirect_url(struct client_state *csp)
             return cgi_error_memory();
          }
 
-         if (enlist_unique_header(rsp->headers, "Location", new_url)
-           || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))))
+         rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
+         if (enlist_unique_header(rsp->headers, "Location", new_url))
          {
             freez(new_url);
             free_http_response(rsp);
@@ -1349,42 +1314,18 @@ struct http_response *redirect_url(struct client_state *csp)
  *
  * Function    :  is_imageurl
  *
- * Description :  Given a URL, decide whether it is an image or not,
- *                using either the info from a previous +image action
- *                or, #ifdef FEATURE_IMAGE_DETECT_MSIE, and the browser
- *                is MSIE and not on a Mac, tell from the browser's accept
- *                header.
+ * Description :  Given a URL, decide whether it should be treated
+ *                as image URL or not.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *
- * Returns     :  True (nonzero) if URL is an image, false (0)
+ * Returns     :  True (nonzero) if URL is an image URL, false (0)
  *                otherwise
  *
  *********************************************************************/
 int is_imageurl(const struct client_state *csp)
 {
-#ifdef FEATURE_IMAGE_DETECT_MSIE
-   char *tmp;
-
-   tmp = get_header_value(csp->headers, "User-Agent:");
-   if (tmp && strstr(tmp, "MSIE") && !strstr(tmp, "Mac_"))
-   {
-      tmp = get_header_value(csp->headers, "Accept:");
-      if (tmp && strstr(tmp, "image/gif"))
-      {
-         /* Client will accept HTML.  If this seems counterintuitive,
-          * blame Microsoft.
-          */
-         return(0);
-      }
-      else
-      {
-         return(1);
-      }
-   }
-#endif /* def FEATURE_IMAGE_DETECT_MSIE */
-
    return ((csp->action->flags & ACTION_IMAGE) != 0);
 
 }
@@ -1470,7 +1411,7 @@ int is_untrusted_url(const struct client_state *csp)
          {
             char * path;
             char * path_end;
-            char * new_entry = strdup("~");
+            char * new_entry = strdup_or_die("~");
 
             string_append(&new_entry, csp->http->hostport);
 
@@ -1793,6 +1734,7 @@ static void set_privoxy_variables(const struct client_state *csp)
       { "PRIVOXY_PATH",   csp->http->path  },
       { "PRIVOXY_HOST",   csp->http->host  },
       { "PRIVOXY_ORIGIN", csp->ip_addr_str },
+      { "PRIVOXY_LISTEN_ADDRESS", csp->listen_addr_str },
    };
 
    for (i = 0; i < SZ(env); i++)
@@ -1876,6 +1818,7 @@ static char *execute_external_filter(const struct client_state *csp,
    {
       log_error(LOG_LEVEL_ERROR, "fwrite(..., %d, 1, ..) failed: %E", *size);
       unlink(file_name);
+      fclose(fp);
       return NULL;
    }
    fclose(fp);
@@ -1906,7 +1849,8 @@ static char *execute_external_filter(const struct client_state *csp,
       return NULL;
    }
 
-   filter_output = malloc_or_die(*size);
+   /* Allocate at least one byte */
+   filter_output = malloc_or_die(*size + 1);
 
    new_size = 0;
    while (!feof(fp) && !ferror(fp))
@@ -1920,7 +1864,7 @@ static char *execute_external_filter(const struct client_state *csp,
          char *p;
 
          /* Could be considered wasteful if the content is 'large'. */
-         *size = (*size != 0) ? *size * 2 : READ_LENGTH;
+         *size += (*size >= READ_LENGTH) ? *size : READ_LENGTH;
 
          p = realloc(filter_output, *size);
          if (p == NULL)
@@ -1931,6 +1875,7 @@ static char *execute_external_filter(const struct client_state *csp,
          }
          filter_output = p;
       }
+      assert(new_size + READ_LENGTH < *size);
       len = fread(&filter_output[new_size], 1, READ_LENGTH, fp);
       if (len > 0)
       {
@@ -1974,7 +1919,11 @@ static char *execute_external_filter(const struct client_state *csp,
  *                or NULL in case something went wrong.
  *
  *********************************************************************/
+#ifdef FUZZ
+char *gif_deanimate_response(struct client_state *csp)
+#else
 static char *gif_deanimate_response(struct client_state *csp)
+#endif
 {
    struct binbuffer *in, *out;
    char *p;
@@ -1982,12 +1931,8 @@ static char *gif_deanimate_response(struct client_state *csp)
 
    size = (size_t)(csp->iob->eod - csp->iob->cur);
 
-   if (  (NULL == (in =  (struct binbuffer *)zalloc(sizeof *in )))
-      || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) )
-   {
-      log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)");
-      return NULL;
-   }
+   in =  zalloc_or_die(sizeof(*in));
+   out = zalloc_or_die(sizeof(*out));
 
    in->buffer = csp->iob->cur;
    in->size = size;
@@ -2044,7 +1989,6 @@ static filter_function_ptr get_filter_function(const struct client_state *csp)
     * the content type and action settings.
     */
    if ((csp->content_type & CT_TEXT) &&
-       (csp->rlist != NULL) &&
        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
    {
       filter_function = pcrs_filter_response;
@@ -2077,11 +2021,22 @@ static filter_function_ptr get_filter_function(const struct client_state *csp)
  *                JB_ERR_PARSE otherwise
  *
  *********************************************************************/
+#ifdef FUZZ
+extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
+#else
 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
+#endif
 {
    size_t newsize = 0;
    unsigned int chunksize = 0;
    char *from_p, *to_p;
+   const char *end_of_buffer = buffer + *size;
+
+   if (*size == 0)
+   {
+      log_error(LOG_LEVEL_FATAL, "Invalid chunked input. Buffer is empty.");
+      return JB_ERR_PARSE;
+   }
 
    assert(buffer);
    from_p = to_p = buffer;
@@ -2094,27 +2049,62 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
 
    while (chunksize > 0U)
    {
+      /*
+       * If the chunk-size is valid, we should have at least
+       * chunk-size bytes of chunk-data and five bytes of
+       * meta data (chunk-size, CRLF, CRLF) left in the buffer.
+       */
+      if (chunksize + 5 >= *size - newsize)
+      {
+         log_error(LOG_LEVEL_ERROR,
+            "Chunk size %u exceeds buffered data left. "
+            "Already digested %u of %u buffered bytes.",
+            chunksize, (unsigned int)newsize, (unsigned int)*size);
+         return JB_ERR_PARSE;
+      }
+
+      /*
+       * Skip the chunk-size, the optional chunk-ext and the CRLF
+       * that is supposed to be located directly before the start
+       * of chunk-data.
+       */
       if (NULL == (from_p = strstr(from_p, "\r\n")))
       {
          log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding");
          return JB_ERR_PARSE;
       }
+      from_p += 2;
 
-      if (chunksize >= *size - newsize)
+      /*
+       * The previous strstr() does not enforce chunk-validity
+       * and is sattisfied as long a CRLF is left in the buffer.
+       *
+       * Make sure the bytes we consider chunk-data are within
+       * the valid range.
+       */
+      if (from_p + chunksize >= end_of_buffer)
       {
          log_error(LOG_LEVEL_ERROR,
-            "Chunk size %u exceeds buffered data left. "
-            "Already digested %u of %u buffered bytes.",
-            chunksize, (unsigned int)newsize, (unsigned int)*size);
+            "End of chunk is beyond the end of the buffer.");
          return JB_ERR_PARSE;
       }
-      newsize += chunksize;
-      from_p += 2;
 
       memmove(to_p, from_p, (size_t) chunksize);
+      newsize += chunksize;
       to_p = buffer + newsize;
-      from_p += chunksize + 2;
+      from_p += chunksize;
 
+      /*
+       * Not merging this check with the previous one allows us
+       * to keep chunks without trailing CRLF. It's not clear
+       * if we actually have to care about those, though.
+       */
+      if (from_p + 2 >= end_of_buffer)
+      {
+         log_error(LOG_LEVEL_ERROR, "Not enough room for trailing CRLF.");
+         return JB_ERR_PARSE;
+      }
+      from_p += 2;
       if (sscanf(from_p, "%x", &chunksize) != 1)
       {
          log_error(LOG_LEVEL_INFO, "Invalid \"chunked\" transfer encoding detected and ignored.");
@@ -2257,7 +2247,6 @@ char *execute_content_filters(struct client_state *csp)
 
 #ifdef FEATURE_EXTERNAL_FILTERS
    if ((csp->content_type & CT_TEXT) &&
-       (csp->rlist != NULL) &&
        !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]))
    {
       struct list_entry *filtername;
@@ -2272,7 +2261,15 @@ char *execute_content_filters(struct client_state *csp)
       for (filtername = csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]->first;
            filtername ; filtername = filtername->next)
       {
-         content = execute_external_filter(csp, filtername->str, content, &size);
+         char *result = execute_external_filter(csp, filtername->str, content, &size);
+         if (result != NULL)
+         {
+            if (content != csp->iob->cur)
+            {
+               free(content);
+            }
+            content = result;
+         }
       }
       csp->flags |= CSP_FLAG_MODIFIED;
       csp->content_length = size;
@@ -2312,13 +2309,16 @@ void get_url_actions(struct client_state *csp, struct http_request *http)
          return;
       }
 
+#ifdef FEATURE_CLIENT_TAGS
+      apply_url_actions(csp->action, http, csp->client_tags, b);
+#else
       apply_url_actions(csp->action, http, b);
+#endif
    }
 
    return;
 }
 
-
 /*********************************************************************
  *
  * Function    :  apply_url_actions
@@ -2328,14 +2328,18 @@ void get_url_actions(struct client_state *csp, struct http_request *http)
  * Parameters  :
  *          1  :  action = Destination.
  *          2  :  http = Current URL
- *          3  :  b = list of URL actions to apply
+ *          3  :  client_tags = list of client tags
+ *          4  :  b = list of URL actions to apply
  *
  * Returns     :  N/A
  *
  *********************************************************************/
-void apply_url_actions(struct current_action_spec *action,
-                       struct http_request *http,
-                       struct url_actions *b)
+static void apply_url_actions(struct current_action_spec *action,
+                              struct http_request *http,
+#ifdef FEATURE_CLIENT_TAGS
+                              const struct list *client_tags,
+#endif
+                              struct url_actions *b)
 {
    if (b == NULL)
    {
@@ -2349,6 +2353,12 @@ void apply_url_actions(struct current_action_spec *action,
       {
          merge_current_action(action, b->action);
       }
+#ifdef FEATURE_CLIENT_TAGS
+      if (client_tag_match(b->url, client_tags))
+      {
+         merge_current_action(action, b->action);
+      }
+#endif
    }
 }
 
@@ -2379,7 +2389,7 @@ void apply_url_actions(struct current_action_spec *action,
  *                Invalid syntax is fatal.
  *
  *********************************************************************/
-const static struct forward_spec *get_forward_override_settings(struct client_state *csp)
+static const struct forward_spec *get_forward_override_settings(struct client_state *csp)
 {
    const char *forward_override_line = csp->action->string[ACTION_STRING_FORWARD_OVERRIDE];
    char forward_settings[BUFFER_SIZE];
@@ -2410,14 +2420,7 @@ const static struct forward_spec *get_forward_override_settings(struct client_st
     * the lifetime of this request. Save its location
     * in csp as well, so sweep() can free it later on.
     */
-   fwd = csp->fwd = zalloc(sizeof(*fwd));
-   if (NULL == fwd)
-   {
-      log_error(LOG_LEVEL_FATAL,
-         "can't allocate memory for forward-override{%s}", forward_override_line);
-      /* Never get here - LOG_LEVEL_FATAL causes program exit */
-      return NULL;
-   }
+   fwd = csp->fwd = zalloc_or_die(sizeof(*fwd));
 
    vec_count = ssplit(forward_settings, " \t", vec, SZ(vec));
    if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
@@ -2427,6 +2430,14 @@ const static struct forward_spec *get_forward_override_settings(struct client_st
       /* Parse the parent HTTP proxy host:port */
       http_parent = vec[1];
 
+   }
+   else if ((vec_count == 2) && !strcasecmp(vec[0], "forward-webserver"))
+   {
+      fwd->type = FORWARD_WEBSERVER;
+
+      /* Parse the parent HTTP server host:port */
+      http_parent = vec[1];
+
    }
    else if (vec_count == 3)
    {
@@ -2582,12 +2593,7 @@ struct http_response *direct_response(struct client_state *csp)
                   return cgi_error_memory();
                }
 
-               if (NULL == (rsp->status = strdup("501 Not Implemented")))
-               {
-                  free_http_response(rsp);
-                  return cgi_error_memory();
-               }
-
+               rsp->status = strdup_or_die("501 Not Implemented");
                rsp->is_static = 1;
                rsp->crunch_reason = UNSUPPORTED;
 
@@ -2659,7 +2665,6 @@ int content_requires_filtering(struct client_state *csp)
     * the content type and action settings.
     */
    if ((csp->content_type & CT_TEXT) &&
-       (csp->rlist != NULL) &&
        (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]) ||
         !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])))
    {