X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=056a6c1e9b1af98786ccd072ac4d47acec0e87a5;hp=c211036878c13480531b0b8ed832796b53babf43;hb=f9888d9c1d0b5b4e082718508532ddaf2ba8759c;hpb=f33471c811a622f5fc2e61a2350bc7185e242a43 diff --git a/jcc.c b/jcc.c index c2110368..056a6c1e 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.127 2007/03/20 13:53:17 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -33,6 +33,41 @@ 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.127 2007/03/20 13:53:17 fabiankeil + * Log the source address for ACL-related connection drops. + * + * Revision 1.126 2007/03/17 15:20:05 fabiankeil + * New config option: enforce-blocks. + * + * Revision 1.125 2007/03/09 14:12:00 fabiankeil + * - Move null byte check into separate function. + * - Don't confuse the client with error pages + * if a CONNECT request was already confirmed. + * + * Revision 1.124 2007/02/23 14:59:54 fabiankeil + * Speed up NULL byte escaping and only log the complete + * NULL byte requests with header debugging enabled. + * + * 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 +795,7 @@ http://www.fabiankeil.de/sourcecode/privoxy/ #include #include #include +#include #ifdef _WIN32 # ifndef FEATURE_PTHREAD @@ -952,6 +988,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 +1139,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 +1162,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 +1273,203 @@ 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 : request_contains_null_bytes + * + * Description : Checks for NULL bytes in the request and sends + * an error message to the client if any were found. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : buf = Data from the client's request to check. + * 3 : len = The data length. + * + * Returns : TRUE if the request contained one or more NULL bytes, or + * FALSE otherwise. + * + *********************************************************************/ +int request_contains_null_bytes(const struct client_state *csp, char *buf, int len) +{ + size_t c_len; /* Request lenght when treated as C string */ + + c_len = strlen(buf); + + if (c_len < len) + { + /* + * Null byte(s) found. Log the request, + * return an error response and hang up. + */ + size_t tmp_len = c_len; + + do + { + /* + * Replace NULL byte(s) with '°' characters + * so the request can be logged as string. + * XXX: Is there a better replacement character? + */ + buf[tmp_len]='°'; + tmp_len += strlen(buf+tmp_len); + } while (tmp_len < len); + + log_error(LOG_LEVEL_ERROR, "%s\'s request contains at least one NULL byte " + "(length=%d, strlen=%d).", csp->ip_addr_str, len, c_len); + log_error(LOG_LEVEL_HEADER, + "Offending request data with NULL bytes turned into \'°\' characters: %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 TRUE; + } + + return FALSE; +} + + /********************************************************************* * * Function : chat @@ -1309,6 +1551,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 +1560,16 @@ static void chat(struct client_state *csp) for (;;) { - len = read_socket(csp->cfd, buf, sizeof(buf)); + len = read_socket(csp->cfd, buf, sizeof(buf)-1); if (len <= 0) break; /* error! */ - + + if (request_contains_null_bytes(csp, buf, len)) + { + /* NULL bytes found and dealt with, just hang up. */ + return; + } + /* * If there is no memory left for buffering the * request, there is nothing we can do but hang up @@ -1353,15 +1603,23 @@ static void chat(struct client_state *csp) } #ifdef FEATURE_FORCE_LOAD - /* If this request contains the FORCE_PREFIX, - * better get rid of it now and set the force flag --oes + /* + * If this request contains the FORCE_PREFIX and blocks + * aren't enforced, get rid of it and set the force flag. */ - if (strstr(req, FORCE_PREFIX)) { - strclean(req, FORCE_PREFIX); - log_error(LOG_LEVEL_FORCE, "Enforcing request \"%s\".\n", req); - csp->flags |= CSP_FLAG_FORCED; + if (csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS) + { + log_error(LOG_LEVEL_FORCE, + "Ignored force prefix in request: \"%s\".", req); + } + else + { + strclean(req, FORCE_PREFIX); + log_error(LOG_LEVEL_FORCE, "Enforcing request: \"%s\".", req); + csp->flags |= CSP_FLAG_FORCED; + } } #endif /* def FEATURE_FORCE_LOAD */ @@ -1384,8 +1642,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 +1775,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 +1913,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 +1962,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 +1997,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,9 +2015,6 @@ 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", - csp->ip_addr_str, http->ocmd); - if (write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1)) { freez(hdr); @@ -1867,21 +2095,25 @@ 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); + if (http->ssl && (fwd->forward_host == NULL)) + { + /* + * Just hang up. We already confirmed the client's CONNECT + * request with status code 200 and unencrypted content is + * no longer welcome. + */ + log_error(LOG_LEVEL_ERROR, + "CONNECT already confirmed. Unable to tell the client about the problem."); + return; + } 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 +2240,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 +2257,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 +2291,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 +2433,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); } @@ -2972,7 +3206,7 @@ static void listen_loop(void) #ifdef FEATURE_ACL if (block_acl(NULL,csp)) { - log_error(LOG_LEVEL_CONNECT, "Connection dropped due to ACL"); + log_error(LOG_LEVEL_CONNECT, "Connection from %s dropped due to ACL", csp->ip_addr_str); close_socket(csp->cfd); freez(csp); continue;