X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=a7856bf3b5f2830f918ff194d13fecf05cbb2bd3;hb=7939028dfbc9fb296d46ff88d2d5d3febd777c9a;hp=c211036878c13480531b0b8ed832796b53babf43;hpb=f33471c811a622f5fc2e61a2350bc7185e242a43;p=privoxy.git diff --git a/jcc.c b/jcc.c index c2110368..a7856bf3 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.120 2007/01/26 14:18:42 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.123 2007/02/21 18:42:10 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -33,6 +33,26 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.120 2007/01/26 14:18:42 fabiankeil Exp $" * * Revisions : * $Log: jcc.c,v $ + * Revision 1.123 2007/02/21 18:42:10 fabiankeil + * Answer requests that contain NULL bytes with + * a custom response instead of waiting for more + * data until the client eventually hangs up. + * + * Revision 1.122 2007/02/07 11:12:02 fabiankeil + * - Move delivery and logging of crunched responses + * from chat() into send_crunch_response(). + * - Display the reason for generating http_responses. + * - Log the content length for LOG_LEVEL_CLF correctly + * (still incorrect for some fixed responses). + * - Reword an incorrect comment about + * treat-forbidden-connects-like-blocks violating + * the specs. + * - Add some log messages. + * + * Revision 1.121 2007/01/27 10:52:56 fabiankeil + * Move mutex initialization into separate + * function and exit in case of errors. + * * Revision 1.120 2007/01/26 14:18:42 fabiankeil * - Start to reduce chat()'s line count and move * parts of it into separate functions. @@ -760,6 +780,7 @@ http://www.fabiankeil.de/sourcecode/privoxy/ #include #include #include +#include #ifdef _WIN32 # ifndef FEATURE_PTHREAD @@ -952,6 +973,13 @@ const char NO_SERVER_DATA_RESPONSE[] = "Empty server or forwarder response.\r\n" "The connection was closed without sending any data.\r\n"; +/* XXX: should be a template */ +const char NULL_BYTE_RESPONSE[] = + "HTTP/1.0 400 Bad request received from browser\r\n" + "Proxy-Agent: Privoxy " VERSION "\r\n" + "Connection: close\r\n\r\n" + "Bad request. Null byte(s) before end of request.\r\n"; + #if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) /********************************************************************* * @@ -1096,6 +1124,7 @@ jb_err get_request_destination_elsewhere(struct client_state *csp, struct list * log_error(LOG_LEVEL_ERROR, "%s's request: \'%s\' is invalid." " Privoxy isn't configured to accept intercepted requests.", csp->ip_addr_str, csp->http->cmd); + /* XXX: Use correct size */ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd); @@ -1118,6 +1147,7 @@ jb_err get_request_destination_elsewhere(struct client_state *csp, struct list * 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, @@ -1228,6 +1258,144 @@ jb_err get_server_headers(struct client_state *csp) } +/********************************************************************* + * + * Function : crunch_reason + * + * Description : Translates the crunch reason code into a string. + * + * Parameters : + * 1 : rsp = a http_response + * + * Returns : A string with the crunch reason or an error description. + * + *********************************************************************/ +const char *crunch_reason(const struct http_response *rsp) +{ + char * reason = NULL; + + assert(rsp != NULL); + if (rsp == NULL) + { + return "Internal error while searching for crunch reason"; + } + + switch (rsp->reason) + { + case RSP_REASON_UNSUPPORTED: + reason = "Unsupported HTTP feature"; + break; + case RSP_REASON_BLOCKED: + reason = "Blocked"; + break; + case RSP_REASON_UNTRUSTED: + reason = "Untrusted"; + break; + case RSP_REASON_REDIRECTED: + reason = "Redirected"; + break; + case RSP_REASON_CGI_CALL: + reason = "CGI Call"; + break; + case RSP_REASON_NO_SUCH_DOMAIN: + reason = "DNS failure"; + break; + case RSP_REASON_FORWARDING_FAILED: + reason = "Forwarding failed"; + break; + case RSP_REASON_CONNECT_FAILED: + reason = "Connection failure"; + break; + case RSP_REASON_OUT_OF_MEMORY: + reason = "Out of memory (may mask other reasons)"; + break; + default: + reason = "No reason recorded"; + break; + } + + return reason; +} + + +/********************************************************************* + * + * Function : send_crunch_response + * + * Description : Delivers already prepared response for + * intercepted requests, logs the interception + * and frees the response. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 1 : rsp = Fully prepared response. Will be freed on exit. + * + * Returns : Nothing. + * + *********************************************************************/ +void send_crunch_response(struct client_state *csp, struct http_response *rsp) +{ + const struct http_request *http = csp->http; + char status_code[4]; + + assert(rsp != NULL); + assert(rsp->head != NULL); + + if (rsp == NULL) + { + /* + * Not supposed to happen. If it does + * anyway, treat it as an unknown error. + */ + cgi_error_unknown(csp, rsp, RSP_REASON_INTERNAL_ERROR); + /* return code doesn't matter */ + } + + if (rsp == NULL) + { + /* If rsp is still NULL, we have serious internal problems. */ + log_error(LOG_LEVEL_FATAL, + "NULL response in send_crunch_response and cgi_error_unknown failed as well."); + } + + /* + * Extract the status code from the actual head + * that was send to the client. It is the only + * way to get it right for all requests, including + * the fixed ones for out-of-memory problems. + * + * A head starts like this: 'HTTP/1.1 200...' + * 0123456789|11 + * 10 + */ + status_code[0] = rsp->head[9]; + status_code[1] = rsp->head[10]; + status_code[2] = rsp->head[11]; + status_code[3] = '\0'; + + /* Write the answer to the client */ + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + /* There is nothing we can do about it. */ + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", csp->http->host); + } + + /* Log that the request was crunched and why. */ + log_error(LOG_LEVEL_GPC, "%s%s crunch! (%s)", + http->hostport, http->path, crunch_reason(rsp)); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" %s %d", + csp->ip_addr_str, http->ocmd, status_code, rsp->content_length); + + /* Clean up and return */ + if (cgi_error_memory() != rsp) + { + free_http_response(rsp); + } + return; +} + + /********************************************************************* * * Function : chat @@ -1309,6 +1477,8 @@ static void chat(struct client_state *csp) http = csp->http; + memset(buf, 0, sizeof(buf)); + /* * Read the client's request. Note that since we're not using select() we * could get blocked here if a client connected, then didn't say anything! @@ -1316,10 +1486,46 @@ static void chat(struct client_state *csp) for (;;) { - len = read_socket(csp->cfd, buf, sizeof(buf)); + size_t c_len; /* Request lenght when treated as C string */ + + len = read_socket(csp->cfd, buf, sizeof(buf)-1); if (len <= 0) break; /* error! */ - + + /* Naughty NULL bytes inside the request? */ + c_len = strlen(buf); + if (c_len < len) + { + /* + * Yes. Log the request, return an + * error response and hang up. + */ + size_t tmp_len = c_len; + + do + { + /* + * Replace NULL byte(s) with line break(s) + * so the request can be logged as string. + */ + buf[tmp_len]='\n'; + tmp_len += strlen(buf+tmp_len); + } while (tmp_len < len); + + log_error(LOG_LEVEL_ERROR, "%s\'s request contains NULL byte(s) " + "(length=%d, strlen=%d).", csp->ip_addr_str, len, c_len); + log_error(LOG_LEVEL_HEADER, + "Denied request with NULL byte(s) turned into line break(s):\n%s", buf); + + strcpy(buf, NULL_BYTE_RESPONSE); + write_socket(csp->cfd, buf, strlen(buf)); + + /* XXX: Log correct size */ + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str); + + return; + } + /* * If there is no memory left for buffering the * request, there is nothing we can do but hang up @@ -1384,8 +1590,9 @@ static void chat(struct client_state *csp) { strcpy(buf, CHEADER); write_socket(csp->cfd, buf, strlen(buf)); - - log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 400 0", csp->ip_addr_str); + /* XXX: Use correct size */ + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str); + log_error(LOG_LEVEL_ERROR, "Invalid header received from %s.", csp->ip_addr_str); free_http_request(http); return; @@ -1516,7 +1723,10 @@ static void chat(struct client_state *csp) { if (csp->action->flags & ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS) { - /* The response will violate the specs, but makes unblocking easier. */ + /* + * The response may confuse some clients, + * but makes unblocking easier. + */ log_error(LOG_LEVEL_ERROR, "Marking suspicious CONNECT request from %s for blocking.", csp->ip_addr_str); csp->action->flags |= ACTION_BLOCK; @@ -1651,24 +1861,16 @@ static void chat(struct client_state *csp) ) { - /* Write the answer to the client */ - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - } - + /* + * Deliver, log and free the interception + * response. Afterwards we're done here. + */ + send_crunch_response(csp, rsp); #ifdef FEATURE_STATISTICS /* Count as a rejected request */ csp->flags |= CSP_FLAG_REJECTED; #endif /* def FEATURE_STATISTICS */ - /* Log (FIXME: All intercept reasons appear as "crunch" with Status 200) */ - log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path); - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", csp->ip_addr_str, http->ocmd); - - /* Clean up and return */ - free_http_response(rsp); return; } @@ -1708,40 +1910,25 @@ static void chat(struct client_state *csp) { /* Socks error. */ rsp = error_response(csp, "forwarding-failed", errno); - - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", - csp->ip_addr_str, http->ocmd); } else if (errno == EINVAL) { rsp = error_response(csp, "no-such-domain", errno); - - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 404 0", - csp->ip_addr_str, http->ocmd); } else { rsp = error_response(csp, "connect-failed", errno); - log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E", http->hostport); - - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", - csp->ip_addr_str, http->ocmd); } /* Write the answer to the client */ - if(rsp) + if(rsp != NULL) { - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - } + send_crunch_response(csp, rsp); } - free_http_response(rsp); freez(hdr); return; } @@ -1758,21 +1945,13 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E", http->hostport); - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", - csp->ip_addr_str, http->ocmd); - rsp = error_response(csp, "connect-failed", errno); if(rsp) { - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - } + send_crunch_response(csp, rsp); } - free_http_response(rsp); freez(hdr); return; } @@ -1784,7 +1963,7 @@ static void chat(struct client_state *csp) * so just send the "connect succeeded" message to the * client, flush the rest, and get out of the way. */ - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 2\n", + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 0", csp->ip_addr_str, http->ocmd); if (write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1)) @@ -1867,21 +2046,13 @@ static void chat(struct client_state *csp) { log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host); - log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", - csp->ip_addr_str, http->ocmd); - rsp = error_response(csp, "connect-failed", errno); if(rsp) { - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - } + send_crunch_response(csp, rsp); } - free_http_response(rsp); return; } @@ -2008,12 +2179,8 @@ static void chat(struct client_state *csp) */ log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush."); rsp = cgi_error_memory(); + send_crunch_response(csp, rsp); - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - } return; } @@ -2029,7 +2196,7 @@ static void chat(struct client_state *csp) return; } - byte_count += hdrlen + (size_t)(flushed + len); + byte_count += hdrlen + (size_t)flushed + (size_t)len; freez(hdr); content_filter = NULL; server_body = 1; @@ -2063,12 +2230,8 @@ static void chat(struct client_state *csp) { log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers."); rsp = cgi_error_memory(); - - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - } + send_crunch_response(csp, rsp); + return; } @@ -2209,8 +2372,18 @@ static void chat(struct client_state *csp) return; /* huh? we should never get here */ } + if (csp->content_length == 0) + { + /* + * If Privoxy didn't recalculate the + * Content-Lenght, byte_count is still + * correct. + */ + csp->content_length = byte_count; + } + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %d", - csp->ip_addr_str, http->ocmd, byte_count); + csp->ip_addr_str, http->ocmd, csp->content_length); }