X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=3322574fc87a7abf51bca5299545d364ba3613c8;hp=372d9df37658553f6fcbb6acc35088c2488de704;hb=c74183d1e272e806975969ffaef1e0abb442d7ef;hpb=95be250e5c7527da293cb7110622e681fd07f1d1 diff --git a/jcc.c b/jcc.c index 372d9df3..3322574f 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-2021 the + * Copyright : Written by and Copyright (C) 2001-2022 the * Privoxy team. https://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -127,6 +127,10 @@ 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 @@ -191,9 +195,12 @@ 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_statistics_mutex; +privoxy_mutex_t block_reason_statistics_mutex; #endif #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) @@ -281,6 +288,13 @@ static const char CLIENT_BODY_PARSE_ERROR_RESPONSE[] = "Connection: close\r\n\r\n" "Failed parsing or buffering the chunk-encoded client body.\n"; +static const char CLIENT_BODY_BUFFER_ERROR_RESPONSE[] = + "HTTP/1.1 400 Failed reading client body\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Failed to buffer the client body to apply content filters.\n" + "Could be caused by a socket timeout\n"; + static const char UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE[] = "HTTP/1.1 417 Expecting too much\r\n" "Content-Type: text/plain\r\n" @@ -945,6 +959,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 */ @@ -995,12 +1014,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 != 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 { @@ -1611,6 +1653,14 @@ extern int fuzz_chunked_transfer_encoding(struct client_state *csp, char *fuzz_i { log_error(LOG_LEVEL_INFO, "Chunked body is incomplete or invalid"); } + if (get_bytes_missing_from_chunked_data(csp->iob->cur, size, 0) == 0) + { + if (CHUNK_STATUS_BODY_COMPLETE != status) + { + log_error(LOG_LEVEL_ERROR, + "There's disagreement about whether or not the chunked body is complete."); + } + } return (JB_ERR_OK == remove_chunked_transfer_coding(csp->iob->cur, &size)); @@ -1790,6 +1840,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); @@ -1812,7 +1875,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; } @@ -2057,12 +2121,13 @@ static int read_http_request_body(struct client_state *csp) 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); + 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); + log_error(LOG_LEVEL_CONNECT, + "The last %d bytes of the request body have been read.", len); return 0; } @@ -2116,7 +2181,7 @@ static int update_client_headers(struct client_state *csp, size_t new_content_le /********************************************************************* * - * Function : can_filter_request_body + * Function : can_buffer_request_body * * Description : Checks if the current request body can be stored in * the client_iob without hitting buffer limit. @@ -2128,14 +2193,14 @@ static int update_client_headers(struct client_state *csp, size_t new_content_le * FALSE otherwise. * *********************************************************************/ -static int can_filter_request_body(const struct client_state *csp) +static int can_buffer_request_body(const struct client_state *csp) { if (!can_add_to_iob(csp->client_iob, csp->config->buffer_limit, csp->expected_client_content_length)) { log_error(LOG_LEVEL_INFO, "Not filtering request body from %s: buffer limit %lu will be exceeded " - "(content length %lluu)", csp->ip_addr_str, csp->config->buffer_limit, + "(content length %llu)", csp->ip_addr_str, csp->config->buffer_limit, csp->expected_client_content_length); return FALSE; } @@ -2153,39 +2218,13 @@ static int can_filter_request_body(const struct client_state *csp) * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * - * Returns : 0 on success, anything else is an error. + * Returns : 0 on success, 1 on error, 2 if the request got crunched. * *********************************************************************/ static int send_http_request(struct client_state *csp) { char *hdr; int write_failure; - const char *to_send; - size_t to_send_len; - int filter_client_body = csp->expected_client_content_length != 0 && - client_body_filters_enabled(csp->action) && can_filter_request_body(csp); - - if (filter_client_body) - { - if (read_http_request_body(csp)) - { - return 1; - } - to_send_len = csp->expected_client_content_length; - to_send = execute_client_body_filters(csp, &to_send_len); - if (to_send == NULL) - { - /* just flush client_iob */ - filter_client_body = FALSE; - } - else if (to_send_len != csp->expected_client_content_length && - update_client_headers(csp, to_send_len)) - { - log_error(LOG_LEVEL_HEADER, "Error updating client headers"); - return 1; - } - csp->expected_client_content_length = 0; - } hdr = list_to_text(csp->headers); if (hdr == NULL) @@ -2209,18 +2248,7 @@ static int send_http_request(struct client_state *csp) return 1; } - if (filter_client_body) - { - write_failure = 0 != write_socket(csp->server_connection.sfd, to_send, to_send_len); - freez(to_send); - if (write_failure) - { - log_error(LOG_LEVEL_CONNECT, "Failed sending filtered request body to: %s: %E", - csp->http->hostport); - return 1; - } - } - + /* XXX: Filtered data is not sent if there's a pipelined request? */ if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0) && (flush_iob(csp->server_connection.sfd, csp->client_iob, 0) < 0)) { @@ -2267,13 +2295,15 @@ static int read_https_request_body(struct client_state *csp) 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.", + "Buffering encrypted client body. Prepared to read up to %lu bytes.", max_bytes_to_read); len = ssl_recv_data(&(csp->ssl_client_attr), buf, (unsigned)max_bytes_to_read); if (len <= 0) { - log_error(LOG_LEVEL_CONNECT, "Failed receiving request body from %s", csp->ip_addr_str); + log_error(LOG_LEVEL_CONNECT, + "Did not receive the whole encrypted request body from %s", + csp->ip_addr_str); return 1; } if (add_to_iob(csp->client_iob, csp->config->buffer_limit, (char *)buf, len)) @@ -2286,13 +2316,13 @@ static int read_https_request_body(struct client_state *csp) if (to_read != 0) { log_error(LOG_LEVEL_CONNECT, - "Not enough request body has been read: expected %lu more bytes", + "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 request body have been read", + "The last %llu bytes of the encrypted request body have been read", csp->expected_client_content_length); return 0; } @@ -2315,8 +2345,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->ssl_client_attr)) - || (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; @@ -2327,7 +2358,7 @@ 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 request body from the client.", + "Prepared to read up to %d bytes of encrypted request body from the client.", max_bytes_to_read); len = ssl_recv_data(&(csp->ssl_client_attr), buf, (unsigned)max_bytes_to_read); @@ -2340,7 +2371,7 @@ 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 request body", + log_error(LOG_LEVEL_CONNECT, "Forwarding %d bytes of encrypted request body.", len); len = ssl_send_data(&(csp->ssl_server_attr), buf, (size_t)len); if (len == -1) @@ -2355,13 +2386,13 @@ static int receive_and_send_encrypted_post_data(struct client_state *csp) } if (csp->expected_client_content_length == 0) { - log_error(LOG_LEVEL_CONNECT, "Forwarded the last %d bytes", len); + log_error(LOG_LEVEL_CONNECT, "Forwarded the last %d bytes.", len); break; } } } - log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted request body"); + log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted request body."); return 0; @@ -2386,38 +2417,12 @@ 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) { /* FIXME Should handle error properly */ - log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header"); + log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header."); } list_remove_all(csp->https_headers); @@ -2438,18 +2443,7 @@ 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; - } - } - + /* XXX: Client body isn't sent if there's pipelined data? */ if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0) && ((flushed = ssl_flush_socket(&(csp->ssl_server_attr), csp->client_iob)) < 0)) @@ -2465,14 +2459,14 @@ static int send_https_request(struct client_state *csp) if (csp->expected_client_content_length < flushed) { log_error(LOG_LEVEL_ERROR, - "Flushed %ld bytes of request body while only expecting %llu", + "Flushed %ld bytes of request body while only expecting %llu.", flushed, csp->expected_client_content_length); csp->expected_client_content_length = 0; } else { log_error(LOG_LEVEL_CONNECT, - "Flushed %ld bytes of request body while expecting %llu", + "Flushed %ld bytes of request body while expecting %llu.", flushed, csp->expected_client_content_length); csp->expected_client_content_length -= (unsigned)flushed; if (receive_and_send_encrypted_post_data(csp)) @@ -2484,11 +2478,11 @@ static int send_https_request(struct client_state *csp) else { log_error(LOG_LEVEL_CONNECT, - "Flushed %ld bytes of request body", flushed); + "Flushed %ld bytes of request body.", flushed); } } - log_error(LOG_LEVEL_CONNECT, "Encrypted request sent"); + log_error(LOG_LEVEL_CONNECT, "Encrypted request sent."); return 0; @@ -2497,9 +2491,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...) @@ -2508,7 +2503,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; @@ -2516,7 +2511,7 @@ static jb_err receive_encrypted_request(struct client_state *csp) do { - log_error(LOG_LEVEL_HEADER, "Reading encrypted headers"); + log_error(LOG_LEVEL_HEADER, "Waiting for encrypted client headers"); if (!is_ssl_pending(&(csp->ssl_client_attr)) && !data_is_available(csp->cfd, csp->config->socket_timeout)) { @@ -2652,9 +2647,10 @@ static jb_err change_encrypted_request_destination(struct client_state *csp) /********************************************************************* * - * Function : process_encrypted_request + * Function : process_encrypted_request_headers * - * Description : Receives and parses an encrypted request. + * Description : Receives and parses the encrypted headers send + * by the client when https-inspecting. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -2663,7 +2659,7 @@ static jb_err change_encrypted_request_destination(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; @@ -2680,7 +2676,7 @@ static jb_err process_encrypted_request(struct client_state *csp) csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; } #endif - err = receive_encrypted_request(csp); + err = receive_encrypted_request_headers(csp); if (err != JB_ERR_OK) { if (csp->client_iob->cur == NULL || @@ -2775,6 +2771,8 @@ static jb_err process_encrypted_request(struct client_state *csp) "Failed to get the encrypted request destination"); ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp)); + destroy_list(headers); + return JB_ERR_PARSE; } @@ -2847,8 +2845,7 @@ 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); + log_error(LOG_LEVEL_HEADER, "Encrypted request headers processed"); log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport, csp->http->path); @@ -2914,12 +2911,18 @@ static void continue_https_chat(struct client_state *csp) { const struct forward_spec *fwd; - if (JB_ERR_OK != process_encrypted_request(csp)) + if (JB_ERR_OK != process_encrypted_request_headers(csp)) { csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; return; } +#if defined(FEATURE_STATISTICS) && defined(MUTEX_LOCKS_AVAILABLE) + privoxy_mutex_lock(&block_statistics_mutex); + number_of_requests_received++; + privoxy_mutex_unlock(&block_statistics_mutex); +#endif + csp->requests_received_total++; /* @@ -2943,6 +2946,46 @@ static void continue_https_chat(struct client_state *csp) } assert(csp->server_connection.sfd != JB_INVALID_SOCKET); + if (csp->expected_client_content_length != 0 && + (client_body_filters_enabled(csp->action) || + client_body_taggers_enabled(csp->action)) && + can_buffer_request_body(csp)) + { + int content_modified; + + if (read_https_request_body(csp)) + { + /* XXX: handle */ + return; + } + if (client_body_taggers_enabled(csp->action)) + { + execute_client_body_taggers(csp, csp->expected_client_content_length); + if (crunch_response_triggered(csp, crunchers_all)) + { + /* + * Yes. The client got the crunch response and we're done here. + */ + return; + } + } + if (client_body_filters_enabled(csp->action)) + { + size_t modified_content_length = csp->expected_client_content_length; + content_modified = execute_client_body_filters(csp, + &modified_content_length); + if ((content_modified == 1) && + (modified_content_length != csp->expected_client_content_length) && + update_client_headers(csp, modified_content_length)) + { + /* XXX: Send error response */ + log_error(LOG_LEVEL_HEADER, "Error updating client headers"); + return; + } + } + csp->expected_client_content_length = 0; + } + fwd = forward_url(csp, csp->http); if (!connection_destination_matches(&csp->server_connection, csp->http, fwd)) { @@ -2954,6 +2997,8 @@ static void continue_https_chat(struct client_state *csp) 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, @@ -2972,6 +3017,7 @@ static void continue_https_chat(struct client_state *csp) csp->cfd); return; } + csp->server_connection.request_sent = time(NULL); csp->server_connection.requests_sent_total++; handle_established_connection(csp); freez(csp->receive_buffer); @@ -2980,12 +3026,74 @@ static void continue_https_chat(struct client_state *csp) #endif +/********************************************************************* + * + * Function : send_server_headers + * + * Description : Sends the server headers to the client. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 on succes, -1 on error. + * + *********************************************************************/ +static int send_server_headers(struct client_state *csp) +{ + char *server_headers; + int ret; + + server_headers = list_to_text(csp->headers); + if (server_headers == NULL) + { + /* + * Memory is too tight to even generate the header. + * Send our static "Out-of-memory" page. + */ + log_error(LOG_LEVEL_ERROR, + "Out of memory while trying to send server headers."); + send_crunch_response(csp, cgi_error_memory()); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_INSPECTION + close_client_and_server_ssl_connections(csp); +#endif + return -1; + } +#ifdef FEATURE_HTTPS_INSPECTION + if (client_use_ssl(csp)) + { + ret = ssl_send_data_delayed(&(csp->ssl_client_attr), + (const unsigned char *)server_headers, strlen(server_headers), + get_write_delay(csp)); + } + else +#endif + { + ret = write_socket_delayed(csp->cfd, server_headers, strlen(server_headers), + get_write_delay(csp)); + } + freez(server_headers); + if (ret < 0) + { + log_error(LOG_LEVEL_ERROR, "Failed to send server headers to the client."); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_INSPECTION + close_client_and_server_ssl_connections(csp); +#endif + return -1; + } + + return 0; + +} + /********************************************************************* * * Function : handle_established_connection * * Description : Shuffle data between client and server once the - * connection has been established. + * connection has been established and the request + * has been sent. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -2995,7 +3103,6 @@ static void continue_https_chat(struct client_state *csp) *********************************************************************/ static void handle_established_connection(struct client_state *csp) { - char *hdr; char *p; int n; #ifdef HAVE_POLL @@ -3012,6 +3119,7 @@ static void handle_established_connection(struct client_state *csp) long len = 0; /* for buffer sizes (and negative error codes) */ int buffer_and_filter_content = 0; unsigned int write_delay; + size_t chunk_offset = 0; #ifdef FEATURE_HTTPS_INSPECTION int ret = 0; int use_ssl_tunnel = 0; @@ -3061,6 +3169,28 @@ 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 FD_ZERO(&rfds); #ifdef FEATURE_CONNECTION_KEEP_ALIVE @@ -3078,23 +3208,6 @@ static void handle_established_connection(struct client_state *csp) #endif /* ndef HAVE_POLL */ #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if ((csp->flags & CSP_FLAG_CHUNKED) - && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET) - && ((csp->iob->eod - csp->iob->cur) >= 5) - && !memcmp(csp->iob->eod-5, "0\r\n\r\n", 5)) - { - /* - * XXX: This check should be obsolete now, - * but let's wait a while to be sure. - */ - log_error(LOG_LEVEL_CONNECT, - "Looks like we got the last chunk together with " - "the server headers but didn't detect it earlier. " - "We better stop reading."); - byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur); - csp->expected_content_length = byte_count; - csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET; - } if (server_body && server_response_is_complete(csp, byte_count)) { if (csp->expected_content_length == byte_count) @@ -3147,7 +3260,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", @@ -3201,7 +3314,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)) @@ -3325,8 +3444,6 @@ static void handle_established_connection(struct client_state *csp) /* * The server wants to talk. It could be the header or the body. - * If `hdr' is null, then it's the header otherwise it's the body. - * FIXME: Does `hdr' really mean `host'? No. */ #ifdef HAVE_POLL if (poll_fds[1].revents != 0) @@ -3334,6 +3451,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 @@ -3419,18 +3539,6 @@ static void handle_established_connection(struct client_state *csp) } #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if (csp->flags & CSP_FLAG_CHUNKED) - { - if ((len >= 5) && !memcmp(csp->receive_buffer+len-5, "0\r\n\r\n", 5)) - { - /* XXX: this is a temporary hack */ - log_error(LOG_LEVEL_CONNECT, - "Looks like we reached the end of the last chunk. " - "We better stop reading."); - csp->expected_content_length = byte_count + (unsigned long long)len; - csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET; - } - } reading_done: #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ @@ -3518,11 +3626,9 @@ static void handle_established_connection(struct client_state *csp) "Failed to update server headers. after filtering."); } - hdr = list_to_text(csp->headers); - if (hdr == NULL) + if (send_server_headers(csp)) { - /* FIXME Should handle error properly */ - log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); + return; } #ifdef FEATURE_HTTPS_INSPECTION @@ -3531,16 +3637,12 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - if ((ssl_send_data_delayed(&(csp->ssl_client_attr), - (const unsigned char *)hdr, strlen(hdr), - get_write_delay(csp)) < 0) - || (ssl_send_data_delayed(&(csp->ssl_client_attr), + if (ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *) ((p != NULL) ? p : csp->iob->cur), - csp->content_length, get_write_delay(csp)) < 0)) + csp->content_length, get_write_delay(csp)) < 0) { - log_error(LOG_LEVEL_ERROR, "write modified content to " - "client over TLS/SSL failed"); - freez(hdr); + log_error(LOG_LEVEL_ERROR, + "Failed to send the modified content to the client over TLS"); freez(p); mark_server_socket_tainted(csp); close_client_and_server_ssl_connections(csp); @@ -3550,19 +3652,16 @@ static void handle_established_connection(struct client_state *csp) else #endif /* def FEATURE_HTTPS_INSPECTION */ { - if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay) - || write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur), + if (write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur), (size_t)csp->content_length, write_delay)) { log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E"); - freez(hdr); freez(p); mark_server_socket_tainted(csp); return; } } - freez(hdr); freez(p); } @@ -3570,8 +3669,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); @@ -3604,29 +3703,15 @@ static void handle_established_connection(struct client_state *csp) */ if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len)) { - size_t hdrlen; long flushed; log_error(LOG_LEVEL_INFO, "Flushing header and buffers. Stepping back from filtering."); - hdr = list_to_text(csp->headers); - if (hdr == NULL) + if (send_server_headers(csp)) { - /* - * Memory is too tight to even generate the header. - * Send our static "Out-of-memory" page. - */ - log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush."); - rsp = cgi_error_memory(); - send_crunch_response(csp, rsp); - mark_server_socket_tainted(csp); -#ifdef FEATURE_HTTPS_INSPECTION - close_client_and_server_ssl_connections(csp); -#endif return; } - hdrlen = strlen(hdr); #ifdef FEATURE_HTTPS_INSPECTION /* @@ -3634,9 +3719,7 @@ static void handle_established_connection(struct client_state *csp) */ if (client_use_ssl(csp)) { - if ((ssl_send_data_delayed(&(csp->ssl_client_attr), - (const unsigned char *)hdr, hdrlen, get_write_delay(csp)) < 0) - || ((flushed = ssl_flush_socket(&(csp->ssl_client_attr), + if (((flushed = ssl_flush_socket(&(csp->ssl_client_attr), csp->iob)) < 0) || (ssl_send_data_delayed(&(csp->ssl_client_attr), (const unsigned char *)csp->receive_buffer, (size_t)len, @@ -3644,7 +3727,6 @@ static void handle_established_connection(struct client_state *csp) { log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed"); - freez(hdr); mark_server_socket_tainted(csp); close_client_and_server_ssl_connections(csp); return; @@ -3653,26 +3735,43 @@ static void handle_established_connection(struct client_state *csp) else #endif /* def FEATURE_HTTPS_INSPECTION */ { - if (write_socket_delayed(csp->cfd, hdr, hdrlen, write_delay) - || ((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0) + if (((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0) || write_socket_delayed(csp->cfd, csp->receive_buffer, (size_t)len, write_delay)) { log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E"); - freez(hdr); mark_server_socket_tainted(csp); return; } } /* - * Reset the byte_count to the amount of bytes - * we just flushed. len will be added a few lines below, - * hdrlen doesn't matter for LOG_LEVEL_CLF. + * Reset the byte_count to the amount of bytes we just + * flushed. len will be added a few lines below. */ byte_count = (unsigned long long)flushed; - freez(hdr); + if ((csp->flags & CSP_FLAG_CHUNKED) && (chunk_offset != 0)) + { + log_error(LOG_LEVEL_CONNECT, + "Reducing chunk offset %lu by %ld to %lu.", chunk_offset, flushed, + (chunk_offset - (unsigned)flushed)); + assert(chunk_offset >= flushed); /* XXX: Reachable with malicious input? */ + chunk_offset -= (unsigned)flushed; + + /* Make room in the iob. */ + csp->iob->cur = csp->iob->eod = csp->iob->buf; + + if (add_to_iob(csp->iob, csp->config->buffer_limit, + csp->receive_buffer, len)) + { + /* This is not supposed to happen but ... */ + csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + log_error(LOG_LEVEL_ERROR, "Failed to buffer %ld bytes of " + "chunk-encoded data after resetting the buffer.", len); + return; + } + } buffer_and_filter_content = 0; server_body = 1; } @@ -3708,8 +3807,66 @@ static void handle_established_connection(struct client_state *csp) return; } } + if (csp->flags & CSP_FLAG_CHUNKED) + { + /* + * While we don't need the data to filter it, put it in the + * buffer so we can keep track of the offset to the start of + * the next chunk and detect when the response is finished. + */ + size_t encoded_bytes = (size_t)(csp->iob->eod - csp->iob->cur); + + if (csp->config->buffer_limit / 4 < encoded_bytes) + { + /* + * Reset the buffer to reduce the memory footprint. + */ + log_error(LOG_LEVEL_CONNECT, + "Reducing the chunk offset from %lu to %lu after " + "discarding %lu bytes to make room in the buffer.", + chunk_offset, (chunk_offset - encoded_bytes), + encoded_bytes); + chunk_offset -= encoded_bytes; + csp->iob->cur = csp->iob->eod = csp->iob->buf; + } + if (add_to_iob(csp->iob, csp->config->buffer_limit, + csp->receive_buffer, len)) + { + /* This is not supposed to happen but ... */ + csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE; + log_error(LOG_LEVEL_ERROR, + "Failed to buffer %ld bytes of chunk-encoded data.", + len); + return; + } + } } byte_count += (unsigned long long)len; + + if (csp->flags & CSP_FLAG_CHUNKED) + { + int rc; + size_t encoded_bytes = (size_t)(csp->iob->eod - csp->iob->cur); + + rc = get_bytes_missing_from_chunked_data(csp->iob->cur, encoded_bytes, + chunk_offset); + if (rc >= 0) + { + if (rc != 0) + { + chunk_offset = (size_t)rc; + } + + if (chunked_data_is_complete(csp->iob->cur, encoded_bytes, chunk_offset)) + { + log_error(LOG_LEVEL_CONNECT, + "We buffered the last chunk of the response."); + csp->expected_content_length = byte_count; + csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET; + } + } + } + continue; } else @@ -3864,6 +4021,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 @@ -3896,26 +4065,32 @@ static void handle_established_connection(struct client_state *csp) #endif return; } - hdr = list_to_text(csp->headers); - if (hdr == NULL) - { - /* FIXME Should handle error properly */ - log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); - } if ((csp->flags & CSP_FLAG_CHUNKED) - && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET) - && ((csp->iob->eod - csp->iob->cur) >= 5) - && !memcmp(csp->iob->eod-5, "0\r\n\r\n", 5)) + && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET)) { - log_error(LOG_LEVEL_CONNECT, - "Looks like we got the last chunk together with " - "the server headers. We better stop reading."); - byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur); - csp->expected_content_length = byte_count; - csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET; - } + int rc; + size_t encoded_size = (size_t)(csp->iob->eod - csp->iob->cur); + rc = get_bytes_missing_from_chunked_data(csp->iob->cur, encoded_size, + chunk_offset); + if (rc >= 0) + { + if (rc != 0) + { + chunk_offset = (size_t)rc; + } + if (chunked_data_is_complete(csp->iob->cur, encoded_size, chunk_offset)) + { + log_error(LOG_LEVEL_CONNECT, + "Looks like we got the last chunk together with " + "the server headers. We better stop reading."); + byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur); + csp->expected_content_length = byte_count; + csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET; + } + } + } csp->server_connection.response_received = time(NULL); if (crunch_response_triggered(csp, crunchers_light)) @@ -3926,7 +4101,6 @@ static void handle_established_connection(struct client_state *csp) * delivered the crunch response to the client * and are done here after cleaning up. */ - freez(hdr); mark_server_socket_tainted(csp); #ifdef FEATURE_HTTPS_INSPECTION close_client_and_server_ssl_connections(csp); @@ -3945,22 +4119,23 @@ static void handle_established_connection(struct client_state *csp) * may be in the buffer). Use standard or secured * connection. */ + if (send_server_headers(csp)) + { + return; + } #ifdef FEATURE_HTTPS_INSPECTION if (client_use_ssl(csp)) { - if ((ssl_send_data_delayed(&(csp->ssl_client_attr), - (const unsigned char *)hdr, strlen(hdr), - get_write_delay(csp)) < 0) - || ((len = ssl_flush_socket(&(csp->ssl_client_attr), - csp->iob)) < 0)) + if ((len = ssl_flush_socket(&(csp->ssl_client_attr), + csp->iob)) < 0) { - log_error(LOG_LEVEL_CONNECT, "Write header to client failed"); + log_error(LOG_LEVEL_CONNECT, + "Sending buffered bytes to the client failed"); /* * The write failed, so don't bother mentioning it * to the client... it probably can't hear us anyway. */ - freez(hdr); mark_server_socket_tainted(csp); #ifdef FEATURE_HTTPS_INSPECTION close_client_and_server_ssl_connections(csp); @@ -3971,25 +4146,48 @@ static void handle_established_connection(struct client_state *csp) else #endif /* def FEATURE_HTTPS_INSPECTION */ { - if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay) - || ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)) + if ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 0) { log_error(LOG_LEVEL_ERROR, - "write header to client failed"); + "Sending buffered bytes to the client failed."); /* * The write failed, so don't bother mentioning it * to the client... it probably can't hear us anyway. */ - freez(hdr); mark_server_socket_tainted(csp); return; } + } + if (csp->flags & CSP_FLAG_CHUNKED && + !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET)) + { + /* + * In case of valid data we shouldn't flush more + * data than chunk_offset but the data may be invalid. + */ + if (chunk_offset >= len) + { + log_error(LOG_LEVEL_CONNECT, + "Reducing chunk offset from %lu to %lu after flushing %ld bytes", + chunk_offset, (chunk_offset - (unsigned)len), len); + chunk_offset = chunk_offset - (unsigned)len; + } + else + { + log_error(LOG_LEVEL_CONNECT, + "Keeping chunk offset at %lu despite flushing %ld bytes", + chunk_offset, len); + /* + * If we can't parse the chunk-encoded data we should + * not reuse the server connection. + */ + mark_server_socket_tainted(csp); + } } } /* we're finished with the server's header */ - freez(hdr); server_body = 1; /* @@ -4114,14 +4312,18 @@ static void chat(struct client_state *csp) { return; } + +#if defined(FEATURE_STATISTICS) && defined(MUTEX_LOCKS_AVAILABLE) + privoxy_mutex_lock(&block_statistics_mutex); + number_of_requests_received++; + privoxy_mutex_unlock(&block_statistics_mutex); +#endif + if (parse_client_request(csp) != JB_ERR_OK) { return; } - /* decide how to route the HTTP request */ - fwd = forward_url(csp, http); - #ifdef FEATURE_HTTPS_INSPECTION /* * Setting flags to use old solution with SSL tunnel and to disable @@ -4224,10 +4426,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 @@ -4246,7 +4444,126 @@ 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 + + /* If we need to apply client body filters, buffer the whole request now. */ + if (csp->expected_client_content_length != 0 && + (client_body_filters_enabled(csp->action) || + client_body_taggers_enabled(csp->action)) && + can_buffer_request_body(csp)) + { + int content_modified; + size_t modified_content_length; + +#ifdef FEATURE_HTTPS_INSPECTION + if (client_use_ssl(csp) && read_https_request_body(csp)) + { + log_error(LOG_LEVEL_ERROR, "Failed to buffer the encrypted " + "request body to apply filters or taggers."); + log_error(LOG_LEVEL_CLF, + "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd); + + ssl_send_data_delayed(&(csp->ssl_client_attr), + (const unsigned char *)CLIENT_BODY_BUFFER_ERROR_RESPONSE, + strlen(CLIENT_BODY_BUFFER_ERROR_RESPONSE), + get_write_delay(csp)); + + return; + } + else +#endif + if (read_http_request_body(csp)) + { + log_error(LOG_LEVEL_ERROR, + "Failed to buffer the request body to apply filters or taggers,"); + log_error(LOG_LEVEL_CLF, + "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd); + + write_socket_delayed(csp->cfd, CLIENT_BODY_BUFFER_ERROR_RESPONSE, + strlen(CLIENT_BODY_BUFFER_ERROR_RESPONSE), get_write_delay(csp)); + + return; + } + if (client_body_taggers_enabled(csp->action)) + { + execute_client_body_taggers(csp, csp->expected_client_content_length); + if (crunch_response_triggered(csp, crunchers_all)) + { + /* + * Yes. The client got the crunch response and we're done here. + */ + return; + } + } + if (client_body_filters_enabled(csp->action)) + { + modified_content_length = csp->expected_client_content_length; + content_modified = execute_client_body_filters(csp, + &modified_content_length); + if ((content_modified == 1) && + (modified_content_length != csp->expected_client_content_length) && + update_client_headers(csp, modified_content_length)) + { + /* XXX: Send error response */ + log_error(LOG_LEVEL_HEADER, "Error updating client headers"); + return; + } + } + csp->expected_client_content_length = 0; + } + log_applied_actions(csp->action); + + /* decide how to route the HTTP request */ + fwd = forward_url(csp, http); + + freez(csp->headers->first->str); + build_request_line(csp, fwd, &csp->headers->first->str); + if (fwd->forward_host) { log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s", @@ -4294,51 +4611,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 (client_use_ssl(csp) && !use_ssl_tunnel) - { - int ret; - /* - * Creating a SSL proxy. - * - * By sending the CSUCCEED message we're lying to the client as - * the connection hasn't actually been established yet. We don't - * establish the connection until we have seen and parsed the - * encrypted client headers. - */ - if (write_socket_delayed(csp->cfd, CSUCCEED, - strlen(CSUCCEED), get_write_delay(csp)) != 0) - { - log_error(LOG_LEVEL_ERROR, "Sending SUCCEED to client failed"); - return; - } - ret = create_client_ssl_connection(csp); - if (ret != 0) - { - log_error(LOG_LEVEL_ERROR, - "Failed to open a secure connection with the client"); - return; - } - if (JB_ERR_OK != process_encrypted_request(csp)) - { - close_client_ssl_connection(csp); - return; - } - /* - * We have an encrypted request. Check if one of the crunchers now - * wants it (for example because the previously invisible path was - * required to match). - */ - if (crunch_response_triggered(csp, crunchers_all)) - { - /* - * Yes. The client got the crunch response and we're done here. - */ - return; - } - } -#endif /* * Connecting to destination server */ @@ -4523,7 +4796,13 @@ static void chat(struct client_state *csp) #endif )) { - if (send_http_request(csp)) + int status = send_http_request(csp); + if (status == 2) + { + /* The request got crunched, a response has been delivered. */ + return; + } + if (status != 0) { rsp = error_response(csp, "connect-failed"); if (rsp) @@ -4834,6 +5113,22 @@ static void serve(struct client_state *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 } } @@ -5170,9 +5465,12 @@ 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_statistics_mutex); + privoxy_mutex_init(&block_reason_statistics_mutex); #endif /* @@ -5751,15 +6049,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 */ @@ -6029,7 +6330,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, @@ -6199,6 +6500,8 @@ static void listen_loop(void) log_error(LOG_LEVEL_INFO, "Graceful termination requested."); + close_ports_helper(bfds); + unload_current_config_file(); unload_current_actions_file(); unload_current_re_filterfile();