* Purpose : Main file. Contains main() method, main loop, and
* the main connection-handling function.
*
- * Copyright : Written by and Copyright (C) 2001-2021 the
+ * Copyright : Written by and Copyright (C) 2001-2022 the
* Privoxy team. https://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
#ifdef FEATURE_STATISTICS
int urls_read = 0; /* total nr of urls read inc rejected */
int urls_rejected = 0; /* total nr of urls rejected */
+#ifdef MUTEX_LOCKS_AVAILABLE
+unsigned long long number_of_requests_received = 0;
+unsigned long long number_of_requests_blocked = 0;
+#endif
#endif /* def FEATURE_STATISTICS */
#ifdef FEATURE_GRACEFUL_TERMINATION
#ifdef FEATURE_CLIENT_TAGS
privoxy_mutex_t client_tags_mutex;
#endif
+#ifdef FEATURE_STATISTICS
+privoxy_mutex_t block_statistics_mutex;
+#endif
#ifdef FEATURE_EXTENDED_STATISTICS
privoxy_mutex_t filter_statistics_mutex;
-privoxy_mutex_t block_statistics_mutex;
+privoxy_mutex_t block_reason_statistics_mutex;
#endif
#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
"HTTP/1.1 400 Invalid header received from client\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Invalid header received from client.\r\n";
+ "Invalid header received from client.\n";
static const char FTP_RESPONSE[] =
"HTTP/1.1 400 Invalid request received from client\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Invalid request. Privoxy doesn't support FTP.\r\n";
+ "Invalid request. Privoxy doesn't support FTP.\n";
static const char GOPHER_RESPONSE[] =
"HTTP/1.1 400 Invalid request received from client\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Invalid request. Privoxy doesn't support gopher.\r\n";
+ "Invalid request. Privoxy doesn't support gopher.\n";
/* XXX: should be a template */
static const char MISSING_DESTINATION_RESPONSE[] =
"HTTP/1.1 400 Bad request received from client\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Bad request. Privoxy was unable to extract the destination.\r\n";
+ "Bad request. Privoxy was unable to extract the destination.\n";
/* XXX: should be a template */
static const char INVALID_SERVER_HEADERS_RESPONSE[] =
"HTTP/1.1 502 Server or forwarder response invalid\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Bad response. The server or forwarder response doesn't look like HTTP.\r\n";
+ "Bad response. The server or forwarder response doesn't look like HTTP.\n";
/* XXX: should be a template */
static const char MESSED_UP_REQUEST_RESPONSE[] =
"HTTP/1.1 400 Malformed request after rewriting\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Bad request. Messed up with header filters.\r\n";
+ "Bad request. Messed up with header filters.\n";
static const char TOO_MANY_CONNECTIONS_RESPONSE[] =
"HTTP/1.1 503 Too many open connections\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Maximum number of open connections reached.\r\n";
+ "Maximum number of open connections reached.\n";
static const char CLIENT_CONNECTION_TIMEOUT_RESPONSE[] =
"HTTP/1.1 504 Connection timeout\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "The connection timed out because the client request didn't arrive in time.\r\n";
+ "The connection timed out because the client request didn't arrive in time.\n";
static const char CLIENT_BODY_PARSE_ERROR_RESPONSE[] =
"HTTP/1.1 400 Failed reading client body\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Failed parsing or buffering the chunk-encoded client body.\r\n";
+ "Failed parsing or buffering the chunk-encoded client body.\n";
+
+static const char CLIENT_BODY_BUFFER_ERROR_RESPONSE[] =
+ "HTTP/1.1 400 Failed reading client body\r\n"
+ "Content-Type: text/plain\r\n"
+ "Connection: close\r\n\r\n"
+ "Failed to buffer the client body to apply content filters.\n"
+ "Could be caused by a socket timeout\n";
static const char UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE[] =
"HTTP/1.1 417 Expecting too much\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Privoxy detected an unsupported Expect header value.\r\n";
+ "Privoxy detected an unsupported Expect header value.\n";
/* A function to crunch a response */
typedef struct http_response *(*crunch_func_ptr)(struct client_state *);
* We shouldn't be here, unless we catch signals
* in main() that we can't handle here!
*/
- log_error(LOG_LEVEL_FATAL, "sig_handler: exiting on unexpected signal %d", the_signal);
+ log_error(LOG_LEVEL_FATAL,
+ "sig_handler: exiting on unexpected signal %d", the_signal);
}
return;
#ifdef FEATURE_STATISTICS
if (c->flags & CF_COUNT_AS_REJECT)
{
+#ifdef MUTEX_LOCKS_AVAILABLE
+ privoxy_mutex_lock(&block_statistics_mutex);
+ number_of_requests_blocked++;
+ privoxy_mutex_unlock(&block_statistics_mutex);
+#endif
csp->flags |= CSP_FLAG_REJECTED;
}
#endif /* def FEATURE_STATISTICS */
* Rebuild the request line.
*/
freez(*request_line);
- *request_line = strdup(http->gpc);
- string_append(request_line, " ");
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (fwd != NULL && fwd->forward_host &&
+ fwd->type != FORWARD_WEBSERVER && client_use_ssl(csp))
+ {
+ *request_line = strdup("CONNECT ");
+ }
+ else
+#endif
+ {
+ *request_line = strdup(http->gpc);
+ string_append(request_line, " ");
+ }
if (fwd != NULL && fwd->forward_host && fwd->type != FORWARD_WEBSERVER)
{
- string_append(request_line, http->url);
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp))
+ {
+ char port_string[10];
+
+ string_append(request_line, http->host);
+ snprintf(port_string, sizeof(port_string), ":%d", http->port);
+ string_append(request_line, port_string);
+ }
+ else
+#endif
+ {
+ string_append(request_line, http->url);
+ }
}
else
{
* Returns : void
*
*********************************************************************/
-void save_connection_destination(jb_socket sfd,
- const struct http_request *http,
- const struct forward_spec *fwd,
- struct reusable_connection *server_connection)
+static void save_connection_destination(jb_socket sfd,
+ const struct http_request *http,
+ const struct forward_spec *fwd,
+ struct reusable_connection *server_connection)
{
assert(sfd != JB_INVALID_SOCKET);
assert(NULL != http->host);
/* Move beyond the chunkdata. */
p += 2 + chunksize;
+ /* Make sure we're still within the buffer and have two bytes left */
+ if (p + 2 > iob->eod)
+ {
+ return CHUNK_STATUS_MISSING_DATA;
+ }
+
/* There should be another "\r\n" to skip */
if (memcmp(p, "\r\n", 2))
{
len = read_socket(csp->cfd, buf, sizeof(buf) - 1);
if (len <= 0)
{
- log_error(LOG_LEVEL_ERROR, "Read the client body failed: %E");
+ log_error(LOG_LEVEL_ERROR,
+ "Reading the client body failed: %E");
break;
}
if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len))
{
log_error(LOG_LEVEL_INFO, "Chunked body is incomplete or invalid");
}
+ if (get_bytes_missing_from_chunked_data(csp->iob->cur, size, 0) == 0)
+ {
+ if (CHUNK_STATUS_BODY_COMPLETE != status)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "There's disagreement about whether or not the chunked body is complete.");
+ }
+ }
return (JB_ERR_OK == remove_chunked_transfer_coding(csp->iob->cur, &size));
free_http_request(http);
return JB_ERR_PARSE;
}
+ if (http->ssl && strcmpic(http->gpc, "CONNECT"))
+ {
+ write_socket_delayed(csp->cfd, 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, "Client %s tried to send a https "
+ "URL without sending a CONNECT request first",
+ csp->ip_addr_str);
+ free_http_request(http);
+ return JB_ERR_PARSE;
+ }
/* grab the rest of the client's headers */
init_list(headers);
if (!data_is_available(csp->cfd, csp->config->socket_timeout))
{
log_error(LOG_LEVEL_ERROR,
- "Stopped grabbing the client headers.");
+ "Client headers did not arrive in time. Timeout: %d",
+ csp->config->socket_timeout);
destroy_list(headers);
return JB_ERR_PARSE;
}
* elsewhere failed or Privoxy is configured
* to only accept proxy requests.
*
- * An error response has already been send
+ * An error response has already been sent
* and we're done here.
*/
return JB_ERR_PARSE;
size_t max_bytes_to_read = to_read < sizeof(buf) ? to_read : sizeof(buf);
log_error(LOG_LEVEL_CONNECT,
- "Waiting for up to %d bytes of request body from the client.",
+ "Waiting for up to %lu bytes of request body from the client.",
max_bytes_to_read);
len = read_socket(csp->cfd, buf, (int)max_bytes_to_read);
if (len <= -1)
if (to_read != 0)
{
- log_error(LOG_LEVEL_CONNECT, "Not enough request body has been read: expected %d more bytes",
- csp->expected_client_content_length);
+ log_error(LOG_LEVEL_CONNECT,
+ "Not enough request body has been read: expected %lu more bytes.",
+ to_read);
return 1;
}
- log_error(LOG_LEVEL_CONNECT, "The last %d bytes of the request body have been read",
- csp->expected_client_content_length);
+ log_error(LOG_LEVEL_CONNECT,
+ "The last %d bytes of the request body have been read.", len);
return 0;
}
/*********************************************************************
*
- * Function : can_filter_request_body
+ * Function : can_buffer_request_body
*
* Description : Checks if the current request body can be stored in
* the client_iob without hitting buffer limit.
* FALSE otherwise.
*
*********************************************************************/
-static int can_filter_request_body(const struct client_state *csp)
+static int can_buffer_request_body(const struct client_state *csp)
{
if (!can_add_to_iob(csp->client_iob, csp->config->buffer_limit,
csp->expected_client_content_length))
{
log_error(LOG_LEVEL_INFO,
- "Not filtering request body from %s: buffer limit %d will be exceeded "
- "(content length %d)", csp->ip_addr_str, csp->config->buffer_limit,
+ "Not filtering request body from %s: buffer limit %lu will be exceeded "
+ "(content length %llu)", csp->ip_addr_str, csp->config->buffer_limit,
csp->expected_client_content_length);
return FALSE;
}
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*
- * Returns : 0 on success, anything else is an error.
+ * Returns : 0 on success, 1 on error, 2 if the request got crunched.
*
*********************************************************************/
static int send_http_request(struct client_state *csp)
{
char *hdr;
int write_failure;
- const char *to_send;
- size_t to_send_len;
- int filter_client_body = csp->expected_client_content_length != 0 &&
- client_body_filters_enabled(csp->action) && can_filter_request_body(csp);
-
- if (filter_client_body)
- {
- if (read_http_request_body(csp))
- {
- return 1;
- }
- to_send_len = csp->expected_client_content_length;
- to_send = execute_client_body_filters(csp, &to_send_len);
- if (to_send == NULL)
- {
- /* just flush client_iob */
- filter_client_body = FALSE;
- }
- else if (to_send_len != csp->expected_client_content_length &&
- update_client_headers(csp, to_send_len))
- {
- log_error(LOG_LEVEL_HEADER, "Error updating client headers");
- return 1;
- }
- csp->expected_client_content_length = 0;
- }
hdr = list_to_text(csp->headers);
if (hdr == NULL)
return 1;
}
- if (filter_client_body)
- {
- write_failure = 0 != write_socket(csp->server_connection.sfd, to_send, to_send_len);
- freez(to_send);
- if (write_failure)
- {
- log_error(LOG_LEVEL_CONNECT, "Failed sending filtered request body to: %s: %E",
- csp->http->hostport);
- return 1;
- }
- }
-
+ /* XXX: Filtered data is not sent if there's a pipelined request? */
if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
&& (flush_iob(csp->server_connection.sfd, csp->client_iob, 0) < 0))
{
size_t max_bytes_to_read = to_read < sizeof(buf) ? to_read : sizeof(buf);
log_error(LOG_LEVEL_CONNECT,
- "Waiting for up to %d bytes of request body from the client.",
+ "Buffering encrypted client body. Prepared to read up to %lu bytes.",
max_bytes_to_read);
len = ssl_recv_data(&(csp->ssl_client_attr), buf,
(unsigned)max_bytes_to_read);
if (len <= 0)
{
- log_error(LOG_LEVEL_CONNECT, "Failed receiving request body from %s", csp->ip_addr_str);
+ log_error(LOG_LEVEL_CONNECT,
+ "Did not receive the whole encrypted request body from %s.",
+ csp->ip_addr_str);
return 1;
}
if (add_to_iob(csp->client_iob, csp->config->buffer_limit, (char *)buf, len))
if (to_read != 0)
{
- log_error(LOG_LEVEL_CONNECT, "Not enough request body has been read: expected %d more bytes", to_read);
+ log_error(LOG_LEVEL_CONNECT,
+ "Not enough encrypted request body has been read: expected %lu more bytes.",
+ to_read);
return 1;
}
- log_error(LOG_LEVEL_CONNECT, "The last %d bytes of the request body have been read",
+ log_error(LOG_LEVEL_CONNECT,
+ "The last %llu bytes of the encrypted request body have been read.",
csp->expected_client_content_length);
return 0;
}
max_bytes_to_read = (int)csp->expected_client_content_length;
}
log_error(LOG_LEVEL_CONNECT,
- "Waiting for up to %d bytes of request body from the client.",
+ "Prepared to read up to %d bytes of encrypted request body from the client.",
max_bytes_to_read);
len = ssl_recv_data(&(csp->ssl_client_attr), buf,
(unsigned)max_bytes_to_read);
/* XXX: Does this actually happen? */
break;
}
- log_error(LOG_LEVEL_CONNECT, "Forwarding %d bytes of encrypted request body",
+ log_error(LOG_LEVEL_CONNECT, "Forwarding %d bytes of encrypted request body.",
len);
len = ssl_send_data(&(csp->ssl_server_attr), buf, (size_t)len);
if (len == -1)
}
if (csp->expected_client_content_length == 0)
{
- log_error(LOG_LEVEL_CONNECT, "Forwarded the last %d bytes", len);
+ log_error(LOG_LEVEL_CONNECT, "Forwarded the last %d bytes.", len);
break;
}
}
}
- log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted request body");
+ log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted request body.");
return 0;
char *hdr;
int ret;
long flushed = 0;
- const char *to_send;
- size_t to_send_len;
- int filter_client_body = csp->expected_client_content_length != 0 &&
- client_body_filters_enabled(csp->action) && can_filter_request_body(csp);
-
- if (filter_client_body)
- {
- if (read_https_request_body(csp))
- {
- return 1;
- }
- to_send_len = csp->expected_client_content_length;
- to_send = execute_client_body_filters(csp, &to_send_len);
- if (to_send == NULL)
- {
- /* just flush client_iob */
- filter_client_body = FALSE;
- }
- else if (to_send_len != csp->expected_client_content_length &&
- update_client_headers(csp, to_send_len))
- {
- log_error(LOG_LEVEL_HEADER, "Error updating client headers");
- return 1;
- }
- csp->expected_client_content_length = 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");
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header.");
}
list_remove_all(csp->https_headers);
return 1;
}
- if (filter_client_body)
- {
- ret = ssl_send_data(&(csp->ssl_server_attr), (const unsigned char *)to_send, to_send_len);
- freez(to_send);
- if (ret < 0)
- {
- log_error(LOG_LEVEL_CONNECT, "Failed sending filtered request body to: %s",
- csp->http->hostport);
- return 1;
- }
- }
-
+ /* XXX: Client body isn't sent if there's pipelined data? */
if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
&& ((flushed = ssl_flush_socket(&(csp->ssl_server_attr),
csp->client_iob)) < 0))
if (csp->expected_client_content_length < flushed)
{
log_error(LOG_LEVEL_ERROR,
- "Flushed %ld bytes of request body while only expecting %llu",
+ "Flushed %ld 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 %ld bytes of request body while expecting %llu",
+ "Flushed %ld 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))
else
{
log_error(LOG_LEVEL_CONNECT,
- "Flushed %ld bytes of request body", flushed);
+ "Flushed %ld bytes of request body.", flushed);
}
}
- log_error(LOG_LEVEL_CONNECT, "Encrypted request sent");
+ log_error(LOG_LEVEL_CONNECT, "Encrypted request sent.");
return 0;
/*********************************************************************
*
- * Function : receive_encrypted_request
+ * Function : receive_encrypted_request_headers
*
- * Description : Receives an encrypted request.
+ * Description : Receives the encrypted request headers when
+ * https-inspecting.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
* JB_ERR_PARSE or JB_ERR_MEMORY otherwise
*
*********************************************************************/
-static jb_err receive_encrypted_request(struct client_state *csp)
+static jb_err receive_encrypted_request_headers(struct client_state *csp)
{
char buf[BUFFER_SIZE];
int len;
do
{
- log_error(LOG_LEVEL_HEADER, "Reading encrypted headers");
+ log_error(LOG_LEVEL_HEADER, "Waiting for encrypted client headers");
if (!is_ssl_pending(&(csp->ssl_client_attr)) &&
!data_is_available(csp->cfd, csp->config->socket_timeout))
{
/*********************************************************************
*
- * Function : process_encrypted_request
+ * Function : process_encrypted_request_headers
*
- * Description : Receives and parses an encrypted request.
+ * Description : Receives and parses the encrypted headers send
+ * by the client when https-inspecting.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
* JB_ERR_PARSE or JB_ERR_MEMORY otherwise
*
*********************************************************************/
-static jb_err process_encrypted_request(struct client_state *csp)
+static jb_err process_encrypted_request_headers(struct client_state *csp)
{
char *p;
char *request_line;
csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
}
#endif
- err = receive_encrypted_request(csp);
+ err = receive_encrypted_request_headers(csp);
if (err != JB_ERR_OK)
{
if (csp->client_iob->cur == NULL ||
"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));
+ destroy_list(headers);
+
return JB_ERR_PARSE;
}
return JB_ERR_PARSE;
}
- log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
- log_applied_actions(csp->action);
+ log_error(LOG_LEVEL_HEADER, "Encrypted request headers processed");
log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport,
csp->http->path);
{
const struct forward_spec *fwd;
- if (JB_ERR_OK != process_encrypted_request(csp))
+ if (JB_ERR_OK != process_encrypted_request_headers(csp))
{
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
return;
}
+#if defined(FEATURE_STATISTICS) && defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_lock(&block_statistics_mutex);
+ number_of_requests_received++;
+ privoxy_mutex_unlock(&block_statistics_mutex);
+#endif
+
csp->requests_received_total++;
/*
}
assert(csp->server_connection.sfd != JB_INVALID_SOCKET);
+ if (csp->expected_client_content_length != 0 &&
+ (client_body_filters_enabled(csp->action) ||
+ client_body_taggers_enabled(csp->action)) &&
+ can_buffer_request_body(csp))
+ {
+ int content_modified;
+
+ if (read_https_request_body(csp))
+ {
+ /* XXX: handle */
+ return;
+ }
+ if (client_body_taggers_enabled(csp->action))
+ {
+ execute_client_body_taggers(csp, csp->expected_client_content_length);
+ if (crunch_response_triggered(csp, crunchers_all))
+ {
+ /*
+ * Yes. The client got the crunch response and we're done here.
+ */
+ return;
+ }
+ }
+ if (client_body_filters_enabled(csp->action))
+ {
+ size_t modified_content_length = csp->expected_client_content_length;
+ content_modified = execute_client_body_filters(csp,
+ &modified_content_length);
+ if ((content_modified == 1) &&
+ (modified_content_length != csp->expected_client_content_length) &&
+ update_client_headers(csp, modified_content_length))
+ {
+ /* XXX: Send error response */
+ log_error(LOG_LEVEL_HEADER, "Error updating client headers");
+ return;
+ }
+ }
+ csp->expected_client_content_length = 0;
+ }
+
fwd = forward_url(csp, csp->http);
if (!connection_destination_matches(&csp->server_connection, csp->http, fwd))
{
return;
}
+ log_applied_actions(csp->action);
+
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->cfd);
return;
}
+ csp->server_connection.request_sent = time(NULL);
csp->server_connection.requests_sent_total++;
handle_established_connection(csp);
freez(csp->receive_buffer);
#endif
+/*********************************************************************
+ *
+ * Function : send_server_headers
+ *
+ * Description : Sends the server headers to the client.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : 0 on succes, -1 on error.
+ *
+ *********************************************************************/
+static int send_server_headers(struct client_state *csp)
+{
+ char *server_headers;
+ int ret;
+
+ server_headers = list_to_text(csp->headers);
+ if (server_headers == NULL)
+ {
+ /*
+ * Memory is too tight to even generate the header.
+ * Send our static "Out-of-memory" page.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory while trying to send server headers.");
+ send_crunch_response(csp, cgi_error_memory());
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return -1;
+ }
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp))
+ {
+ ret = ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)server_headers, strlen(server_headers),
+ get_write_delay(csp));
+ }
+ else
+#endif
+ {
+ ret = write_socket_delayed(csp->cfd, server_headers, strlen(server_headers),
+ get_write_delay(csp));
+ }
+ freez(server_headers);
+ if (ret < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to send server headers to the client.");
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return -1;
+ }
+
+ return 0;
+
+}
+
/*********************************************************************
*
* Function : handle_established_connection
*
* Description : Shuffle data between client and server once the
- * connection has been established.
+ * connection has been established and the request
+ * has been sent.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*********************************************************************/
static void handle_established_connection(struct client_state *csp)
{
- char *hdr;
char *p;
int n;
#ifdef HAVE_POLL
long len = 0; /* for buffer sizes (and negative error codes) */
int buffer_and_filter_content = 0;
unsigned int write_delay;
+ size_t chunk_offset = 0;
#ifdef FEATURE_HTTPS_INSPECTION
int ret = 0;
int use_ssl_tunnel = 0;
for (;;)
{
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (server_use_ssl(csp) && is_ssl_pending(&(csp->ssl_server_attr)))
+ {
+ /*
+ * It's possible that the TLS library already consumed all the
+ * data the server intends to send. If that happens poll() and
+ * select() will no longer see the data as available so we have
+ * to skip the calls.
+ */
+ goto server_wants_to_talk;
+ }
+ if (watch_client_socket && client_use_ssl(csp) &&
+ is_ssl_pending(&(csp->ssl_client_attr)))
+ {
+ /*
+ * The TLS libray may also consume all of the remaining data
+ * from the client when we're shuffling the data from an
+ * unbuffered request body to the server.
+ */
+ goto client_wants_to_talk;
+ }
+#endif
#ifndef HAVE_POLL
FD_ZERO(&rfds);
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
#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)
n = select((int)maxfd + 1, &rfds, NULL, NULL, &timeout);
#endif /* def HAVE_POLL */
- /*server or client not responding in timeout */
+ /* Server or client not responding in timeout */
if (n == 0)
{
log_error(LOG_LEVEL_CONNECT, "Socket timeout %d reached: %s",
if (FD_ISSET(csp->cfd, &rfds))
#endif /* def HAVE_POLL*/
{
- int max_bytes_to_read = (int)csp->receive_buffer_size;
+ int max_bytes_to_read;
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ client_wants_to_talk:
+#endif
+
+ max_bytes_to_read = (int)csp->receive_buffer_size;
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ))
/*
* 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)
if (FD_ISSET(csp->server_connection.sfd, &rfds))
#endif /* HAVE_POLL */
{
+#ifdef FEATURE_HTTPS_INSPECTION
+ server_wants_to_talk:
+#endif
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
/*
* If we are buffering content, we don't want to eat up to
}
#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,
- "Looks like we reached the end of the last chunk. "
- "We better stop reading.");
- csp->expected_content_length = byte_count + (unsigned long long)len;
- csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
- }
- }
reading_done:
#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
"Failed to update server headers. after filtering.");
}
- hdr = list_to_text(csp->headers);
- if (hdr == NULL)
+ if (send_server_headers(csp))
{
- /* FIXME Should handle error properly */
- log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ return;
}
#ifdef FEATURE_HTTPS_INSPECTION
*/
if (client_use_ssl(csp))
{
- if ((ssl_send_data_delayed(&(csp->ssl_client_attr),
- (const unsigned char *)hdr, strlen(hdr),
- get_write_delay(csp)) < 0)
- || (ssl_send_data_delayed(&(csp->ssl_client_attr),
+ if (ssl_send_data_delayed(&(csp->ssl_client_attr),
(const unsigned char *) ((p != NULL) ? p : csp->iob->cur),
- csp->content_length, get_write_delay(csp)) < 0))
+ csp->content_length, get_write_delay(csp)) < 0)
{
- log_error(LOG_LEVEL_ERROR, "write modified content to "
- "client over TLS/SSL failed");
- freez(hdr);
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to send the modified content to the client over TLS");
freez(p);
mark_server_socket_tainted(csp);
close_client_and_server_ssl_connections(csp);
else
#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),
+ if (write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur),
(size_t)csp->content_length, write_delay))
{
log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
- freez(hdr);
freez(p);
mark_server_socket_tainted(csp);
return;
}
}
- freez(hdr);
freez(p);
}
}
/*
- * This is NOT the body, so
- * Let's pretend the server just sent us a blank line.
+ * This is not the body, so let's pretend the server just sent
+ * us a blank line.
*/
snprintf(csp->receive_buffer, csp->receive_buffer_size, "\r\n");
len = (int)strlen(csp->receive_buffer);
*/
if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len))
{
- size_t hdrlen;
long flushed;
log_error(LOG_LEVEL_INFO,
"Flushing header and buffers. Stepping back from filtering.");
- hdr = list_to_text(csp->headers);
- if (hdr == NULL)
+ if (send_server_headers(csp))
{
- /*
- * Memory is too tight to even generate the header.
- * Send our static "Out-of-memory" page.
- */
- log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush.");
- rsp = cgi_error_memory();
- send_crunch_response(csp, rsp);
- mark_server_socket_tainted(csp);
-#ifdef FEATURE_HTTPS_INSPECTION
- close_client_and_server_ssl_connections(csp);
-#endif
return;
}
- hdrlen = strlen(hdr);
#ifdef FEATURE_HTTPS_INSPECTION
/*
*/
if (client_use_ssl(csp))
{
- if ((ssl_send_data_delayed(&(csp->ssl_client_attr),
- (const unsigned char *)hdr, hdrlen, get_write_delay(csp)) < 0)
- || ((flushed = ssl_flush_socket(&(csp->ssl_client_attr),
+ if (((flushed = ssl_flush_socket(&(csp->ssl_client_attr),
csp->iob)) < 0)
|| (ssl_send_data_delayed(&(csp->ssl_client_attr),
(const unsigned char *)csp->receive_buffer, (size_t)len,
{
log_error(LOG_LEVEL_CONNECT,
"Flush header and buffers to client failed");
- freez(hdr);
mark_server_socket_tainted(csp);
close_client_and_server_ssl_connections(csp);
return;
else
#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)
+ if (((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)
|| write_socket_delayed(csp->cfd, csp->receive_buffer, (size_t)len,
write_delay))
{
log_error(LOG_LEVEL_CONNECT,
"Flush header and buffers to client failed: %E");
- freez(hdr);
mark_server_socket_tainted(csp);
return;
}
}
/*
- * Reset the byte_count to the amount of bytes
- * we just flushed. len will be added a few lines below,
- * hdrlen doesn't matter for LOG_LEVEL_CLF.
+ * Reset the byte_count to the amount of bytes we just
+ * flushed. len will be added a few lines below.
*/
byte_count = (unsigned long long)flushed;
- freez(hdr);
+ if ((csp->flags & CSP_FLAG_CHUNKED) && (chunk_offset != 0))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Reducing chunk offset %lu by %ld to %lu.", chunk_offset, flushed,
+ (chunk_offset - (unsigned)flushed));
+ assert(chunk_offset >= flushed); /* XXX: Reachable with malicious input? */
+ chunk_offset -= (unsigned)flushed;
+
+ /* Make room in the iob. */
+ csp->iob->cur = csp->iob->eod = csp->iob->buf;
+
+ if (add_to_iob(csp->iob, csp->config->buffer_limit,
+ csp->receive_buffer, len))
+ {
+ /* This is not supposed to happen but ... */
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ log_error(LOG_LEVEL_ERROR, "Failed to buffer %ld bytes of "
+ "chunk-encoded data after resetting the buffer.", len);
+ return;
+ }
+ }
buffer_and_filter_content = 0;
server_body = 1;
}
return;
}
}
+ if (csp->flags & CSP_FLAG_CHUNKED)
+ {
+ /*
+ * While we don't need the data to filter it, put it in the
+ * buffer so we can keep track of the offset to the start of
+ * the next chunk and detect when the response is finished.
+ */
+ size_t encoded_bytes = (size_t)(csp->iob->eod - csp->iob->cur);
+
+ if (csp->config->buffer_limit / 4 < encoded_bytes)
+ {
+ /*
+ * Reset the buffer to reduce the memory footprint.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "Reducing the chunk offset from %lu to %lu after "
+ "discarding %lu bytes to make room in the buffer.",
+ chunk_offset, (chunk_offset - encoded_bytes),
+ encoded_bytes);
+ chunk_offset -= encoded_bytes;
+ csp->iob->cur = csp->iob->eod = csp->iob->buf;
+ }
+ if (add_to_iob(csp->iob, csp->config->buffer_limit,
+ csp->receive_buffer, len))
+ {
+ /* This is not supposed to happen but ... */
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to buffer %ld bytes of chunk-encoded data.",
+ len);
+ return;
+ }
+ }
}
byte_count += (unsigned long long)len;
+
+ if (csp->flags & CSP_FLAG_CHUNKED)
+ {
+ int rc;
+ size_t encoded_bytes = (size_t)(csp->iob->eod - csp->iob->cur);
+
+ rc = get_bytes_missing_from_chunked_data(csp->iob->cur, encoded_bytes,
+ chunk_offset);
+ if (rc >= 0)
+ {
+ if (rc != 0)
+ {
+ chunk_offset = (size_t)rc;
+ }
+
+ if (chunked_data_is_complete(csp->iob->cur, encoded_bytes, chunk_offset))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "We buffered the last chunk of the response.");
+ csp->expected_content_length = byte_count;
+ csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
+ }
+ }
+ }
+
continue;
}
else
return;
}
+ /*
+ * Disable redirect checkers, so that they will be only run
+ * again if the user also enables them through tags.
+ *
+ * From a performance point of view it doesn't matter,
+ * but it prevents duplicated log messages.
+ */
+#ifdef FEATURE_FAST_REDIRECTS
+ csp->action->flags &= ~ACTION_FAST_REDIRECTS;
+#endif
+ csp->action->flags &= ~ACTION_REDIRECT;
+
/*
* We have now received the entire server header,
* filter it and send the result to the client
#endif
return;
}
- hdr = list_to_text(csp->headers);
- if (hdr == NULL)
- {
- /* FIXME Should handle error properly */
- log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
- }
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))
+ && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET))
{
- log_error(LOG_LEVEL_CONNECT,
- "Looks like we got the last chunk together with "
- "the server headers. 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;
- }
+ int rc;
+ size_t encoded_size = (size_t)(csp->iob->eod - csp->iob->cur);
+ rc = get_bytes_missing_from_chunked_data(csp->iob->cur, encoded_size,
+ chunk_offset);
+ if (rc >= 0)
+ {
+ if (rc != 0)
+ {
+ chunk_offset = (size_t)rc;
+ }
+ if (chunked_data_is_complete(csp->iob->cur, encoded_size, chunk_offset))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Looks like we got the last chunk together with "
+ "the server headers. 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;
+ }
+ }
+ }
csp->server_connection.response_received = time(NULL);
if (crunch_response_triggered(csp, crunchers_light))
* delivered the crunch response to the client
* and are done here after cleaning up.
*/
- freez(hdr);
mark_server_socket_tainted(csp);
#ifdef FEATURE_HTTPS_INSPECTION
close_client_and_server_ssl_connections(csp);
* may be in the buffer). Use standard or secured
* connection.
*/
+ if (send_server_headers(csp))
+ {
+ return;
+ }
#ifdef FEATURE_HTTPS_INSPECTION
if (client_use_ssl(csp))
{
- if ((ssl_send_data_delayed(&(csp->ssl_client_attr),
- (const unsigned char *)hdr, strlen(hdr),
- get_write_delay(csp)) < 0)
- || (len = ssl_flush_socket(&(csp->ssl_client_attr),
- csp->iob) < 0))
+ if ((len = ssl_flush_socket(&(csp->ssl_client_attr),
+ csp->iob)) < 0)
{
- log_error(LOG_LEVEL_CONNECT, "Write header to client failed");
+ log_error(LOG_LEVEL_CONNECT,
+ "Sending buffered bytes to the client failed");
/*
* The write failed, so don't bother mentioning it
* to the client... it probably can't hear us anyway.
*/
- freez(hdr);
mark_server_socket_tainted(csp);
#ifdef FEATURE_HTTPS_INSPECTION
close_client_and_server_ssl_connections(csp);
else
#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))
+ if ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)
{
log_error(LOG_LEVEL_ERROR,
- "write header to client failed");
+ "Sending buffered bytes to the client failed.");
/*
* The write failed, so don't bother mentioning it
* to the client... it probably can't hear us anyway.
*/
- freez(hdr);
mark_server_socket_tainted(csp);
return;
}
+ }
+ if (csp->flags & CSP_FLAG_CHUNKED &&
+ !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET))
+ {
+ /*
+ * In case of valid data we shouldn't flush more
+ * data than chunk_offset but the data may be invalid.
+ */
+ if (chunk_offset >= len)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Reducing the chunk offset "
+ "from %lu to %lu after flushing %ld bytes.",
+ chunk_offset, (chunk_offset - (unsigned)len), len);
+ chunk_offset = chunk_offset - (unsigned)len;
+ }
+ else
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Keeping chunk offset at %lu despite flushing %ld bytes",
+ chunk_offset, len);
+ /*
+ * If we can't parse the chunk-encoded data we should
+ * not reuse the server connection.
+ */
+ mark_server_socket_tainted(csp);
+ }
}
}
/* we're finished with the server's header */
- freez(hdr);
server_body = 1;
/*
{
return;
}
+
+#if defined(FEATURE_STATISTICS) && defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_lock(&block_statistics_mutex);
+ number_of_requests_received++;
+ privoxy_mutex_unlock(&block_statistics_mutex);
+#endif
+
if (parse_client_request(csp) != JB_ERR_OK)
{
return;
}
- /* decide how to route the HTTP request */
- fwd = forward_url(csp, http);
-
#ifdef FEATURE_HTTPS_INSPECTION
/*
* Setting flags to use old solution with SSL tunnel and to disable
#endif
}
-
- freez(csp->headers->first->str);
- build_request_line(csp, fwd, &csp->headers->first->str);
-
/*
* We have a request. Check if one of the crunchers wants it
* unless the client wants to use TLS/SSL in which case we
return;
}
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp) && !use_ssl_tunnel)
+ {
+ int ret;
+ /*
+ * Creating a SSL proxy.
+ *
+ * By sending the CSUCCEED message 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,
+ "Failed to open a secure connection with the client");
+ return;
+ }
+ if (JB_ERR_OK != process_encrypted_request_headers(csp))
+ {
+ 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.
+ */
+ return;
+ }
+ }
+#endif
+
+ /* If we need to apply client body filters, buffer the whole request now. */
+ if (csp->expected_client_content_length != 0 &&
+ (client_body_filters_enabled(csp->action) ||
+ client_body_taggers_enabled(csp->action)) &&
+ can_buffer_request_body(csp))
+ {
+ int content_modified;
+ size_t modified_content_length;
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp) && read_https_request_body(csp))
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to buffer the encrypted "
+ "request body to apply filters or taggers.");
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd);
+
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CLIENT_BODY_BUFFER_ERROR_RESPONSE,
+ strlen(CLIENT_BODY_BUFFER_ERROR_RESPONSE),
+ get_write_delay(csp));
+
+ return;
+ }
+ else
+#endif
+ if (read_http_request_body(csp))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to buffer the request body to apply filters or taggers,");
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd);
+
+ write_socket_delayed(csp->cfd, CLIENT_BODY_BUFFER_ERROR_RESPONSE,
+ strlen(CLIENT_BODY_BUFFER_ERROR_RESPONSE), get_write_delay(csp));
+
+ return;
+ }
+ if (client_body_taggers_enabled(csp->action))
+ {
+ execute_client_body_taggers(csp, csp->expected_client_content_length);
+ if (crunch_response_triggered(csp, crunchers_all))
+ {
+ /*
+ * Yes. The client got the crunch response and we're done here.
+ */
+ return;
+ }
+ }
+ if (client_body_filters_enabled(csp->action))
+ {
+ modified_content_length = csp->expected_client_content_length;
+ content_modified = execute_client_body_filters(csp,
+ &modified_content_length);
+ if ((content_modified == 1) &&
+ (modified_content_length != csp->expected_client_content_length) &&
+ update_client_headers(csp, modified_content_length))
+ {
+ /* XXX: Send error response */
+ log_error(LOG_LEVEL_HEADER, "Error updating client headers");
+ return;
+ }
+ }
+ csp->expected_client_content_length = 0;
+ }
+
log_applied_actions(csp->action);
+
+ /* decide how to route the HTTP request */
+ fwd = forward_url(csp, http);
+
+ freez(csp->headers->first->str);
+ build_request_line(csp, fwd, &csp->headers->first->str);
+
if (fwd->forward_host)
{
log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s",
mark_connection_closed(&csp->server_connection);
}
#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
-#ifdef FEATURE_HTTPS_INSPECTION
- if (client_use_ssl(csp) && !use_ssl_tunnel)
- {
- int ret;
- /*
- * Creating a SSL proxy.
- *
- * By sending the CSUCCEED message 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,
- "Failed to open a secure connection with the client");
- return;
- }
- if (JB_ERR_OK != process_encrypted_request(csp))
- {
- 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.
- */
- return;
- }
- }
-#endif
/*
* Connecting to destination server
*/
#endif
))
{
- if (send_http_request(csp))
+ int status = send_http_request(csp);
+ if (status == 2)
+ {
+ /* The request got crunched, a response has been delivered. */
+ return;
+ }
+ if (status != 0)
{
rsp = error_response(csp, "connect-failed");
if (rsp)
log_error(LOG_LEVEL_CONNECT,
"Closing server socket %d connected to %s. "
"Keep-alive: %u. Tainted: %u. Socket alive: %u. Timeout: %u.",
- csp->server_connection.sfd, csp->server_connection.host,
+ csp->server_connection.sfd, (csp->server_connection.host != NULL) ?
+ csp->server_connection.host : csp->http->host,
0 != (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE),
0 != (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED),
socket_is_still_alive(csp->server_connection.sfd),
#endif
close_socket(csp->server_connection.sfd);
mark_connection_closed(&csp->server_connection);
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (continue_chatting && client_use_ssl(csp))
+ {
+ /*
+ * Close the client socket as well as Privoxy currently
+ * can't establish a new server connection when the client
+ * socket is reused and would drop the connection in
+ * continue_https_chat() anyway.
+ */
+ continue_chatting = 0;
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ log_error(LOG_LEVEL_CONNECT,
+ "Client socket %d is no longer usable. "
+ "The server socket has been closed.", csp->cfd);
+ }
+#endif
}
}
#ifdef FEATURE_CLIENT_TAGS
privoxy_mutex_init(&client_tags_mutex);
#endif
+#ifdef FEATURE_STATISTICS
+ privoxy_mutex_init(&block_statistics_mutex);
+#endif
#ifdef FEATURE_EXTENDED_STATISTICS
privoxy_mutex_init(&filter_statistics_mutex);
- privoxy_mutex_init(&block_statistics_mutex);
+ privoxy_mutex_init(&block_reason_statistics_mutex);
#endif
/*
"can't bind to %s:%d: There may be another Privoxy "
"or some other proxy running on port %d",
bind_address, hport, hport);
+ exit(-1);
case -2:
log_error(LOG_LEVEL_FATAL,
"can't bind to %s:%d: The hostname is not resolvable",
bind_address, hport);
+ exit(-1);
default:
log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: %E",
bind_address, hport);
+ exit(-1);
}
/* shouldn't get here */
if ((0 != config->max_client_connections)
&& (active_threads >= config->max_client_connections))
{
- log_error(LOG_LEVEL_CONNECT,
+ log_error(LOG_LEVEL_ERROR,
"Rejecting connection from %s. Maximum number of connections reached.",
csp->ip_addr_str);
write_socket_delayed(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE,
}
#endif
-#if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
+#if defined(_WIN32) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
child_id = _beginthread(
(void (*)(void *))serve,
/* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */
-#ifdef FEATURE_HTTPS_INSPECTION
- /* Clean up. Aim: free all memory (no leaks) */
- ssl_release();
-#endif
-
#ifdef FEATURE_GRACEFUL_TERMINATION
- log_error(LOG_LEVEL_INFO, "Graceful termination requested");
+ log_error(LOG_LEVEL_INFO, "Graceful termination requested.");
+
+ close_ports_helper(bfds);
unload_current_config_file();
unload_current_actions_file();
if (i <= 0)
{
- log_error(LOG_LEVEL_ERROR, "Graceful termination failed - still some live clients after 1 minute wait.");
+ log_error(LOG_LEVEL_ERROR, "Graceful termination failed "
+ "- still some live clients after 1 minute wait.");
}
}
sweep();
freez(basedir);
#endif
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Only release TLS backed resources if there
+ * are no active connections left.
+ */
+ if (clients->next == NULL)
+ {
+ ssl_release();
+ }
+#endif
+
log_error(LOG_LEVEL_INFO, "Exiting gracefully.");
#if defined(_WIN32) && !defined(_WIN_CONSOLE)