+ if (!server_use_ssl(csp))
+ {
+ log_error(LOG_LEVEL_REDIRECTS,
+ "Rewritten request line results in downgrade to http");
+ /*
+ * Replace the unencryptd headers received with the
+ * CONNECT request with the ones we received securely.
+ */
+ destroy_list(csp->headers);
+ csp->headers->first = csp->https_headers->first;
+ csp->headers->last = csp->https_headers->last;
+ csp->https_headers->first = NULL;
+ csp->https_headers->last = NULL;
+ }
+
+ 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;
+
+ assert(csp->ssl_with_client_is_opened);
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
+ {
+ csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ }
+#endif
+ err = receive_encrypted_request(csp);
+ if (err != JB_ERR_OK)
+ {
+ if (csp->client_iob->cur == NULL ||
+ csp->client_iob->cur == csp->client_iob->eod)
+ {
+ /*
+ * We did not receive any data, most likely because the
+ * client is done. Don't log this as a parse failure.
+ */
+ return JB_ERR_PARSE;
+ }
+ /* XXX: Also used for JB_ERR_MEMORY */
+ log_error(LOG_LEVEL_ERROR, "Failed to receive encrypted request: %s",
+ jb_err_to_string(err));
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ 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)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to get the encrypted request line");
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ return JB_ERR_PARSE;
+ }
+ assert(*request_line != '\0');
+
+ if (client_protocol_is_unsupported(csp, request_line))
+ {
+ /*
+ * If the protocol is unsupported we're done here.
+ * client_protocol_is_unsupported() took care of sending
+ * the error response and logging the error message.
+ */
+ 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_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ /* 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.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to get the encrypted request destination");
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ return JB_ERR_PARSE;
+ }
+
+ /* Split the domain we just got for pattern matching */
+ init_domain_components(csp->http);
+
+#ifdef FEATURE_CLIENT_TAGS
+ /* XXX: If the headers were enlisted sooner, passing csp would do. */
+ if (csp->client_address == NULL)
+ {
+ set_client_address(csp, headers);
+ get_tag_list_for_client(csp->client_tags, csp->client_address);
+ }
+#endif
+
+#ifdef FEATURE_TOGGLE
+ if ((csp->flags & CSP_FLAG_TOGGLED_ON) != 0)
+#endif
+ {
+ /*
+ * Determine the actions for this request after
+ * clearing the ones from the previous one.
+ */
+ free_current_action(csp->action);
+ 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_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ 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;
+ }
+
+ if ((NULL == csp->https_headers->first->str)
+ || (strcmp(csp->http->cmd, csp->https_headers->first->str) &&
+ (JB_ERR_OK != change_encrypted_request_destination(csp))))
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)MESSED_UP_REQUEST_RESPONSE,
+ strlen(MESSED_UP_REQUEST_RESPONSE), get_write_delay(csp));
+ log_error(LOG_LEVEL_ERROR,
+ "Invalid request line after applying header filters.");
+ /* XXX: Use correct size */
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"Invalid request generated\" 400 0", csp->ip_addr_str);
+
+ return JB_ERR_PARSE;
+ }
+
+ log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
+ log_applied_actions(csp->action);
+ log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport,
+ csp->http->path);
+
+ return err;
+
+}
+
+/*********************************************************************
+ *
+ * Function : cgi_page_requested
+ *
+ * Description : Checks if a request is for an internal CGI page.
+ *
+ * Parameters :
+ * 1 : host = The host requested by the client.
+ *
+ * Returns : 1 if a CGI page has been requested, 0 otherwise
+ *
+ *********************************************************************/
+static int cgi_page_requested(const char *host)
+{
+ if ((0 == strcmpic(host, CGI_SITE_1_HOST))
+ || (0 == strcmpic(host, CGI_SITE_1_HOST "."))
+ || (0 == strcmpic(host, CGI_SITE_2_HOST))
+ || (0 == strcmpic(host, CGI_SITE_2_HOST ".")))
+ {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function : continue_https_chat
+ *
+ * Description : Behaves similar to chat() but only deals with
+ * https-inspected requests that arrive on an already
+ * established connection. The first request is always
+ * served by chat() which is a lot more complex as it
+ * has to deal with forwarding settings and connection
+ * failures etc.
+ *
+ * If a connection to the server has already been
+ * opened it is reused unless the request is blocked
+ * or the forwarder changed.
+ *
+ * If a connection to the server has not yet been
+ * opened (because the previous request was crunched),
+ * or the forwarder changed, the connection is dropped
+ * so that the client retries on a fresh one.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Nothing.
+ *
+ *********************************************************************/
+static void continue_https_chat(struct client_state *csp)
+{
+ const struct forward_spec *fwd;
+
+ if (JB_ERR_OK != process_encrypted_request(csp))
+ {
+ return;
+ }
+
+ csp->requests_received_total++;
+
+ /*
+ * We have an encrypted request. Check if one of the crunchers wants it.
+ */
+ if (crunch_response_triggered(csp, crunchers_all))
+ {
+ /*
+ * Yes. The client got the crunch response and we're done here.
+ */
+ return;
+ }
+ if (csp->ssl_with_server_is_opened == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Dropping the client connection on socket %d. "
+ "The server connection has not been established yet.",
+ csp->cfd);
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ return;
+ }
+ assert(csp->server_connection.sfd != JB_INVALID_SOCKET);
+
+ fwd = forward_url(csp, csp->http);
+ if (!connection_destination_matches(&csp->server_connection, csp->http, fwd))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Dropping the client connection on socket %d with "
+ "server socket %d connected to %s. The forwarder has changed.",
+ csp->cfd, csp->server_connection.sfd, csp->server_connection.host);
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ return;
+ }
+
+ log_error(LOG_LEVEL_CONNECT,
+ "Reusing server socket %d connected to %s. Requests already sent: %u.",
+ csp->server_connection.sfd, csp->server_connection.host,
+ csp->server_connection.requests_sent_total);
+
+ if (send_https_request(csp))
+ {
+ /*
+ * Most likely the server connection timed out. We can't easily
+ * create a new one so simply drop the client connection without a
+ * error response to let the client retry.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "Dropping client connection on socket %d. "
+ "Forwarding the encrypted client request failed.",
+ csp->cfd);
+ return;
+ }
+ csp->server_connection.requests_sent_total++;
+ handle_established_connection(csp);
+ freez(csp->receive_buffer);
+}
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+#endif
+
+
+/*********************************************************************
+ *
+ * Function : handle_established_connection
+ *
+ * Description : Shuffle data between client and server once the
+ * connection has been established.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Nothing.
+ *
+ *********************************************************************/
+static void handle_established_connection(struct client_state *csp)
+{
+ char *hdr;
+ char *p;
+ int n;
+#ifdef HAVE_POLL
+ struct pollfd poll_fds[2];
+#else
+ fd_set rfds;
+ jb_socket maxfd;
+ struct timeval timeout;
+#endif
+ int server_body;
+ int ms_iis5_hack = 0;
+ unsigned long long byte_count = 0;
+ struct http_request *http;
+ long len = 0; /* for buffer sizes (and negative error codes) */
+ int buffer_and_filter_content = 0;
+ unsigned int write_delay;
+#ifdef FEATURE_HTTPS_INSPECTION
+ int ret = 0;
+ int use_ssl_tunnel = 0;
+ csp->dont_verify_certificate = 0;
+
+ if (csp->http->ssl && !(csp->action->flags & ACTION_HTTPS_INSPECTION))
+ {
+ /* Pass encrypted content without filtering. */
+ use_ssl_tunnel = 1;
+ }
+#endif
+
+ /* Skeleton for HTTP response, if we should intercept the request */
+ struct http_response *rsp;
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ int watch_client_socket;
+#endif
+
+ csp->receive_buffer_size = csp->config->receive_buffer_size;
+ csp->receive_buffer = zalloc(csp->receive_buffer_size + 1);
+ if (csp->receive_buffer == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory. Failed to allocate the receive buffer.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+ return;
+ }
+
+ http = csp->http;
+
+#ifndef HAVE_POLL
+ maxfd = (csp->cfd > csp->server_connection.sfd) ?
+ csp->cfd : csp->server_connection.sfd;
+#endif
+
+ /* pass data between the client and server
+ * until one or the other shuts down the connection.
+ */
+
+ server_body = 0;
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ watch_client_socket = 0 == (csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING);
+#endif
+ write_delay = get_write_delay(csp);
+
+ for (;;)
+ {
+#ifndef HAVE_POLL
+ FD_ZERO(&rfds);
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (!watch_client_socket)
+ {
+ maxfd = csp->server_connection.sfd;
+ }
+ else
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+ {
+ FD_SET(csp->cfd, &rfds);
+ }
+
+ FD_SET(csp->server_connection.sfd, &rfds);
+#endif /* ndef HAVE_POLL */
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if ((csp->flags & CSP_FLAG_CHUNKED)
+ && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET)
+ && ((csp->iob->eod - csp->iob->cur) >= 5)
+ && !memcmp(csp->iob->eod-5, "0\r\n\r\n", 5))
+ {
+ /*
+ * XXX: This check should be obsolete now,
+ * but let's wait a while to be sure.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "Looks like we got the last chunk together with "
+ "the server headers but didn't detect it earlier. "
+ "We better stop reading.");
+ 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))
+ {
+ if (csp->expected_content_length == byte_count)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Done reading from server. Content length: %llu as expected. "
+ "Bytes most recently read: %ld.",
+ byte_count, len);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Done reading from server. Expected content length: %llu. "
+ "Actual content length: %llu. Bytes most recently read: %ld.",
+ csp->expected_content_length, byte_count, len);
+ }
+ len = 0;
+ /*
+ * XXX: Should not jump around, handle_established_connection()
+ * is complicated enough already.
+ */
+ goto reading_done;
+ }
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
+#ifdef HAVE_POLL
+ poll_fds[0].fd = csp->cfd;
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (!watch_client_socket)
+ {
+ /*
+ * Ignore incoming data, but still watch out
+ * for disconnects etc. These flags are always
+ * implied anyway but explicitly setting them
+ * doesn't hurt.
+ */
+ poll_fds[0].events = POLLERR|POLLHUP;
+ }
+ else
+#endif
+ {
+ poll_fds[0].events = POLLIN;
+ }
+ poll_fds[1].fd = csp->server_connection.sfd;
+ poll_fds[1].events = POLLIN;
+ n = poll(poll_fds, 2, csp->config->socket_timeout * 1000);
+#else
+ timeout.tv_sec = csp->config->socket_timeout;
+ timeout.tv_usec = 0;
+ n = select((int)maxfd + 1, &rfds, NULL, NULL, &timeout);
+#endif /* def HAVE_POLL */
+
+ /*server or client not responding in timeout */
+ if (n == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Socket timeout %d reached: %s",
+ csp->config->socket_timeout, http->url);
+ if ((byte_count == 0) && (http->ssl == 0))
+ {
+ send_crunch_response(csp, error_response(csp, "connection-timeout"));
+ }
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ else if (n < 0)
+ {
+#ifdef HAVE_POLL
+ log_error(LOG_LEVEL_ERROR, "poll() failed!: %E");
+#else
+ log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
+#endif
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /*
+ * This is the body of the browser's request,
+ * just read and write it.
+ *
+ * Receives data from browser and sends it to server
+ *
+ * XXX: Make sure the client doesn't use pipelining
+ * behind Privoxy's back.
+ */
+#ifdef HAVE_POLL
+ if ((poll_fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "The client socket %d has become unusable while "
+ "the server socket %d is still open.",
+ csp->cfd, csp->server_connection.sfd);
+ mark_server_socket_tainted(csp);
+ break;
+ }
+
+ if (poll_fds[0].revents != 0)
+#else
+ if (FD_ISSET(csp->cfd, &rfds))
+#endif /* def HAVE_POLL*/
+ {
+ int max_bytes_to_read = (int)csp->receive_buffer_size;
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ))
+ {
+ if (data_is_available(csp->cfd, 0))
+ {
+ /*
+ * If the next request is already waiting, we have
+ * to stop select()ing the client socket. Otherwise
+ * we would always return right away and get nothing
+ * else done.
+ */
+ watch_client_socket = 0;
+ log_error(LOG_LEVEL_CONNECT,
+ "Stop watching client socket %d. "
+ "There's already another request waiting.",
+ csp->cfd);
+ continue;
+ }
+ /*
+ * If the client socket is set, but there's no data
+ * available on the socket, the client went fishing
+ * and continuing talking to the server makes no sense.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "The client closed socket %d while "
+ "the server socket %d is still open.",
+ csp->cfd, csp->server_connection.sfd);
+ mark_server_socket_tainted(csp);
+ break;
+ }
+ if (csp->expected_client_content_length != 0)
+ {
+ if (csp->expected_client_content_length < csp->receive_buffer_size)
+ {
+ max_bytes_to_read = (int)csp->expected_client_content_length;
+ }
+ log_error(LOG_LEVEL_CONNECT,
+ "Waiting for up to %d bytes from the client.",
+ max_bytes_to_read);
+ }
+ assert(max_bytes_to_read <= csp->receive_buffer_size);
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp))
+ {
+ if (csp->http->status == 101)
+ {
+ len = ssl_recv_data(&(csp->ssl_client_attr),
+ (unsigned char *)csp->receive_buffer,
+ (size_t)max_bytes_to_read);
+ if (len == -1)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to receive data "
+ "on client socket %d for an upgraded connection",
+ csp->cfd);
+ break;
+ }
+ if (len == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Done receiving data "
+ "on client socket %d for an upgraded connection",
+ csp->cfd);
+ break;
+ }
+ byte_count += (unsigned long long)len;
+ len = ssl_send_data(&(csp->ssl_server_attr),
+ (unsigned char *)csp->receive_buffer, (size_t)len);
+ if (len == -1)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to send data "
+ "on server socket %d for an upgraded connection",
+ csp->server_connection.sfd);
+ break;
+ }
+ continue;
+ }
+ log_error(LOG_LEVEL_CONNECT, "Breaking with TLS/SSL.");
+ break;
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ len = read_socket(csp->cfd, csp->receive_buffer, max_bytes_to_read);
+
+ if (len <= 0)
+ {
+ /* XXX: not sure if this is necessary. */
+ mark_server_socket_tainted(csp);
+ break; /* "game over, man" */
+ }
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->expected_client_content_length != 0)
+ {
+ assert(len <= max_bytes_to_read);
+ csp->expected_client_content_length -= (unsigned)len;
+ log_error(LOG_LEVEL_CONNECT,
+ "Expected client content length set to %llu "
+ "after reading %ld bytes.",
+ csp->expected_client_content_length, len);
+ if (csp->expected_client_content_length == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Done reading from the client.");
+ csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ;
+ }
+ }
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+ if (write_socket(csp->server_connection.sfd, csp->receive_buffer, (size_t)len))
+ {
+ log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
+ mark_server_socket_tainted(csp);
+ return;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * The server wants to talk. It could be the header or the body.
+ * If `hdr' is null, then it's the header otherwise it's the body.
+ * FIXME: Does `hdr' really mean `host'? No.
+ */
+#ifdef HAVE_POLL
+ if (poll_fds[1].revents != 0)
+#else
+ if (FD_ISSET(csp->server_connection.sfd, &rfds))
+#endif /* HAVE_POLL */
+ {
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ /*
+ * If we are buffering content, we don't want to eat up to
+ * buffer-limit bytes if the client no longer cares about them.
+ * If we aren't buffering, however, a dead client socket will be
+ * noticed pretty much right away anyway, so we can reduce the
+ * overhead by skipping the check.
+ */
+ if (buffer_and_filter_content && !socket_is_still_alive(csp->cfd))
+ {
+#ifdef _WIN32
+ log_error(LOG_LEVEL_CONNECT,
+ "The server still wants to talk, but the client may already have hung up on us.");
+#else
+ 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_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+#endif /* def _WIN32 */
+ }
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Reading data from standard or secured connection (HTTP/HTTPS)
+ */
+ if (server_use_ssl(csp))
+ {
+ len = ssl_recv_data(&(csp->ssl_server_attr),
+ (unsigned char *)csp->receive_buffer, csp->receive_buffer_size);
+ }
+ else
+#endif
+ {
+ len = read_socket(csp->server_connection.sfd, csp->receive_buffer,
+ (int)csp->receive_buffer_size);
+ }
+
+ if (len < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
+
+ if ((http->ssl && (csp->fwd == NULL))
+#ifdef FEATURE_HTTPS_INSPECTION
+ && use_ssl_tunnel
+#endif
+ )
+ {
+ /*
+ * Just hang up. We already confirmed the client's CONNECT
+ * request with status code 200 and unencrypted content is
+ * no longer welcome.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "CONNECT already confirmed. Unable to tell the client about the problem.");
+ return;
+ }
+ else if (byte_count)
+ {
+ /*
+ * Just hang up. We already transmitted the original headers
+ * and parts of the original content and therefore missed the
+ * chance to send an error message (without risking data corruption).
+ *
+ * XXX: we could retry with a fancy range request here.
+ */
+ 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_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ /*
+ * XXX: Consider handling the cases above the same.
+ */
+ mark_server_socket_tainted(csp);
+ len = 0;
+ }
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->flags & CSP_FLAG_CHUNKED)
+ {
+ if ((len >= 5) && !memcmp(csp->receive_buffer+len-5, "0\r\n\r\n", 5))
+ {
+ /* XXX: this is a temporary hack */
+ log_error(LOG_LEVEL_CONNECT,