handle_established_connection(): Remove pointless increments of n
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index c30e75d..28e797b 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -5,8 +5,8 @@
  * Purpose     :  Main file.  Contains main() method, main loop, and
  *                the main connection-handling function.
  *
- * Copyright   :  Written by and Copyright (C) 2001-2018 the
- *                Privoxy team. http://www.privoxy.org/
+ * Copyright   :  Written by and Copyright (C) 2001-2020 the
+ *                Privoxy team. https://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
  *                by and Copyright (C) 1997 Anonymous Coders and
 #include "project.h"
 #include "list.h"
 #include "jcc.h"
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
 #include "ssl.h"
 #endif
 #include "filters.h"
@@ -150,7 +150,7 @@ static int client_protocol_is_unsupported(const struct client_state *csp, char *
 static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers);
 static jb_err get_server_headers(struct client_state *csp);
 static const char *crunch_reason(const struct http_response *rsp);
-static void send_crunch_response(const struct client_state *csp, struct http_response *rsp);
+static void send_crunch_response(struct client_state *csp, struct http_response *rsp);
 static char *get_request_line(struct client_state *csp);
 static jb_err receive_client_request(struct client_state *csp);
 static jb_err parse_client_request(struct client_state *csp);
@@ -193,12 +193,10 @@ privoxy_mutex_t log_mutex;
 privoxy_mutex_t log_init_mutex;
 privoxy_mutex_t connection_reuse_mutex;
 
-#ifdef LIMIT_MUTEX_NUMBER
-   privoxy_mutex_t certificates_mutexes[32];
-#else
-   privoxy_mutex_t certificates_mutexes[65536];
-#endif /* LIMIT_MUTEX_NUMBER */
-   privoxy_mutex_t rng_mutex;
+#ifdef FEATURE_HTTPS_INSPECTION
+privoxy_mutex_t certificate_mutex;
+privoxy_mutex_t rng_mutex;
+#endif
 
 #ifdef FEATURE_EXTERNAL_FILTERS
 privoxy_mutex_t external_filter_mutex;
@@ -809,7 +807,7 @@ static void log_applied_actions(const struct current_action_spec *actions)
  * Returns     :  Nothing.
  *
  *********************************************************************/
-static void send_crunch_response(const struct client_state *csp, struct http_response *rsp)
+static void send_crunch_response(struct client_state *csp, struct http_response *rsp)
 {
       const struct http_request *http = csp->http;
       char status_code[4];
@@ -844,13 +842,33 @@ static void send_crunch_response(const struct client_state *csp, struct http_res
          csp->ip_addr_str, http->ocmd, status_code, rsp->content_length);
 
       /* Write the answer to the client */
-      if (write_socket_delayed(csp->cfd, rsp->head, rsp->head_length, get_write_delay(csp))
-       || write_socket_delayed(csp->cfd, rsp->body, rsp->content_length, get_write_delay(csp)))
+#ifdef FEATURE_HTTPS_INSPECTION
+      if (client_use_ssl(csp))
       {
-         /* There is nothing we can do about it. */
-         log_error(LOG_LEVEL_CONNECT,
-            "Couldn't deliver the error message for %s through client socket %d: %E",
-            http->url, csp->cfd);
+         if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+                 (const unsigned char *)rsp->head, rsp->head_length) < 0)
+          || (ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+                 (const unsigned char *)rsp->body, rsp->content_length) < 0))
+         {
+            /* There is nothing we can do about it. */
+            log_error(LOG_LEVEL_CONNECT, "Couldn't deliver the error message "
+               "for %s through client socket %d using TLS/SSL",
+               http->url, csp->cfd);
+         }
+      }
+      else
+#endif
+      {
+         if (write_socket_delayed(csp->cfd, rsp->head, rsp->head_length,
+                get_write_delay(csp))
+          || write_socket_delayed(csp->cfd, rsp->body, rsp->content_length,
+                get_write_delay(csp)))
+         {
+            /* There is nothing we can do about it. */
+            log_error(LOG_LEVEL_CONNECT,
+               "Couldn't deliver the error message for %s through client socket %d: %E",
+               http->url, csp->cfd);
+         }
       }
 
       /* Clean up and return */
@@ -1994,6 +2012,367 @@ static int send_http_request(struct client_state *csp)
 }
 
 
+#ifdef FEATURE_HTTPS_INSPECTION
+/*********************************************************************
+ *
+ * Function    : receive_and_send_encrypted_post_data
+ *
+ * Description : Reads remaining POST data from the client and sends
+ *               it to the server.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static jb_err receive_and_send_encrypted_post_data(struct client_state *csp)
+{
+   int content_length_known = csp->expected_client_content_length != 0;
+
+   while (is_ssl_pending(&(csp->mbedtls_client_attr.ssl)))
+   {
+      unsigned char buf[BUFFER_SIZE];
+      int len;
+      int max_bytes_to_read = sizeof(buf);
+
+      if (content_length_known && csp->expected_client_content_length < sizeof(buf))
+      {
+         max_bytes_to_read = (int)csp->expected_client_content_length;
+      }
+      log_error(LOG_LEVEL_CONNECT,
+         "Waiting for up to %d bytes of POST data from the client.",
+         max_bytes_to_read);
+      len = ssl_recv_data(&(csp->mbedtls_client_attr.ssl), buf,
+         (unsigned)max_bytes_to_read);
+      if (len == -1)
+      {
+         return 1;
+      }
+      if (len == 0)
+      {
+         /* XXX: Does this actually happen? */
+         break;
+      }
+      log_error(LOG_LEVEL_HEADER, "Forwarding %d bytes of encrypted POST data",
+         len);
+      len = ssl_send_data(&(csp->mbedtls_server_attr.ssl), buf, (size_t)len);
+      if (len == -1)
+      {
+         return 1;
+      }
+      if (csp->expected_client_content_length != 0)
+      {
+         if (csp->expected_client_content_length >= len)
+         {
+            csp->expected_client_content_length -= (unsigned)len;
+         }
+         if (csp->expected_client_content_length == 0)
+         {
+            log_error(LOG_LEVEL_HEADER, "Forwarded the last %d bytes", len);
+            break;
+         }
+      }
+   }
+
+   log_error(LOG_LEVEL_HEADER, "Done forwarding encrypted POST data");
+
+   return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    : send_https_request
+ *
+ * Description : Sends the HTTP headers from the client request
+ *               and all the body data that has already been received.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static int send_https_request(struct client_state *csp)
+{
+   char *hdr;
+   int ret;
+   long flushed = 0;
+
+   hdr = list_to_text(csp->https_headers);
+   if (hdr == NULL)
+   {
+      /* FIXME Should handle error properly */
+      log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header");
+   }
+   list_remove_all(csp->https_headers);
+
+   /*
+    * Write the client's (modified) header to the server
+    * (along with anything else that may be in the buffer)
+    */
+   ret = ssl_send_data(&(csp->mbedtls_server_attr.ssl),
+      (const unsigned char *)hdr, strlen(hdr));
+   freez(hdr);
+
+   if (ret < 0)
+   {
+      log_error(LOG_LEVEL_CONNECT,
+         "Failed sending encrypted request headers to: %s: %E",
+         csp->http->hostport);
+      mark_server_socket_tainted(csp);
+      close_client_and_server_ssl_connections(csp);
+      return 1;
+   }
+
+   if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
+      && ((flushed = ssl_flush_socket(&(csp->mbedtls_server_attr.ssl),
+            csp->client_iob)) < 0))
+   {
+      log_error(LOG_LEVEL_CONNECT, "Failed sending request body to: %s: %E",
+         csp->http->hostport);
+      return 1;
+   }
+   if (flushed != 0)
+   {
+      if (csp->expected_client_content_length != 0)
+      {
+         if (csp->expected_client_content_length < flushed)
+         {
+            log_error(LOG_LEVEL_ERROR,
+               "Flushed %d bytes of request body while only expecting %llu",
+               flushed, csp->expected_client_content_length);
+            csp->expected_client_content_length = 0;
+         }
+         else
+         {
+            log_error(LOG_LEVEL_CONNECT,
+               "Flushed %d bytes of request body while expecting %llu",
+               flushed, csp->expected_client_content_length);
+            csp->expected_client_content_length -= (unsigned)flushed;
+            if (receive_and_send_encrypted_post_data(csp))
+            {
+               return 1;
+            }
+         }
+      }
+      else
+      {
+         log_error(LOG_LEVEL_CONNECT,
+            "Flushed %d bytes of request body", flushed);
+      }
+   }
+
+   log_error(LOG_LEVEL_CONNECT, "Encrypted request sent");
+
+   return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  receive_encrypted_request
+ *
+ * Description :  Receives an encrypted request.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  JB_ERR_OK on success,
+ *                JB_ERR_PARSE or JB_ERR_MEMORY otherwise
+ *
+ *********************************************************************/
+static jb_err receive_encrypted_request(struct client_state *csp)
+{
+   char buf[BUFFER_SIZE];
+   int len;
+   char *p;
+
+   do
+   {
+      log_error(LOG_LEVEL_HEADER, "Reading encrypted headers");
+      if (!data_is_available(csp->cfd, (int)csp->config->keep_alive_timeout))
+      {
+         log_error(LOG_LEVEL_CONNECT,
+            "Socket %d timed out while waiting for client headers", csp->cfd);
+         return JB_ERR_PARSE;
+      }
+      len = ssl_recv_data(&(csp->mbedtls_client_attr.ssl),
+         (unsigned char *)buf, sizeof(buf));
+      if (len == -1)
+      {
+         return JB_ERR_PARSE;
+      }
+      if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len))
+      {
+         return JB_ERR_MEMORY;
+      }
+      p = strstr(csp->client_iob->cur, "\r\n\r\n");
+   } while (p == NULL);
+
+   log_error(LOG_LEVEL_HEADER, "Encrypted headers received completely");
+
+   return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  process_encrypted_request
+ *
+ * Description :  Receives and parses an encrypted request.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  JB_ERR_OK on success,
+ *                JB_ERR_PARSE or JB_ERR_MEMORY otherwise
+ *
+ *********************************************************************/
+static jb_err process_encrypted_request(struct client_state *csp)
+{
+   char *p;
+   char *request_line;
+   jb_err err;
+   /* Temporary copy of the client's headers before they get enlisted in csp->https_headers */
+   struct list header_list;
+   struct list *headers = &header_list;
+
+   err = receive_encrypted_request(csp);
+   if (err != JB_ERR_OK)
+   {
+      /* XXX: Also used for JB_ERR_MEMORY */
+      ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+         (const unsigned char *)CHEADER, strlen(CHEADER));
+      return err;
+   }
+
+   /* We don't need get_request_line() because the whole HTTP head is buffered. */
+   request_line = get_header(csp->client_iob);
+   if (request_line == NULL)
+   {
+      ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+         (const unsigned char *)CHEADER, strlen(CHEADER));
+      return JB_ERR_PARSE;
+   }
+   assert(*request_line != '\0');
+
+   if (client_protocol_is_unsupported(csp, request_line))
+   {
+      ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+         (const unsigned char *)CHEADER, strlen(CHEADER));
+      return JB_ERR_PARSE;
+   }
+
+#ifdef FEATURE_FORCE_LOAD
+   if (force_required(csp, request_line))
+   {
+      csp->flags |= CSP_FLAG_FORCED;
+   }
+#endif /* def FEATURE_FORCE_LOAD */
+
+   free_http_request(csp->http);
+
+   err = parse_http_request(request_line, csp->http);
+   /* XXX: Restore ssl setting. This is ugly */
+   csp->http->client_ssl = 1;
+   csp->http->server_ssl = 1;
+
+   freez(request_line);
+   if (JB_ERR_OK != err)
+   {
+      ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+         (const unsigned char *)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,
+         "Couldn't parse request line received from %s: %s",
+         csp->ip_addr_str, jb_err_to_string(err));
+
+      free_http_request(csp->http);
+      return JB_ERR_PARSE;
+   }
+
+   /* Parse the rest of the client's headers. */
+   init_list(headers);
+   for (;;)
+   {
+      p = get_header(csp->client_iob);
+
+      if (p == NULL)
+      {
+         /* There are no additional headers to read. */
+         break;
+      }
+      enlist(headers, p);
+      freez(p);
+   }
+
+   if (JB_ERR_OK != get_destination_from_https_headers(headers, csp->http))
+   {
+      /*
+       * Our attempts to get the request destination
+       * elsewhere failed.
+       */
+      ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+         (const unsigned char *)CHEADER, strlen(CHEADER));
+      return JB_ERR_PARSE;
+   }
+
+#ifndef FEATURE_EXTENDED_HOST_PATTERNS
+   /* Split the domain we just got for pattern matching */
+   init_domain_components(csp->http);
+#endif
+
+#ifdef FEATURE_TOGGLE
+   if ((csp->flags & CSP_FLAG_TOGGLED_ON) != 0)
+#endif
+   {
+      /* Determine the actions for this URL */
+      get_url_actions(csp, csp->http);
+   }
+
+   enlist(csp->https_headers, csp->http->cmd);
+
+   /* Append the previously read headers */
+   err = list_append_list_unique(csp->https_headers, headers);
+   destroy_list(headers);
+   if (JB_ERR_OK != err)
+   {
+      /* XXX: Send error message */
+      return err;
+   }
+
+   /* XXX: Work around crash */
+   csp->error_message = NULL;
+
+   /* XXX: Why do this here? */
+   csp->http->ssl = 1;
+
+   err = sed_https(csp);
+   if (JB_ERR_OK != err)
+   {
+      ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+         (const unsigned char *)CHEADER, strlen(CHEADER));
+      log_error(LOG_LEVEL_ERROR, "Failed to parse client request from %s.",
+         csp->ip_addr_str);
+      log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0",
+         csp->ip_addr_str, csp->http->cmd);
+      return JB_ERR_PARSE;
+   }
+
+   log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
+   log_applied_actions(csp->action);
+
+   return err;
+
+}
+#endif
+
+
 /*********************************************************************
  *
  * Function    :  handle_established_connection
@@ -2026,7 +2405,7 @@ static void handle_established_connection(struct client_state *csp)
    long len = 0; /* for buffer sizes (and negative error codes) */
    int buffer_and_filter_content = 0;
    unsigned int write_delay;
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
    int ret = 0;
    int use_ssl_tunnel = 0;
    csp->dont_verify_certificate = 0;
@@ -2038,7 +2417,7 @@ static void handle_established_connection(struct client_state *csp)
    csp->ssl_with_server_is_opened = 0;
    csp->ssl_with_client_is_opened = 0;
 
-   if (csp->http->ssl && !(csp->action->flags & ACTION_ENABLE_HTTPS_FILTER))
+   if (csp->http->ssl && !(csp->action->flags & ACTION_HTTPS_INSPECTION))
    {
       /* Pass encrypted content without filtering. */
       use_ssl_tunnel = 1;
@@ -2149,7 +2528,7 @@ static void handle_established_connection(struct client_state *csp)
       }
 #endif  /* FEATURE_CONNECTION_KEEP_ALIVE */
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       /*
        * Test if some data from client or destination server are pending
        * on TLS/SSL. We must work with them preferably. TLS/SSL data can
@@ -2208,7 +2587,7 @@ static void handle_established_connection(struct client_state *csp)
                send_crunch_response(csp, error_response(csp, "connection-timeout"));
             }
             mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
             close_client_and_server_ssl_connections(csp);
 #endif
             return;
@@ -2221,13 +2600,13 @@ static void handle_established_connection(struct client_state *csp)
             log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
 #endif
             mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
             close_client_and_server_ssl_connections(csp);
 #endif
             return;
          }
       }
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       else
       {
          /* set FD if some data are pending on TLS/SSL connections */
@@ -2242,7 +2621,6 @@ static void handle_established_connection(struct client_state *csp)
 #else
             FD_SET(csp->cfd, &rfds);
 #endif
-            n++;
          }
 
          if (read_ssl_server)
@@ -2253,7 +2631,6 @@ static void handle_established_connection(struct client_state *csp)
 #else
             FD_SET(csp->server_connection.sfd, &rfds);
 #endif
-            n++;
          }
       }
 #endif
@@ -2327,7 +2704,7 @@ static void handle_established_connection(struct client_state *csp)
          assert(max_bytes_to_read <= csp->receive_buffer_size);
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
          /*
           * Reading data from standard or secured connection (HTTP/HTTPS)
           */
@@ -2359,7 +2736,7 @@ static void handle_established_connection(struct client_state *csp)
             }
          }
          else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
          {
             len = read_socket(csp->cfd, csp->receive_buffer, max_bytes_to_read);
 
@@ -2392,7 +2769,7 @@ static void handle_established_connection(struct client_state *csp)
             {
                log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -2429,7 +2806,7 @@ static void handle_established_connection(struct client_state *csp)
             log_error(LOG_LEVEL_CONNECT,
                "The server still wants to talk, but the client hung up on us.");
             mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
             close_client_and_server_ssl_connections(csp);
 #endif
             return;
@@ -2437,7 +2814,7 @@ static void handle_established_connection(struct client_state *csp)
          }
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
          /*
           * Reading data from standard or secured connection (HTTP/HTTPS)
           */
@@ -2458,7 +2835,7 @@ static void handle_established_connection(struct client_state *csp)
             log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
 
             if ((http->ssl && (csp->fwd == NULL))
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                && use_ssl_tunnel
 #endif
                 )
@@ -2484,7 +2861,7 @@ static void handle_established_connection(struct client_state *csp)
                log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. "
                   "Unable to tell the client about the problem.");
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -2553,7 +2930,7 @@ static void handle_established_connection(struct client_state *csp)
          {
 
             if (server_body || (http->ssl
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                   && use_ssl_tunnel
 #endif
                ))
@@ -2603,7 +2980,7 @@ static void handle_established_connection(struct client_state *csp)
                      log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
                   }
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                   /*
                    * Sending data with standard or secured connection (HTTP/HTTPS)
                    */
@@ -2625,7 +3002,7 @@ static void handle_established_connection(struct client_state *csp)
                      }
                   }
                   else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                   {
                      if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay)
                       || write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur),
@@ -2635,7 +3012,7 @@ static void handle_established_connection(struct client_state *csp)
                         freez(hdr);
                         freez(p);
                         mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                         close_client_and_server_ssl_connections(csp);
 #endif
                         return;
@@ -2670,7 +3047,7 @@ static void handle_established_connection(struct client_state *csp)
           * content-filtering.
           */
          if (server_body || (http->ssl
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                && use_ssl_tunnel
 #endif
             ))
@@ -2701,14 +3078,14 @@ static void handle_established_connection(struct client_state *csp)
                      rsp = cgi_error_memory();
                      send_crunch_response(csp, rsp);
                      mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                      close_client_and_server_ssl_connections(csp);
 #endif
                      return;
                   }
                   hdrlen = strlen(hdr);
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                   /*
                    * Sending data with standard or secured connection (HTTP/HTTPS)
                    */
@@ -2730,7 +3107,7 @@ static void handle_established_connection(struct client_state *csp)
                      }
                   }
                   else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                   {
                      if (write_socket_delayed(csp->cfd, hdr, hdrlen, write_delay)
                       || ((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)
@@ -2741,7 +3118,7 @@ static void handle_established_connection(struct client_state *csp)
                            "Flush header and buffers to client failed: %E");
                         freez(hdr);
                         mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                         close_client_and_server_ssl_connections(csp);
 #endif
                         return;
@@ -2761,7 +3138,7 @@ static void handle_established_connection(struct client_state *csp)
             }
             else
             {
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                /*
                 * Sending data with standard or secured connection (HTTP/HTTPS)
                 */
@@ -2779,14 +3156,14 @@ static void handle_established_connection(struct client_state *csp)
                   }
                }
                else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                {
                   if (write_socket_delayed(csp->cfd, csp->receive_buffer,
                         (size_t)len, write_delay))
                   {
                      log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
                      mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                      close_client_and_server_ssl_connections(csp);
 #endif
                      return;
@@ -2809,7 +3186,7 @@ static void handle_established_connection(struct client_state *csp)
                rsp = cgi_error_memory();
                send_crunch_response(csp, rsp);
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -2829,7 +3206,7 @@ static void handle_established_connection(struct client_state *csp)
                      "Applying the MS IIS5 hack didn't help.");
                   log_error(LOG_LEVEL_CLF,
                      "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                   /*
                    * Sending data with standard or secured connection (HTTP/HTTPS)
                    */
@@ -2840,14 +3217,14 @@ static void handle_established_connection(struct client_state *csp)
                         strlen(INVALID_SERVER_HEADERS_RESPONSE));
                   }
                   else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                   {
                      write_socket_delayed(csp->cfd,
                         INVALID_SERVER_HEADERS_RESPONSE,
                         strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
                   }
                   mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                   close_client_and_server_ssl_connections(csp);
 #endif
                   return;
@@ -2895,7 +3272,7 @@ static void handle_established_connection(struct client_state *csp)
                }
                free_http_request(http);
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -2923,7 +3300,7 @@ static void handle_established_connection(struct client_state *csp)
                   csp->headers->first->str);
                log_error(LOG_LEVEL_CLF,
                   "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                /*
                 * Sending data with standard or secured connection (HTTP/HTTPS)
                 */
@@ -2934,14 +3311,14 @@ static void handle_established_connection(struct client_state *csp)
                      strlen(INVALID_SERVER_HEADERS_RESPONSE));
                }
                else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                {
                   write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
                      strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
                }
                free_http_request(http);
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -2955,7 +3332,7 @@ static void handle_established_connection(struct client_state *csp)
             {
                log_error(LOG_LEVEL_CLF,
                   "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                /*
                 * Sending data with standard or secured connection (HTTP/HTTPS)
                 */
@@ -2973,7 +3350,7 @@ static void handle_established_connection(struct client_state *csp)
                }
                free_http_request(http);
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -3010,7 +3387,7 @@ static void handle_established_connection(struct client_state *csp)
                 */
                freez(hdr);
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -3027,7 +3404,7 @@ static void handle_established_connection(struct client_state *csp)
                 * may be in the buffer). Use standard or secured
                 * connection.
                 */
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                if (client_use_ssl(csp))
                {
                   if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
@@ -3043,14 +3420,14 @@ static void handle_established_connection(struct client_state *csp)
                       */
                      freez(hdr);
                      mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                      close_client_and_server_ssl_connections(csp);
 #endif
                      return;
                   }
                }
                else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                {
                   if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay)
                      || ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 0))
@@ -3063,7 +3440,7 @@ static void handle_established_connection(struct client_state *csp)
                       */
                      freez(hdr);
                      mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                      close_client_and_server_ssl_connections(csp);
 #endif
                      return;
@@ -3088,7 +3465,7 @@ static void handle_established_connection(struct client_state *csp)
                   "Applying the MS IIS5 hack didn't help.");
                log_error(LOG_LEVEL_CLF,
                   "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                /*
                 * Sending data with standard or secured connection (HTTP/HTTPS)
                 */
@@ -3099,13 +3476,13 @@ static void handle_established_connection(struct client_state *csp)
                      strlen(INVALID_SERVER_HEADERS_RESPONSE));
                }
                else
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
                {
                   write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
                      strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
                }
                mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
                close_client_and_server_ssl_connections(csp);
 #endif
                return;
@@ -3114,12 +3491,12 @@ static void handle_established_connection(struct client_state *csp)
          continue;
       }
       mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       close_client_and_server_ssl_connections(csp);
 #endif
       return; /* huh? we should never get here */
    }
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
    close_client_and_server_ssl_connections(csp);
 #endif
    if (csp->content_length == 0)
@@ -3179,7 +3556,7 @@ static void chat(struct client_state *csp)
    struct http_request *http;
    /* Skeleton for HTTP response, if we should intercept the request */
    struct http_response *rsp;
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
    int use_ssl_tunnel = 0;
 #endif
 
@@ -3203,12 +3580,12 @@ static void chat(struct client_state *csp)
       return;
    }
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
    /*
     * Setting flags to use old solution with SSL tunnel and to disable
     * certificates verification.
     */
-   if (csp->http->ssl && !(csp->action->flags & ACTION_ENABLE_HTTPS_FILTER))
+   if (csp->http->ssl && !(csp->action->flags & ACTION_HTTPS_INSPECTION))
    {
       use_ssl_tunnel = 1;
    }
@@ -3260,7 +3637,7 @@ static void chat(struct client_state *csp)
     *
     */
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
    /*
     * Presetting SSL client and server flags
     */
@@ -3286,7 +3663,7 @@ static void chat(struct client_state *csp)
          csp->ip_addr_str, acceptable_connect_ports, csp->http->hostport);
       csp->action->flags |= ACTION_BLOCK;
       http->ssl = 0;
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       http->client_ssl = 0;
       http->server_ssl = 0;
 #endif
@@ -3297,9 +3674,16 @@ static void chat(struct client_state *csp)
    build_request_line(csp, fwd, &csp->headers->first->str);
 
    /*
-    * We have a request. Check if one of the crunchers wants it.
+    * We have a request. Check if one of the crunchers wants it
+    * unless the client wants to use TLS/SSL in which case we
+    * haven't setup the TLS context yet and will send the crunch
+    * response later.
     */
-   if (crunch_response_triggered(csp, crunchers_all))
+   if (
+#ifdef FEATURE_HTTPS_INSPECTION
+       !client_use_ssl(csp) &&
+#endif
+       crunch_response_triggered(csp, crunchers_all))
    {
       /*
        * Yes. The client got the crunch response and we're done here.
@@ -3353,7 +3737,60 @@ static void chat(struct client_state *csp)
          mark_connection_closed(&csp->server_connection);
       }
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+#ifdef FEATURE_HTTPS_INSPECTION
+      if (http->ssl && !use_ssl_tunnel)
+      {
+         int ret;
+         /*
+          * Creating an SSL proxy. If forwarding is disabled, we must send
+          * CSUCCEED mesage to client. Then TLS/SSL connection with client
+          * is created.
+          */
 
+         if (fwd->forward_host == NULL)
+         {
+            /*
+             * We're lying to the client as the connection hasn't actually
+             * been established yet. We don't establish the connection until
+             * we have seen and parsed the encrypted client headers.
+             */
+            if (write_socket_delayed(csp->cfd, CSUCCEED,
+                  strlen(CSUCCEED), get_write_delay(csp)) != 0)
+            {
+               log_error(LOG_LEVEL_ERROR, "Sending SUCCEED to client failed");
+               return;
+            }
+         }
+
+         ret = create_client_ssl_connection(csp);
+         if (ret != 0)
+         {
+            log_error(LOG_LEVEL_ERROR,
+               "Can't open secure connection with client");
+            close_client_ssl_connection(csp); /* XXX: Is this needed? */
+            return;
+         }
+         if (JB_ERR_OK != process_encrypted_request(csp))
+         {
+            log_error(LOG_LEVEL_ERROR, "Failed to parse encrypted request.");
+            close_client_ssl_connection(csp);
+            return;
+         }
+         /*
+          * We have an encrypted request. Check if one of the crunchers now
+          * wants it (for example because the previously invisible path was
+          * required to match).
+          */
+         if (crunch_response_triggered(csp, crunchers_all))
+         {
+            /*
+             * Yes. The client got the crunch response and we're done here.
+             */
+            close_client_ssl_connection(csp);
+            return;
+         }
+      }
+#endif
       /*
        * Connecting to destination server
        */
@@ -3399,7 +3836,7 @@ static void chat(struct client_state *csp)
          return;
       }
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       /*
        * Creating TLS/SSL connections with destination server or parent
        * proxy. If forwarding is enabled, we must send client request to
@@ -3434,6 +3871,7 @@ static void chat(struct client_state *csp)
                log_error(LOG_LEVEL_CONNECT,
                   "Sending request headers to: %s failed", http->hostport);
                mark_server_socket_tainted(csp);
+               close_client_ssl_connection(csp);
                return;
             }
 
@@ -3452,6 +3890,7 @@ static void chat(struct client_state *csp)
                   send_crunch_response(csp, rsp);
                }
                mark_server_socket_tainted(csp);
+               close_client_ssl_connection(csp);
                return;
             }
 
@@ -3467,6 +3906,7 @@ static void chat(struct client_state *csp)
 
                write_socket(csp->cfd, server_response, (size_t)len);
                mark_server_socket_tainted(csp);
+               close_client_ssl_connection(csp);
                return;
             }
 
@@ -3478,7 +3918,7 @@ static void chat(struct client_state *csp)
 
             /*
             * If TLS/SSL connection wasn't created and invalid certificate
-            * wasn't detected, we can interrupt this fuction. Otherwise, we
+            * wasn't detected, we can interrupt this function. Otherwise, we
             * must inform the client about invalid server certificate.
             */
             if (ret != 0
@@ -3503,6 +3943,7 @@ static void chat(struct client_state *csp)
                log_error(LOG_LEVEL_ERROR,
                   "Sending parent proxy response to client failed");
                mark_server_socket_tainted(csp);
+               close_client_ssl_connection(csp);
                return;
             }
          }/* -END- if (fwd->forward_host != NULL) */
@@ -3513,25 +3954,38 @@ static void chat(struct client_state *csp)
              * with destination server
              */
             int ret = create_server_ssl_connection(csp);
-            /*
-             * If TLS/SSL connection wasn't created and invalid certificate
-             * wasn't detected, we can interrupt this function. Otherwise, we
-             * must inform client about invalid server certificate.
-             */
-            if (ret != 0
-               && (csp->server_cert_verification_result == SSL_CERT_NOT_VERIFIED
-                  || csp->server_cert_verification_result == SSL_CERT_VALID))
+            if (ret != 0)
             {
-               rsp = error_response(csp, "connect-failed");
-               if (rsp)
+               if (csp->server_cert_verification_result != SSL_CERT_VALID &&
+                   csp->server_cert_verification_result != SSL_CERT_NOT_VERIFIED)
                {
-                  send_crunch_response(csp, rsp);
+                  /*
+                   * If the server certificate is invalid, we must inform
+                   * the client and then close connection to the client.
+                   */
+                  ssl_send_certificate_error(csp);
+                  close_client_and_server_ssl_connections(csp);
+                  return;
+               }
+               if (csp->server_cert_verification_result == SSL_CERT_NOT_VERIFIED
+                || csp->server_cert_verification_result == SSL_CERT_VALID)
+               {
+                  /*
+                   * The TLS/SSL connection wasn't created but an invalid
+                   * certificate wasn't detected. Report it as connection
+                   * failure.
+                   */
+                  rsp = error_response(csp, "connect-failed");
+                  if (rsp)
+                  {
+                     send_crunch_response(csp, rsp);
+                  }
+                  return;
                }
-               return;
             }
          }
       }/* -END- if (http->ssl) */
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
 
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
       save_connection_destination(csp->server_connection.sfd,
@@ -3549,7 +4003,7 @@ static void chat(struct client_state *csp)
       assert(csp->headers->last == NULL);
    }
    else if (http->ssl == 0 || (fwd->forward_host
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
          && use_ssl_tunnel
 #endif
            ))
@@ -3570,7 +4024,7 @@ static void chat(struct client_state *csp)
        * Using old solution with SSL tunnel or new solution with SSL proxy
        */
       list_remove_all(csp->headers);
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       if (use_ssl_tunnel)
 #endif
       {
@@ -3585,36 +4039,9 @@ static void chat(struct client_state *csp)
             return;
          }
       }
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
       else
       {
-         int ret;
-         /*
-          * Creating an SSL proxy. If forwarding is disabled, we must send
-          * CSUCCEED mesage to client. Then TLS/SSL connection with client
-          * is created.
-          */
-
-         if (fwd->forward_host == NULL)
-         {
-            if (write_socket_delayed(csp->cfd, CSUCCEED,
-                  strlen(CSUCCEED), get_write_delay(csp)) != 0)
-            {
-               log_error(LOG_LEVEL_ERROR, "Sending SUCCEED to client failed");
-               close_client_and_server_ssl_connections(csp);
-               return;
-            }
-         }
-
-         ret = create_client_ssl_connection(csp);
-         if (ret != 0)
-         {
-            log_error(LOG_LEVEL_ERROR,
-               "Can't open secure connection with client");
-            close_client_and_server_ssl_connections(csp);
-            return;
-         }
-
          /*
           * If server certificate is invalid, we must inform client and then
           * close connection with client.
@@ -3625,8 +4052,18 @@ static void chat(struct client_state *csp)
             close_client_and_server_ssl_connections(csp);
             return;
          }
+         if (send_https_request(csp))
+         {
+            rsp = error_response(csp, "connect-failed");
+            if (rsp)
+            {
+               send_crunch_response(csp, rsp);
+            }
+            close_client_and_server_ssl_connections(csp);
+            return;
+         }
       }
-#endif /* def FEATURE_HTTPS_FILTERING */
+#endif /* def FEATURE_HTTPS_INSPECTION */
       clear_iob(csp->client_iob);
    }/* -END- else ... if (http->ssl == 1) */
 
@@ -4165,17 +4602,10 @@ static void initialize_mutexes(void)
     * Prepare global mutex semaphores
     */
 
-#ifdef LIMIT_MUTEX_NUMBER
-   int i = 0;
-   for (i = 0; i < 32; i++)
-#else
-   int i = 0;
-   for (i = 0; i < 65536; i++)
-#endif /* LIMIT_MUTEX_NUMBER */
-   {
-      privoxy_mutex_init(&(certificates_mutexes[i]));
-   }
+#ifdef FEATURE_HTTPS_INSPECTION
+   privoxy_mutex_init(&certificate_mutex);
    privoxy_mutex_init(&rng_mutex);
+#endif
 
    privoxy_mutex_init(&log_mutex);
    privoxy_mutex_init(&log_init_mutex);
@@ -5212,7 +5642,7 @@ static void listen_loop(void)
 
    /* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */
 
-#ifdef FEATURE_HTTPS_FILTERING
+#ifdef FEATURE_HTTPS_INSPECTION
    /* Clean up.  Aim: free all memory (no leaks) */
    if (rng_seeded == 1)
    {