X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=f4dc57dc0b84991ba7e1b17ed5c622a7edee5627;hp=69c01808d841bdadfbbc825a9b8a8b0e8a6e1b1c;hb=b8c75c0cb4d606a624276a71732439214d028727;hpb=55fc826b8acc4cc6f300c7ec1fa93952eb253824 diff --git a/jcc.c b/jcc.c index 69c01808..f4dc57dc 100644 --- a/jcc.c +++ b/jcc.c @@ -5,7 +5,7 @@ * 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-2022 the * Privoxy team. https://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -62,10 +62,8 @@ #else /* ifndef _WIN32 */ -# if !defined (__OS2__) # include # include -# endif /* ndef __OS2__ */ # include # include # include @@ -86,14 +84,6 @@ # include /* declarations for threads and stuff. */ # endif -# if defined(__EMX__) || defined(__OS2__) -# include /* OS/2/EMX needs a little help with select */ -# endif -# ifdef __OS2__ -#define INCL_DOS -# include -# endif - #ifdef HAVE_POLL #ifdef __GLIBC__ #include @@ -137,13 +127,17 @@ struct file_list files[1]; #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 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); @@ -156,6 +150,7 @@ static jb_err receive_client_request(struct client_state *csp); 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) @@ -176,10 +171,6 @@ static int32 server_thread(void *data); #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); @@ -195,7 +186,7 @@ privoxy_mutex_t connection_reuse_mutex; #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 @@ -204,6 +195,13 @@ privoxy_mutex_t external_filter_mutex; #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_reason_statistics_mutex; +#endif #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) privoxy_mutex_t resolver_mutex; @@ -237,64 +235,64 @@ static const char CHEADER[] = "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 *); @@ -342,7 +340,7 @@ static const struct cruncher crunchers_light[] = { * * here? */ -#if !defined(_WIN32) && !defined(__OS2__) +#if !defined(_WIN32) /********************************************************************* * * Function : sig_handler @@ -385,7 +383,8 @@ static void sig_handler(int the_signal) * 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; @@ -485,7 +484,7 @@ static int client_protocol_is_unsupported(struct client_state *csp, char *req) #ifdef FEATURE_HTTPS_INSPECTION if (client_use_ssl(csp)) { - ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *)response, strlen(response), get_write_delay(csp)); } @@ -568,8 +567,6 @@ static int client_has_unsupported_expectations(const struct client_state *csp) *********************************************************************/ 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." @@ -587,10 +584,8 @@ static jb_err get_request_destination_elsewhere(struct client_state *csp, struct } 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; } @@ -598,15 +593,12 @@ static jb_err get_request_destination_elsewhere(struct client_state *csp, struct { /* 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)); @@ -854,7 +846,7 @@ static void send_crunch_response(struct client_state *csp, struct http_response { 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); } @@ -862,24 +854,24 @@ static void send_crunch_response(struct client_state *csp, struct http_response #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_delayed(&(csp->mbedtls_client_attr.ssl), + 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->mbedtls_client_attr.ssl), + || (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 @@ -960,6 +952,11 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun #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 */ @@ -984,8 +981,8 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun * * 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. @@ -1010,12 +1007,35 @@ static void build_request_line(struct client_state *csp, const struct forward_sp * 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->forward_host && fwd->type != FORWARD_WEBSERVER) + 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 { @@ -1060,6 +1080,16 @@ static jb_err change_request_destination(struct client_state *csp) 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; } @@ -1158,10 +1188,10 @@ static void wait_for_alive_connections(void) * 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); @@ -1187,6 +1217,22 @@ void save_connection_destination(jb_socket sfd, 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) { @@ -1198,6 +1244,7 @@ void save_connection_destination(jb_socket sfd, } server_connection->forward_port = fwd->forward_port; } +#endif /* FEATURE_CONNECTION_KEEP_ALIVE */ /********************************************************************* @@ -1298,7 +1345,6 @@ static void verify_request_length(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "Complete client request received."); } } -#endif /* FEATURE_CONNECTION_KEEP_ALIVE */ /********************************************************************* @@ -1492,6 +1538,12 @@ static enum chunk_status chunked_body_is_complete(struct iob *iob, size_t *lengt /* 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)) { @@ -1542,7 +1594,8 @@ static jb_err receive_chunked_client_request_body(struct client_state *csp) 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)) @@ -1559,7 +1612,7 @@ static jb_err receive_chunked_client_request_body(struct client_state *csp) 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; @@ -1772,6 +1825,19 @@ static jb_err receive_client_request(struct client_state *csp) 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); @@ -1794,7 +1860,8 @@ static jb_err receive_client_request(struct client_state *csp) 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; } @@ -1850,7 +1917,7 @@ static jb_err receive_client_request(struct client_state *csp) * 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; @@ -1919,6 +1986,7 @@ static jb_err parse_client_request(struct client_state *csp) /* Assume persistence until further notice */ csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; } +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ if (csp->http->ssl == 0) { @@ -1939,11 +2007,12 @@ static jb_err parse_client_request(struct client_state *csp) } 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) @@ -1969,7 +2038,7 @@ static jb_err parse_client_request(struct client_state *csp) 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); @@ -1987,6 +2056,143 @@ static jb_err parse_client_request(struct client_state *csp) } +/********************************************************************* + * + * 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 %lu more bytes", + to_read); + return 1; + } + log_error(LOG_LEVEL_CONNECT, + "The last %d bytes of the request body have been read", len); + 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 %llu)", csp->ip_addr_str, csp->config->buffer_limit, + csp->expected_client_content_length); + return FALSE; + } + return TRUE; +} + + /********************************************************************* * * Function : send_http_request @@ -2004,6 +2210,33 @@ 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"); + freez(to_send); + return 1; + } + csp->expected_client_content_length = 0; + } hdr = list_to_text(csp->headers); if (hdr == NULL) @@ -2024,26 +2257,109 @@ static int send_http_request(struct client_state *csp) { log_error(LOG_LEVEL_CONNECT, "Failed sending request headers to: %s: %E", csp->http->hostport); + if (filter_client_body) + { + freez(to_send); + } + 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; +} + + +#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, + "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, + "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)) + { + return 1; + } + assert(to_read >= len); } - return write_failure; + if (to_read != 0) + { + 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 %llu bytes of the encrypted 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 : @@ -2056,8 +2372,9 @@ static int receive_and_send_encrypted_post_data(struct client_state *csp) { int content_length_known = csp->expected_client_content_length != 0; - while (is_ssl_pending(&(csp->mbedtls_client_attr.ssl)) - || (content_length_known && csp->expected_client_content_length != 0)) + while ((content_length_known && csp->expected_client_content_length != 0) || + (is_ssl_pending(&(csp->ssl_client_attr)) || + data_is_available(csp->cfd, csp->config->socket_timeout))) { unsigned char buf[BUFFER_SIZE]; int len; @@ -2068,9 +2385,9 @@ static int receive_and_send_encrypted_post_data(struct client_state *csp) 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.", + "Prepared to read up to %d bytes of encrypted 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) { @@ -2081,9 +2398,9 @@ static int receive_and_send_encrypted_post_data(struct client_state *csp) /* 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; @@ -2102,7 +2419,7 @@ static int receive_and_send_encrypted_post_data(struct client_state *csp) } } - log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted POST data"); + log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted request body"); return 0; @@ -2127,6 +2444,32 @@ static int send_https_request(struct client_state *csp) 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) @@ -2140,7 +2483,7 @@ static int send_https_request(struct client_state *csp) * 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); @@ -2153,29 +2496,41 @@ static int send_https_request(struct client_state *csp) 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)) @@ -2187,7 +2542,7 @@ static int send_https_request(struct client_state *csp) else { log_error(LOG_LEVEL_CONNECT, - "Flushed %d bytes of request body", flushed); + "Flushed %ld bytes of request body", flushed); } } @@ -2200,9 +2555,10 @@ static int send_https_request(struct client_state *csp) /********************************************************************* * - * 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...) @@ -2211,7 +2567,7 @@ static int send_https_request(struct client_state *csp) * 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; @@ -2219,16 +2575,22 @@ static jb_err receive_encrypted_request(struct client_state *csp) do { - log_error(LOG_LEVEL_HEADER, "Reading encrypted headers"); - if (!is_ssl_pending(&(csp->mbedtls_client_attr.ssl)) && + 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)) { 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; @@ -2248,9 +2610,111 @@ static jb_err receive_encrypted_request(struct client_state *csp) /********************************************************************* * - * Function : process_encrypted_request + * 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_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...) @@ -2259,7 +2723,7 @@ static jb_err receive_encrypted_request(struct client_state *csp) * 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; @@ -2268,13 +2732,30 @@ static jb_err process_encrypted_request(struct client_state *csp) struct list header_list; struct list *headers = &header_list; - err = receive_encrypted_request(csp); + 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_headers(csp); if (err != JB_ERR_OK) { + if (csp->client_iob->cur == NULL || + csp->client_iob->cur == csp->client_iob->eod) + { + /* + * We did not receive any data, most likely because the + * client is done. Don't log this as a parse failure. + */ + return JB_ERR_PARSE; + } /* XXX: Also used for JB_ERR_MEMORY */ log_error(LOG_LEVEL_ERROR, "Failed to receive encrypted request: %s", jb_err_to_string(err)); - ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp)); return err; } @@ -2284,7 +2765,7 @@ static jb_err process_encrypted_request(struct client_state *csp) if (request_line == NULL) { log_error(LOG_LEVEL_ERROR, "Failed to get the encrypted request line"); - ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp)); return JB_ERR_PARSE; } @@ -2317,7 +2798,7 @@ static jb_err process_encrypted_request(struct client_state *csp) freez(request_line); if (JB_ERR_OK != err) { - ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + 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); @@ -2352,21 +2833,34 @@ static jb_err process_encrypted_request(struct client_state *csp) */ log_error(LOG_LEVEL_ERROR, "Failed to get the encrypted request destination"); - ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + 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; } -#ifndef FEATURE_EXTENDED_HOST_PATTERNS /* 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 URL */ + /* + * 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); } @@ -2390,7 +2884,7 @@ static jb_err process_encrypted_request(struct client_state *csp) err = sed_https(csp); if (JB_ERR_OK != err) { - ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + 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); @@ -2399,8 +2893,23 @@ static jb_err process_encrypted_request(struct client_state *csp) return JB_ERR_PARSE; } - log_error(LOG_LEVEL_HEADER, "Encrypted request processed"); - log_applied_actions(csp->action); + 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 headers processed"); log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport, csp->http->path); @@ -2410,30 +2919,134 @@ static jb_err process_encrypted_request(struct client_state *csp) /********************************************************************* * - * Function : cgi_page_requested + * 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 : Checks if a request is for an internal CGI page. + * 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 : host = The host requested by the client. + * 1 : csp = Current client state (buffers, headers, etc...) * - * Returns : 1 if a CGI page has been requested, 0 otherwise + * Returns : Nothing. * *********************************************************************/ -static int cgi_page_requested(const char *host) +static void continue_https_chat(struct client_state *csp) { - 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 "."))) + const struct forward_spec *fwd; + + if (JB_ERR_OK != process_encrypted_request_headers(csp)) { - return 1; + csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + return; } - return 0; +#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++; + + /* + * We have an encrypted request. Check if one of the crunchers wants it. + */ + if (crunch_response_triggered(csp, crunchers_all)) + { + /* + * Yes. The client got the crunch response and we're done here. + */ + return; + } + if (csp->ssl_with_server_is_opened == 0) + { + log_error(LOG_LEVEL_CONNECT, + "Dropping the client connection on socket %d. " + "The server connection has not been established yet.", + csp->cfd); + csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + return; + } + assert(csp->server_connection.sfd != JB_INVALID_SOCKET); + + fwd = forward_url(csp, csp->http); + if (!connection_destination_matches(&csp->server_connection, csp->http, fwd)) + { + log_error(LOG_LEVEL_CONNECT, + "Dropping the client connection on socket %d with " + "server socket %d connected to %s. The forwarder has changed.", + csp->cfd, csp->server_connection.sfd, csp->server_connection.host); + csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + return; + } + log_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->server_connection.requests_sent_total); + + if (send_https_request(csp)) + { + /* + * Most likely the server connection timed out. We can't easily + * create a new one so simply drop the client connection without a + * error response to let the client retry. + */ + log_error(LOG_LEVEL_CONNECT, + "Dropping client connection on socket %d. " + "Forwarding the encrypted client request failed.", + csp->cfd); + return; + } + csp->server_connection.request_sent = time(NULL); + csp->server_connection.requests_sent_total++; + handle_established_connection(csp); + freez(csp->receive_buffer); +} +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ #endif @@ -2442,7 +3055,8 @@ static int cgi_page_requested(const char *host) * 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...) @@ -2518,16 +3132,30 @@ static void handle_established_connection(struct client_state *csp) 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 -#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) { @@ -2566,14 +3194,14 @@ static void handle_established_connection(struct client_state *csp) { 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; @@ -2612,7 +3240,7 @@ static void handle_established_connection(struct client_state *csp) 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", @@ -2666,7 +3294,13 @@ static void handle_established_connection(struct client_state *csp) 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)) @@ -2714,6 +3348,37 @@ static void handle_established_connection(struct client_state *csp) #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; } @@ -2736,7 +3401,7 @@ static void handle_established_connection(struct client_state *csp) 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) { @@ -2768,6 +3433,9 @@ static void handle_established_connection(struct client_state *csp) 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 @@ -2799,7 +3467,7 @@ static void handle_established_connection(struct client_state *csp) */ 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 @@ -2965,15 +3633,15 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - if ((ssl_send_data_delayed(&(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->mbedtls_client_attr.ssl), + || (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)) { - log_error(LOG_LEVEL_ERROR, "write modified content to " - "client over TLS/SSL failed"); + log_error(LOG_LEVEL_ERROR, + "Failed to send the modified content to the client over TLS"); freez(hdr); freez(p); mark_server_socket_tainted(csp); @@ -3004,8 +3672,8 @@ static void handle_established_connection(struct client_state *csp) } /* - * 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); @@ -3068,11 +3736,11 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - if ((ssl_send_data_delayed(&(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->mbedtls_client_attr.ssl), + || ((flushed = ssl_flush_socket(&(csp->ssl_client_attr), csp->iob)) < 0) - || (ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + || (ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *)csp->receive_buffer, (size_t)len, get_write_delay(csp)) < 0)) { @@ -3119,7 +3787,7 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - ret = ssl_send_data_delayed(&(csp->mbedtls_client_attr.ssl), + 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) @@ -3185,7 +3853,7 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - ssl_send_data_delayed(&(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), get_write_delay(csp)); } @@ -3210,7 +3878,7 @@ static void handle_established_connection(struct client_state *csp) */ 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; } } @@ -3279,7 +3947,7 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - ssl_send_data_delayed(&(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), get_write_delay(csp)); @@ -3298,6 +3966,18 @@ static void handle_established_connection(struct client_state *csp) 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 @@ -3312,7 +3992,7 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - ssl_send_data_delayed(&(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), get_write_delay(csp)); @@ -3382,11 +4062,11 @@ static void handle_established_connection(struct client_state *csp) #ifdef FEATURE_HTTPS_INSPECTION if (client_use_ssl(csp)) { - if ((ssl_send_data_delayed(&(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) - || (len = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl), - csp->iob) < 0)) + || ((len = ssl_flush_socket(&(csp->ssl_client_attr), + csp->iob)) < 0)) { log_error(LOG_LEVEL_CONNECT, "Write header to client failed"); @@ -3444,7 +4124,7 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - ssl_send_data_delayed(&(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), get_write_delay(csp)); @@ -3470,9 +4150,7 @@ static void handle_established_connection(struct client_state *csp) #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) { /* @@ -3550,17 +4228,15 @@ static void chat(struct client_state *csp) { return; } - if (parse_client_request(csp) != JB_ERR_OK) - { - return; - } - /* decide how to route the HTTP request */ - fwd = forward_url(csp, http); - if (NULL == fwd) +#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) { - log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!? This can't happen!"); - /* Never get here - LOG_LEVEL_FATAL causes program exit */ return; } @@ -3575,7 +4251,7 @@ static void chat(struct client_state *csp) 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; } @@ -3666,10 +4342,6 @@ static void chat(struct client_state *csp) #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 @@ -3688,7 +4360,60 @@ static void chat(struct client_state *csp) 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 + 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", @@ -3716,7 +4441,11 @@ static void chat(struct client_state *csp) 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); } @@ -3732,53 +4461,7 @@ static void chat(struct client_state *csp) mark_connection_closed(&csp->server_connection); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ -#ifdef FEATURE_HTTPS_INSPECTION - if (http->ssl && !use_ssl_tunnel) - { - int ret; - /* - * Creating 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)) - { - log_error(LOG_LEVEL_ERROR, "Failed to parse encrypted request."); - close_client_ssl_connection(csp); - return; - } - /* - * We have an encrypted request. Check if one of the crunchers now - * wants it (for example because the previously invisible path was - * required to match). - */ - if (crunch_response_triggered(csp, crunchers_all)) - { - /* - * Yes. The client got the crunch response and we're done here. - */ - close_client_ssl_connection(csp); - return; - } - } -#endif /* * Connecting to destination server */ @@ -3998,10 +4681,12 @@ static void chat(struct client_state *csp) 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); @@ -4053,7 +4738,7 @@ extern int fuzz_server_response(struct client_state *csp, char *fuzz_input_file) if (strcmp(fuzz_input_file, "-") == 0) { - /* XXX: Doesn'T work yet. */ + /* XXX: Doesn't work yet. */ csp->server_connection.sfd = 0; } else @@ -4118,6 +4803,9 @@ static void prepare_csp_for_next_request(struct client_state *csp) 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); @@ -4146,7 +4834,7 @@ static void prepare_csp_for_next_request(struct client_state *csp) 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; @@ -4202,7 +4890,16 @@ static void serve(struct client_state *csp) { 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, @@ -4243,7 +4940,8 @@ static void serve(struct client_state *csp) 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), @@ -4254,8 +4952,27 @@ static void serve(struct client_state *csp) 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); +#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 } } @@ -4264,6 +4981,15 @@ static void serve(struct client_state *csp) 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) { @@ -4299,8 +5025,8 @@ static void serve(struct client_state *csp) && 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 @@ -4308,7 +5034,11 @@ static void serve(struct client_state *csp) #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; @@ -4355,6 +5085,20 @@ static void serve(struct client_state *csp) 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 @@ -4363,6 +5107,11 @@ static void serve(struct client_state *csp) 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); } @@ -4370,17 +5119,6 @@ static void serve(struct client_state *csp) 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; @@ -4559,7 +5297,7 @@ static void initialize_mutexes(void) #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); @@ -4571,6 +5309,13 @@ static void initialize_mutexes(void) #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_reason_statistics_mutex); +#endif /* * XXX: The assumptions below are a bit naive @@ -4860,7 +5605,7 @@ int main(int argc, char **argv) * 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 }; @@ -4993,7 +5738,10 @@ int main(int argc, char **argv) } #endif - chdir("/"); + if (chdir("/") != 0) + { + log_error(LOG_LEVEL_FATAL, "Failed to cd into '/': %E"); + } } /* -END- if (daemon_mode) */ @@ -5145,15 +5893,18 @@ static jb_socket bind_port_helper(const char *haddr, int hport, int backlog) "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 */ @@ -5322,7 +6073,7 @@ static void listen_loop(void) 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 */ @@ -5352,7 +6103,7 @@ static void listen_loop(void) 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); /* @@ -5423,7 +6174,7 @@ static void listen_loop(void) 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, @@ -5451,14 +6202,15 @@ static void listen_loop(void) #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 -#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, @@ -5466,15 +6218,6 @@ static void listen_loop(void) 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 { @@ -5577,7 +6320,7 @@ static void listen_loop(void) * 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)); @@ -5597,18 +6340,11 @@ static void listen_loop(void) /* 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."); + + close_ports_helper(bfds); unload_current_config_file(); unload_current_actions_file(); @@ -5628,7 +6364,8 @@ static void listen_loop(void) 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(); @@ -5638,6 +6375,19 @@ static void listen_loop(void) 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();