* 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
"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 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;
*********************************************************************/
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."
{
/* 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));
*
* 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;
}
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))
* 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;
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 %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)
+ {
+ 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 %llu more bytes",
+ csp->expected_client_content_length);
+ return 1;
+ }
+ log_error(LOG_LEVEL_CONNECT, "The last %llu 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 %lu will be exceeded "
+ "(content length %lluu)", 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;
}
- else if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
+
+ 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;
+ }
+ }
+
+ 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;
+}
- return write_failure;
+#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 %lu 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);
+ }
+
+ if (to_read != 0)
+ {
+ 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 %llu 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 :
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->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 POST data",
+ 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)
}
}
- 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)
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->ssl_server_attr),
csp->client_iob)) < 0))
csp->http->hostport);
return 1;
}
- if (flushed != 0)
+ if (flushed != 0 || csp->expected_client_content_length != 0)
{
if (csp->expected_client_content_length != 0)
{
}
+/*********************************************************************
+ *
+ * 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
err = receive_encrypted_request(csp);
if (err != JB_ERR_OK)
{
- if (csp->client_iob->cur == NULL)
+ if (csp->client_iob->cur == NULL ||
+ csp->client_iob->cur == csp->client_iob->eod)
{
/*
* We did not receive any data, most likely because the
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,
if (JB_ERR_OK != process_encrypted_request(csp))
{
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
return;
}
#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;
}
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))
+ || ((len = ssl_flush_socket(&(csp->ssl_client_attr),
+ csp->iob)) < 0))
{
log_error(LOG_LEVEL_CONNECT, "Write header to client failed");
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 /* 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;
/*
}
#endif
- chdir("/");
+ if (chdir("/") != 0)
+ {
+ log_error(LOG_LEVEL_FATAL, "Failed to cd into '/': %E");
+ }
} /* -END- if (daemon_mode) */
/* 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.");
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();