* Purpose : Main file. Contains main() method, main loop, and
* the main connection-handling function.
*
- * Copyright : Written by and Copyright (C) 2001-2020 the
+ * Copyright : Written by and Copyright (C) 2001-2021 the
* Privoxy team. https://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
#else /* ifndef _WIN32 */
-# if !defined (__OS2__)
# include <unistd.h>
# include <sys/wait.h>
-# endif /* ndef __OS2__ */
# include <sys/time.h>
# include <sys/stat.h>
# include <sys/ioctl.h>
# include <OS.h> /* declarations for threads and stuff. */
# endif
-# if defined(__EMX__) || defined(__OS2__)
-# include <sys/select.h> /* OS/2/EMX needs a little help with select */
-# endif
-# ifdef __OS2__
-#define INCL_DOS
-# include <os2.h>
-# endif
-
#ifdef HAVE_POLL
#ifdef __GLIBC__
#include <sys/poll.h>
int g_terminate = 0;
#endif
-#if !defined(_WIN32) && !defined(__OS2__)
+#if !defined(_WIN32)
static void sig_handler(int the_signal);
#endif
static int client_protocol_is_unsupported(struct client_state *csp, char *req);
static jb_err parse_client_request(struct client_state *csp);
static void build_request_line(struct client_state *csp, const struct forward_spec *fwd, char **request_line);
static jb_err change_request_destination(struct client_state *csp);
+static void handle_established_connection(struct client_state *csp);
static void chat(struct client_state *csp);
static void serve(struct client_state *csp);
#if !defined(_WIN32) || defined(_WIN_CONSOLE)
#define sleep(N) Sleep(((N) * 1000))
#endif
-#ifdef __OS2__
-#define sleep(N) DosSleep(((N) * 100))
-#endif
-
#ifdef FUZZ
int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file);
void show_fuzz_usage(const char *name);
#ifdef FEATURE_HTTPS_INSPECTION
privoxy_mutex_t certificate_mutex;
-privoxy_mutex_t rng_mutex;
+privoxy_mutex_t ssl_init_mutex;
#endif
#ifdef FEATURE_EXTERNAL_FILTERS
#ifdef FEATURE_CLIENT_TAGS
privoxy_mutex_t client_tags_mutex;
#endif
+#ifdef FEATURE_EXTENDED_STATISTICS
+privoxy_mutex_t filter_statistics_mutex;
+privoxy_mutex_t block_statistics_mutex;
+#endif
#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
privoxy_mutex_t resolver_mutex;
*
* here?
*/
-#if !defined(_WIN32) && !defined(__OS2__)
+#if !defined(_WIN32)
/*********************************************************************
*
* Function : sig_handler
* 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_HTTPS_INSPECTION
if (client_use_ssl(csp))
{
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)response, strlen(response));
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)response, strlen(response),
+ get_write_delay(csp));
}
else
#endif
*********************************************************************/
static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers)
{
- char *req;
-
if (!(csp->config->feature_flags & RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS))
{
log_error(LOG_LEVEL_ERROR, "%s's request: \'%s\' is invalid."
}
else if (JB_ERR_OK == get_destination_from_headers(headers, csp->http))
{
-#ifndef FEATURE_EXTENDED_HOST_PATTERNS
/* Split the domain we just got for pattern matching */
init_domain_components(csp->http);
-#endif
return JB_ERR_OK;
}
{
/* We can't work without destination. Go spread the news.*/
- req = list_to_text(headers);
- chomp(req);
/* XXX: Use correct size */
log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0",
csp->ip_addr_str, csp->http->cmd);
log_error(LOG_LEVEL_ERROR,
- "Privoxy was unable to get the destination for %s's request:\n%s\n%s",
- csp->ip_addr_str, csp->http->cmd, req);
- freez(req);
+ "Privoxy was unable to get the destination for %s's request: %s",
+ csp->ip_addr_str, csp->http->cmd);
write_socket_delayed(csp->cfd, MISSING_DESTINATION_RESPONSE,
strlen(MISSING_DESTINATION_RESPONSE), get_write_delay(csp));
{
log_error(LOG_LEVEL_CRUNCH, "%s: https://%s%s", crunch_reason(rsp),
http->hostport, http->path);
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s https://%s%s %s\" %s %llu",
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s https://%s%s %s\" %s %lu",
csp->ip_addr_str, http->gpc, http->hostport, http->path,
http->version, status_code, rsp->content_length);
}
#endif
{
log_error(LOG_LEVEL_CRUNCH, "%s: %s", crunch_reason(rsp), http->url);
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %u",
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %lu",
csp->ip_addr_str, http->ocmd, status_code, rsp->content_length);
}
/* Write the answer to the client */
#ifdef FEATURE_HTTPS_INSPECTION
if (client_use_ssl(csp))
{
- 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))
+ if ((ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)rsp->head, rsp->head_length,
+ get_write_delay(csp)) < 0)
+ || (ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)rsp->body, rsp->content_length,
+ get_write_delay(csp)) < 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);
+ "for https://%s%s through client socket %d using TLS/SSL",
+ http->hostport, http->url, csp->cfd);
}
}
else
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
- * 2 : fwd = The forwarding spec used for the request
- * XXX: Should use http->fwd instead.
+ * 2 : fwd = The forwarding spec used for the request.
+ * Can be NULL.
* 3 : request_line = The old request line which will be replaced.
*
* Returns : Nothing. Terminates in case of memory problems.
*request_line = strdup(http->gpc);
string_append(request_line, " ");
- if (fwd->forward_host && fwd->type != FORWARD_WEBSERVER)
+ if (fwd != NULL && fwd->forward_host && fwd->type != FORWARD_WEBSERVER)
{
string_append(request_line, http->url);
}
log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.",
jb_err_to_string(err));
}
+ if (http->ssl && strcmpic(csp->http->gpc, "CONNECT"))
+ {
+ /*
+ * A client header filter changed the request URL from
+ * http:// to https:// which we currently don't support.
+ */
+ log_error(LOG_LEVEL_ERROR, "Changing the request destination from http "
+ "to https behind the client's back currently isn't supported.");
+ return JB_ERR_PARSE;
+ }
return err;
}
server_connection->gateway_host = NULL;
}
server_connection->gateway_port = fwd->gateway_port;
+ if (NULL != fwd->auth_username)
+ {
+ server_connection->auth_username = strdup_or_die(fwd->auth_username);
+ }
+ else
+ {
+ server_connection->auth_username = NULL;
+ }
+ if (NULL != fwd->auth_password)
+ {
+ server_connection->auth_password = strdup_or_die(fwd->auth_password);
+ }
+ else
+ {
+ server_connection->auth_password = NULL;
+ }
if (NULL != fwd->forward_host)
{
}
server_connection->forward_port = fwd->forward_port;
}
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
/*********************************************************************
log_error(LOG_LEVEL_CONNECT, "Complete client request received.");
}
}
-#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
/*********************************************************************
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))
return JB_ERR_PARSE;
}
log_error(LOG_LEVEL_CONNECT,
- "Chunked client body completely read. Length: %d", body_length);
+ "Chunked client body completely read. Length: %lu", body_length);
csp->expected_client_content_length = body_length;
return JB_ERR_OK;
* 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;
/* Assume persistence until further notice */
csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
}
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
if (csp->http->ssl == 0)
{
}
verify_request_length(csp);
}
+#ifndef FEATURE_HTTPS_INSPECTION
else
{
csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED;
}
-#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+#endif
err = sed(csp, FILTER_CLIENT_HEADERS);
if (JB_ERR_OK != err)
strlen(MESSED_UP_REQUEST_RESPONSE), get_write_delay(csp));
/* XXX: Use correct size */
log_error(LOG_LEVEL_CLF,
- "%s - - [%T] \"Invalid request generated\" 500 0", csp->ip_addr_str);
+ "%s - - [%T] \"Invalid request generated\" 400 0", csp->ip_addr_str);
log_error(LOG_LEVEL_ERROR,
"Invalid request line after applying header filters.");
free_http_request(http);
}
+/*********************************************************************
+ *
+ * Function : read_http_request_body
+ *
+ * Description : Reads remaining request body from the client.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : 0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static int read_http_request_body(struct client_state *csp)
+{
+ size_t to_read = csp->expected_client_content_length;
+ int len;
+
+ assert(to_read != 0);
+
+ /* check if all data has been already read */
+ if (to_read <= (csp->client_iob->eod - csp->client_iob->cur))
+ {
+ return 0;
+ }
+
+ for (to_read -= (size_t)(csp->client_iob->eod - csp->client_iob->cur);
+ to_read > 0 && data_is_available(csp->cfd, csp->config->socket_timeout);
+ to_read -= (unsigned)len)
+ {
+ char buf[BUFFER_SIZE];
+ 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.",
+ max_bytes_to_read);
+ len = read_socket(csp->cfd, buf, (int)max_bytes_to_read);
+ if (len <= -1)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Failed receiving request body from %s: %E", csp->ip_addr_str);
+ return 1;
+ }
+ if (add_to_iob(csp->client_iob, csp->config->buffer_limit, (char *)buf, len))
+ {
+ return 1;
+ }
+ assert(to_read >= len);
+ }
+
+ 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);
+ return 1;
+ }
+ log_error(LOG_LEVEL_CONNECT, "The last %d bytes of the request body have been read",
+ csp->expected_client_content_length);
+ return 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function : update_client_headers
+ *
+ * Description : Updates the HTTP headers from the client request.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : new_content_length = new content length value to set
+ *
+ * Returns : 0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static int update_client_headers(struct client_state *csp, size_t new_content_length)
+{
+ static const char content_length[] = "Content-Length:";
+ int updated = 0;
+ struct list_entry *p;
+
+#ifndef FEATURE_HTTPS_INSPECTION
+ for (p = csp->headers->first;
+#else
+ for (p = csp->http->client_ssl ? csp->https_headers->first : csp->headers->first;
+#endif
+ !updated && (p != NULL); p = p->next)
+ {
+ /* Header crunch()ed in previous run? -> ignore */
+ if (p->str == NULL)
+ {
+ continue;
+ }
+
+ /* Does the current parser handle this header? */
+ if (0 == strncmpic(p->str, content_length, sizeof(content_length) - 1))
+ {
+ updated = (JB_ERR_OK == header_adjust_content_length((char **)&(p->str), new_content_length));
+ if (!updated)
+ {
+ return 1;
+ }
+ }
+ }
+
+ return !updated;
+}
+
+
+/*********************************************************************
+ *
+ * Function : can_filter_request_body
+ *
+ * Description : Checks if the current request body can be stored in
+ * the client_iob without hitting buffer limit.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : TRUE if the current request size do not exceed buffer limit
+ * FALSE otherwise.
+ *
+ *********************************************************************/
+static int can_filter_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,
+ csp->expected_client_content_length);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
/*********************************************************************
*
* Function : send_http_request
{
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)
{
log_error(LOG_LEVEL_CONNECT, "Failed sending request headers to: %s: %E",
csp->http->hostport);
+ 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;
+ }
}
- else if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
+
+ if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
&& (flush_iob(csp->server_connection.sfd, csp->client_iob, 0) < 0))
{
- write_failure = 1;
log_error(LOG_LEVEL_CONNECT, "Failed sending request body to: %s: %E",
csp->http->hostport);
+ return 1;
+ }
+ return 0;
+}
+
+
+#ifdef FEATURE_HTTPS_INSPECTION
+/*********************************************************************
+ *
+ * Function : read_https_request_body
+ *
+ * Description : Reads remaining request body from the client.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : 0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static int read_https_request_body(struct client_state *csp)
+{
+ size_t to_read = csp->expected_client_content_length;
+ int len;
+
+ assert(to_read != 0);
+
+ /* check if all data has been already read */
+ if (to_read <= (csp->client_iob->eod - csp->client_iob->cur))
+ {
+ return 0;
+ }
+
+ for (to_read -= (size_t)(csp->client_iob->eod - csp->client_iob->cur);
+ to_read > 0 && (is_ssl_pending(&(csp->ssl_client_attr)) ||
+ data_is_available(csp->cfd, csp->config->socket_timeout));
+ to_read -= (unsigned)len)
+ {
+ unsigned char buf[BUFFER_SIZE];
+ 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.",
+ 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);
+ return 1;
+ }
+ if (add_to_iob(csp->client_iob, csp->config->buffer_limit, (char *)buf, len))
+ {
+ return 1;
+ }
+ assert(to_read >= len);
}
- return write_failure;
+ if (to_read != 0)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Not enough request body has been read: expected %d 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);
+ return 0;
}
-#ifdef FEATURE_HTTPS_INSPECTION
/*********************************************************************
*
* Function : receive_and_send_encrypted_post_data
*
- * Description : Reads remaining POST data from the client and sends
+ * Description : Reads remaining request body from the client and sends
* it to the server.
*
* Parameters :
{
int content_length_known = csp->expected_client_content_length != 0;
- while (is_ssl_pending(&(csp->mbedtls_client_attr.ssl))
+ while (is_ssl_pending(&(csp->ssl_client_attr))
|| (content_length_known && csp->expected_client_content_length != 0))
{
unsigned char buf[BUFFER_SIZE];
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.",
+ "Waiting for up to %d bytes of request body from the client.",
max_bytes_to_read);
- len = ssl_recv_data(&(csp->mbedtls_client_attr.ssl), buf,
+ len = ssl_recv_data(&(csp->ssl_client_attr), buf,
(unsigned)max_bytes_to_read);
if (len == -1)
{
/* XXX: Does this actually happen? */
break;
}
- log_error(LOG_LEVEL_CONNECT, "Forwarding %d bytes of encrypted POST data",
+ log_error(LOG_LEVEL_CONNECT, "Forwarding %d bytes of encrypted request body",
len);
- len = ssl_send_data(&(csp->mbedtls_server_attr.ssl), buf, (size_t)len);
+ len = ssl_send_data(&(csp->ssl_server_attr), buf, (size_t)len);
if (len == -1)
{
return 1;
}
}
- log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted POST data");
+ 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)
* 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),
+ ret = ssl_send_data(&(csp->ssl_server_attr),
(const unsigned char *)hdr, strlen(hdr));
freez(hdr);
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;
+ }
+ }
+
if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
- && ((flushed = ssl_flush_socket(&(csp->mbedtls_server_attr.ssl),
+ && ((flushed = ssl_flush_socket(&(csp->ssl_server_attr),
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 (flushed != 0 || csp->expected_client_content_length != 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 %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 %d 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 %d bytes of request body", flushed);
+ "Flushed %ld bytes of request body", flushed);
}
}
do
{
log_error(LOG_LEVEL_HEADER, "Reading encrypted headers");
- if (!data_is_available(csp->cfd, (int)csp->config->keep_alive_timeout))
+ if (!is_ssl_pending(&(csp->ssl_client_attr)) &&
+ !data_is_available(csp->cfd, csp->config->socket_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),
+ len = ssl_recv_data(&(csp->ssl_client_attr),
(unsigned char *)buf, sizeof(buf));
+ if (len == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Socket %d closed while waiting for client headers", csp->cfd);
+ return JB_ERR_PARSE;
+ }
if (len == -1)
{
return JB_ERR_PARSE;
}
+/*********************************************************************
+ *
+ * Function : change_encrypted_request_destination
+ *
+ * Description : Parse a (rewritten) request line from an encrypted
+ * request and regenerate the http request data.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Forwards the parse_http_request() return code.
+ * Terminates in case of memory problems.
+ *
+ *********************************************************************/
+static jb_err change_encrypted_request_destination(struct client_state *csp)
+{
+ jb_err err;
+ char *original_host = csp->http->host;
+ int original_port = csp->http->port;
+
+ log_error(LOG_LEVEL_REDIRECTS, "Rewrite detected: %s",
+ csp->https_headers->first->str);
+ csp->http->host = NULL;
+ free_http_request(csp->http);
+ err = parse_http_request(csp->https_headers->first->str, csp->http);
+ if (JB_ERR_OK != err)
+ {
+ log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.",
+ jb_err_to_string(err));
+ freez(original_host);
+ return err;
+ }
+
+ if (csp->http->host == NULL)
+ {
+ char port_string[10];
+ /*
+ * The rewritten request line did not specify a host
+ * which means we can use the original host specified
+ * by the client.
+ */
+ csp->http->host = original_host;
+ csp->http->port = original_port;
+ log_error(LOG_LEVEL_REDIRECTS, "Keeping the original host: %s",
+ csp->http->host);
+ /*
+ * If the rewritten request line didn't contain a host
+ * it also didn't contain a port so we can reuse the host
+ * port.
+ */
+ freez(csp->http->hostport);
+ csp->http->hostport = strdup_or_die(csp->http->host);
+ snprintf(port_string, sizeof(port_string), ":%d", original_port);
+ err = string_append(&csp->http->hostport, port_string);
+ if (err != JB_ERR_OK)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to rebuild hostport: %s.",
+ jb_err_to_string(err));
+ return err;
+ }
+
+ /*
+ * While the request line didn't mention it,
+ * we're https-inspecting and want to speak TLS
+ * with the server.
+ */
+ csp->http->server_ssl = 1;
+ csp->http->ssl = 1;
+ }
+ else
+ {
+ /* The rewrite filter added a host so we can ditch the original */
+ freez(original_host);
+ csp->http->server_ssl = csp->http->ssl;
+ }
+
+ csp->http->client_ssl = 1;
+
+ freez(csp->https_headers->first->str);
+ build_request_line(csp, NULL, &csp->https_headers->first->str);
+
+ 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
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(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)CHEADER, strlen(CHEADER));
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
return err;
}
if (request_line == NULL)
{
log_error(LOG_LEVEL_ERROR, "Failed to get the encrypted request line");
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)CHEADER, strlen(CHEADER));
+ 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');
freez(request_line);
if (JB_ERR_OK != err)
{
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)CHEADER, strlen(CHEADER));
+ 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,
return JB_ERR_PARSE;
}
- /* Parse the rest of the client's headers. */
- init_list(headers);
- for (;;)
+ /* 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))
{
- p = get_header(csp->client_iob);
-
- if (p == NULL)
- {
- /* There are no additional headers to read. */
- break;
- }
- enlist(headers, p);
- freez(p);
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ return;
}
- if (JB_ERR_OK != get_destination_from_https_headers(headers, csp->http))
+ csp->requests_received_total++;
+
+ /*
+ * We have an encrypted request. Check if one of the crunchers wants it.
+ */
+ if (crunch_response_triggered(csp, crunchers_all))
{
/*
- * Our attempts to get the request destination
- * elsewhere failed.
+ * Yes. The client got the crunch response and we're done here.
*/
- log_error(LOG_LEVEL_ERROR,
- "Failed to get the encrypted request destination");
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)CHEADER, strlen(CHEADER));
- return JB_ERR_PARSE;
+ return;
}
-
-#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
+ if (csp->ssl_with_server_is_opened == 0)
{
- /* Determine the actions for this URL */
- get_url_actions(csp, csp->http);
+ 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);
- 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)
+ fwd = forward_url(csp, csp->http);
+ if (!connection_destination_matches(&csp->server_connection, csp->http, fwd))
{
- /* XXX: Send error message */
- return err;
+ 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;
}
- /* XXX: Work around crash */
- csp->error_message = NULL;
-
- /* XXX: Why do this here? */
- csp->http->ssl = 1;
+ 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);
- err = sed_https(csp);
- if (JB_ERR_OK != err)
+ if (send_https_request(csp))
{
- 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;
+ /*
+ * 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;
}
-
- log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
- log_applied_actions(csp->action);
- log_error(LOG_LEVEL_GPC, "https://%s%s", csp->http->hostport,
- csp->http->path);
-
- return err;
-
+ csp->server_connection.requests_sent_total++;
+ handle_established_connection(csp);
+ freez(csp->receive_buffer);
}
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
#endif
for (;;)
{
#ifndef HAVE_POLL
-#ifdef __OS2__
- /*
- * FD_ZERO here seems to point to an errant macro which crashes.
- * So do this by hand for now...
- */
- memset(&rfds,0x00,sizeof(fd_set));
-#else
FD_ZERO(&rfds);
-#endif
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
if (!watch_client_socket)
{
{
log_error(LOG_LEVEL_CONNECT,
"Done reading from server. Content length: %llu as expected. "
- "Bytes most recently read: %d.",
+ "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: %d.",
+ "Actual content length: %llu. Bytes most recently read: %ld.",
csp->expected_content_length, byte_count, len);
}
len = 0;
#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;
}
csp->expected_client_content_length -= (unsigned)len;
log_error(LOG_LEVEL_CONNECT,
"Expected client content length set to %llu "
- "after reading %d bytes.",
+ "after reading %ld bytes.",
csp->expected_client_content_length, len);
if (csp->expected_client_content_length == 0)
{
*/
if (server_use_ssl(csp))
{
- len = ssl_recv_data(&(csp->mbedtls_server_attr.ssl),
+ len = ssl_recv_data(&(csp->ssl_server_attr),
(unsigned char *)csp->receive_buffer, csp->receive_buffer_size);
}
else
*/
if (client_use_ssl(csp))
{
- if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)hdr, strlen(hdr)) < 0)
- || (ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ 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),
(const unsigned char *) ((p != NULL) ? p : csp->iob->cur),
- csp->content_length) < 0))
+ csp->content_length, get_write_delay(csp)) < 0))
{
log_error(LOG_LEVEL_ERROR, "write modified content to "
"client over TLS/SSL failed");
*/
if (client_use_ssl(csp))
{
- if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)hdr, hdrlen) < 0)
- || ((flushed = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl),
+ 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),
csp->iob)) < 0)
- || (ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)csp->receive_buffer, (size_t)len) < 0))
+ || (ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)csp->receive_buffer, (size_t)len,
+ get_write_delay(csp)) < 0))
{
log_error(LOG_LEVEL_CONNECT,
"Flush header and buffers to client failed");
*/
if (client_use_ssl(csp))
{
- ret = ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)csp->receive_buffer, (size_t)len);
+ ret = ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)csp->receive_buffer, (size_t)len,
+ get_write_delay(csp));
if (ret < 0)
{
log_error(LOG_LEVEL_ERROR,
*/
if (client_use_ssl(csp))
{
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
(const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
- strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), get_write_delay(csp));
}
else
#endif /* def FEATURE_HTTPS_INSPECTION */
*/
log_error(LOG_LEVEL_CONNECT,
"Continuing buffering server headers from socket %d. "
- "Bytes most recently read: %d.", csp->cfd, len);
+ "Bytes most recently read: %ld.", csp->cfd, len);
continue;
}
}
*/
if (client_use_ssl(csp))
{
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
(const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
- strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ strlen(INVALID_SERVER_HEADERS_RESPONSE),
+ get_write_delay(csp));
}
else
#endif /* def FEATURE_HTTPS_INSPECTION */
*/
if (client_use_ssl(csp))
{
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
(const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
- strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ strlen(INVALID_SERVER_HEADERS_RESPONSE),
+ get_write_delay(csp));
}
else
#endif
#ifdef FEATURE_HTTPS_INSPECTION
if (client_use_ssl(csp))
{
- if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
- (const unsigned char *)hdr, strlen(hdr)) < 0)
- || (len = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl),
- csp->iob) < 0))
+ 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))
{
log_error(LOG_LEVEL_CONNECT, "Write header to client failed");
*/
if (client_use_ssl(csp))
{
- ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
(const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
- strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ strlen(INVALID_SERVER_HEADERS_RESPONSE),
+ get_write_delay(csp));
}
else
#endif /* def FEATURE_HTTPS_INSPECTION */
#endif
return; /* huh? we should never get here */
}
-#ifdef FEATURE_HTTPS_INSPECTION
- close_client_and_server_ssl_connections(csp);
-#endif
+
if (csp->content_length == 0)
{
/*
/* decide how to route the HTTP request */
fwd = forward_url(csp, http);
- if (NULL == fwd)
- {
- log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!? This can't happen!");
- /* Never get here - LOG_LEVEL_FATAL causes program exit */
- return;
- }
#ifdef FEATURE_HTTPS_INSPECTION
/*
* Setting flags to use old solution with SSL tunnel and to disable
- * certificates verification.
+ * certificate verification.
*/
- if (csp->http->ssl && !(csp->action->flags & ACTION_HTTPS_INSPECTION))
+ if (csp->http->ssl && !(csp->action->flags & ACTION_HTTPS_INSPECTION)
+ && !cgi_page_requested(csp->http->host))
{
use_ssl_tunnel = 1;
}
- if (http->ssl && csp->action->flags & ACTION_IGNORE_CERTIFICATE_ERRORS)
+ if (http->ssl && (csp->action->flags & ACTION_IGNORE_CERTIFICATE_ERRORS))
{
csp->dont_verify_certificate = 1;
}
}
#endif
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Log the request unless we're https inspecting
+ * in which case we don't have the path yet and
+ * will log the request later.
+ */
+ if (!client_use_ssl(csp))
+#endif
+ {
+ log_error(LOG_LEVEL_REQUEST, "%s%s", http->hostport, http->path);
+ }
+
if (http->ssl && connect_port_is_forbidden(csp))
{
const char *acceptable_connect_ports =
}
log_applied_actions(csp->action);
-#ifdef FEATURE_HTTPS_INSPECTION
- /*
- * Log the request unless we're https inspecting
- * in which case we don't have the path yet and
- * will log the request later.
- */
- if (!client_use_ssl(csp))
-#endif
- {
- log_error(LOG_LEVEL_GPC, "%s%s", http->hostport, http->path);
- }
if (fwd->forward_host)
{
log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s",
if (csp->server_connection.sfd != JB_INVALID_SOCKET)
{
#ifdef FEATURE_CONNECTION_SHARING
- if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
+ if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING
+#ifdef FEATURE_HTTPS_INSPECTION
+ && !server_use_ssl(csp)
+#endif
+ )
{
remember_connection(&csp->server_connection);
}
}
#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
#ifdef FEATURE_HTTPS_INSPECTION
- if (http->ssl && !use_ssl_tunnel)
+ if (client_use_ssl(csp) && !use_ssl_tunnel)
{
int ret;
/*
- * Creating an SSL proxy. If forwarding is disabled, we must send
- * CSUCCEED message to client. Then TLS/SSL connection with client
- * is created.
+ * 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 (fwd->forward_host == NULL)
+ if (write_socket_delayed(csp->cfd, CSUCCEED,
+ strlen(CSUCCEED), get_write_delay(csp)) != 0)
{
- /*
- * 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;
- }
+ 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? */
+ "Failed to open a secure connection with the client");
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;
}
/*
* Yes. The client got the crunch response and we're done here.
*/
- close_client_ssl_connection(csp);
return;
}
}
}
/*
- * Test if connection with destination server was established
- * successfully by parent proxy. Then we can send response to
- * the client and continue or stop.
+ * Test if the connection to the destination server was
+ * established successfully by the parent proxy.
*/
if (!tunnel_established_successfully(server_response, (unsigned int)len))
{
- log_error(LOG_LEVEL_ERROR, "Forwarder hasn't established "
- "connection with destination server.");
-
- write_socket(csp->cfd, server_response, (size_t)len);
- mark_server_socket_tainted(csp);
- close_client_ssl_connection(csp);
- return;
- }
-
- /*
- * Parent proxy has established connection with destination server.
- * Now we must create TLS/SSL connection with parent proxy.
- */
- 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 the 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))
- {
+ log_error(LOG_LEVEL_ERROR,
+ "The forwarder %s failed to establish a connection with %s",
+ fwd->forward_host, http->host);
rsp = error_response(csp, "connect-failed");
if (rsp)
{
send_crunch_response(csp, rsp);
}
+ mark_server_socket_tainted(csp);
+ close_client_ssl_connection(csp);
return;
}
+ } /* -END- if (fwd->forward_host != NULL) */
- /*
- * TLS/SSL connection with parent proxy is established, we can
- * inform client about success.
- */
- ret = write_socket(csp->cfd, server_response, (size_t)len);
- if (ret != 0)
+ /*
+ * We can now create the TLS/SSL connection with the destination server.
+ */
+ int ret = create_server_ssl_connection(csp);
+ if (ret != 0)
+ {
+ if (csp->server_cert_verification_result != SSL_CERT_VALID &&
+ csp->server_cert_verification_result != SSL_CERT_NOT_VERIFIED)
{
- log_error(LOG_LEVEL_ERROR,
- "Sending parent proxy response to client failed");
- mark_server_socket_tainted(csp);
- close_client_ssl_connection(csp);
+ /*
+ * 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;
}
- }/* -END- if (fwd->forward_host != NULL) */
- else
- {
- /*
- * Parent proxy is not used, we can just create TLS/SSL connection
- * with destination server
- */
- int ret = create_server_ssl_connection(csp);
- if (ret != 0)
+ if (csp->server_cert_verification_result == SSL_CERT_NOT_VERIFIED
+ || csp->server_cert_verification_result == SSL_CERT_VALID)
{
- if (csp->server_cert_verification_result != SSL_CERT_VALID &&
- csp->server_cert_verification_result != SSL_CERT_NOT_VERIFIED)
- {
- /*
- * 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)
{
- /*
- * 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);
- }
- close_client_and_server_ssl_connections(csp);
- return;
+ send_crunch_response(csp, rsp);
}
+ close_client_and_server_ssl_connections(csp);
+ return;
}
}
}/* -END- if (http->ssl) */
else
{
/*
- * If server certificate is invalid, we must inform client and then
- * close connection with client.
+ * If server certificate has been verified and is invalid,
+ * we must inform the client and then close the connection
+ * with client and server.
*/
- if (csp->server_cert_verification_result != SSL_CERT_VALID)
+ if (csp->server_cert_verification_result != SSL_CERT_VALID &&
+ csp->server_cert_verification_result != SSL_CERT_NOT_VERIFIED)
{
ssl_send_certificate_error(csp);
close_client_and_server_ssl_connections(csp);
if (strcmp(fuzz_input_file, "-") == 0)
{
- /* XXX: Doesn'T work yet. */
+ /* XXX: Doesn't work yet. */
csp->server_connection.sfd = 0;
}
else
freez(csp->error_message);
free_http_request(csp->http);
destroy_list(csp->headers);
+#ifdef FEATURE_HTTPS_INSPECTION
+ destroy_list(csp->https_headers);
+#endif
destroy_list(csp->tags);
#ifdef FEATURE_CLIENT_TAGS
destroy_list(csp->client_tags);
assert(bytes_to_shift > 0);
assert(data_length > 0);
- log_error(LOG_LEVEL_CONNECT, "Shifting %d pipelined bytes by %d bytes",
+ log_error(LOG_LEVEL_CONNECT, "Shifting %lu pipelined bytes by %ld bytes",
data_length, bytes_to_shift);
memmove(csp->client_iob->buf, csp->client_iob->cur, data_length);
csp->client_iob->cur = csp->client_iob->buf;
{
unsigned int latency;
- chat(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (continue_chatting && client_use_ssl(csp))
+ {
+ continue_https_chat(csp);
+ }
+ else
+#endif
+ {
+ chat(csp);
+ }
/*
* If the request has been crunched,
{
log_error(LOG_LEVEL_CONNECT,
"Closing server socket %d connected to %s. "
- "Keep-alive %u. Tainted: %u. Socket alive %u. Timeout: %u.",
+ "Keep-alive: %u. Tainted: %u. Socket alive: %u. Timeout: %u.",
csp->server_connection.sfd, csp->server_connection.host,
0 != (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE),
0 != (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED),
forget_connection(csp->server_connection.sfd);
}
#endif /* def FEATURE_CONNECTION_SHARING */
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_server_ssl_connection(csp);
+#endif
close_socket(csp->server_connection.sfd);
mark_connection_closed(&csp->server_connection);
}
continue_chatting = 0;
config_file_change_detected = 1;
}
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (continue_chatting && client_use_ssl(csp) &&
+ csp->ssl_with_client_is_opened == 0)
+ {
+ continue_chatting = 0;
+ log_error(LOG_LEVEL_CONNECT, "Client socket %d is no longer usable. "
+ "The TLS session has been terminated.", csp->cfd);
+ }
+#endif
if (continue_chatting)
{
&& socket_is_still_alive(csp->cfd))
{
log_error(LOG_LEVEL_CONNECT,
- "Client request %u arrived in time on socket %d.",
- csp->requests_received_total+1, csp->cfd);
+ "Data arrived in time on client socket %d. Requests so far: %u",
+ csp->cfd, csp->requests_received_total);
prepare_csp_for_next_request(csp);
}
else
#ifdef FEATURE_CONNECTION_SHARING
if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
&& (csp->server_connection.sfd != JB_INVALID_SOCKET)
- && (socket_is_still_alive(csp->server_connection.sfd)))
+ && (socket_is_still_alive(csp->server_connection.sfd))
+#ifdef FEATURE_HTTPS_INSPECTION
+ && !server_use_ssl(csp)
+#endif
+ )
{
time_t time_open = time(NULL) - csp->server_connection.timestamp;
chat(csp);
#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+ if (csp->cfd != JB_INVALID_SOCKET)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Closing client socket %d. "
+ "Keep-alive: %u. Socket alive: %u. Data available: %u. "
+ "Configuration file change detected: %u. Requests received: %u.",
+ csp->cfd, 0 != (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE),
+ socket_is_still_alive(csp->cfd), data_is_available(csp->cfd, 0),
+ config_file_change_detected, csp->requests_received_total);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_ssl_connection(csp);
+#endif
+ drain_and_close_socket(csp->cfd);
+ }
+
if (csp->server_connection.sfd != JB_INVALID_SOCKET)
{
#ifdef FEATURE_CONNECTION_SHARING
forget_connection(csp->server_connection.sfd);
}
#endif /* def FEATURE_CONNECTION_SHARING */
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_server_ssl_connection(csp);
+#endif /* def FEATURE_HTTPS_INSPECTION */
+
close_socket(csp->server_connection.sfd);
}
mark_connection_closed(&csp->server_connection);
#endif
- if (csp->cfd != JB_INVALID_SOCKET)
- {
- log_error(LOG_LEVEL_CONNECT, "Closing client socket %d. "
- "Keep-alive: %u. Socket alive: %u. Data available: %u. "
- "Configuration file change detected: %u. Requests received: %u.",
- csp->cfd, 0 != (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE),
- socket_is_still_alive(csp->cfd), data_is_available(csp->cfd, 0),
- config_file_change_detected, csp->requests_received_total);
- drain_and_close_socket(csp->cfd);
- }
-
free_csp_resources(csp);
csp->flags &= ~CSP_FLAG_ACTIVE;
#ifdef FEATURE_HTTPS_INSPECTION
privoxy_mutex_init(&certificate_mutex);
- privoxy_mutex_init(&rng_mutex);
+ privoxy_mutex_init(&ssl_init_mutex);
#endif
privoxy_mutex_init(&log_mutex);
#ifdef FEATURE_CLIENT_TAGS
privoxy_mutex_init(&client_tags_mutex);
#endif
+#ifdef FEATURE_EXTENDED_STATISTICS
+ privoxy_mutex_init(&filter_statistics_mutex);
+ privoxy_mutex_init(&block_statistics_mutex);
+#endif
/*
* XXX: The assumptions below are a bit naive
* are handled when and where they occur without relying
* on a signal.
*/
-#if !defined(_WIN32) && !defined(__OS2__)
+#if !defined(_WIN32)
{
int idx;
const int catched_signals[] = { SIGTERM, SIGINT, SIGHUP };
}
#endif
- chdir("/");
+ if (chdir("/") != 0)
+ {
+ log_error(LOG_LEVEL_FATAL, "Failed to cd into '/': %E");
+ }
} /* -END- if (daemon_mode) */
for (;;)
#endif
{
-#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(__OS2__)
+#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__)
while (waitpid(-1, NULL, WNOHANG) > 0)
{
/* zombie children */
csp = &csp_list->csp;
log_error(LOG_LEVEL_CONNECT,
- "Waiting for the next client connection. Currently active threads: %d",
+ "Waiting for the next client connection. Currently active threads: %u",
active_threads);
/*
#define SELECTED_ONE_OPTION
{
pthread_t the_thread;
+ int ret;
- errno = pthread_create(&the_thread, &attrs,
+ ret = pthread_create(&the_thread, &attrs,
(void * (*)(void *))serve, csp);
- child_id = errno ? -1 : 0;
+ child_id = ret ? -1 : 0;
}
#endif
csp);
#endif
-#if defined(__OS2__) && !defined(SELECTED_ONE_OPTION)
-#define SELECTED_ONE_OPTION
- child_id = _beginthread(
- (void(* _Optlink)(void*))serve,
- NULL,
- 64 * 1024,
- csp);
-#endif
-
#if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
{
* XXX: If you assume ...
*/
log_error(LOG_LEVEL_ERROR,
- "Unable to take any additional connections: %E. Active threads: %d",
+ "Unable to take any additional connections: %E. Active threads: %u",
active_threads);
write_socket_delayed(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE,
strlen(TOO_MANY_CONNECTIONS_RESPONSE), get_write_delay(csp));
/* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */
-#ifdef FEATURE_HTTPS_INSPECTION
- /* Clean up. Aim: free all memory (no leaks) */
- if (rng_seeded == 1)
- {
- mbedtls_ctr_drbg_free(&ctr_drbg);
- mbedtls_entropy_free(&entropy);
- }
-#endif
-
#ifdef FEATURE_GRACEFUL_TERMINATION
- log_error(LOG_LEVEL_ERROR, "Graceful termination requested");
+ log_error(LOG_LEVEL_INFO, "Graceful termination requested.");
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)
/* Cleanup - remove taskbar icon etc. */
TermLogWindow();