X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=jcc.c;h=4b3e4ebaa3985096254740dc8aa6fb7beb8ff95c;hb=c1106aa0f9d5e26d68c852bc1ef02beb088562d9;hp=b2a2881845b1be7514eeba11527e50711904877a;hpb=1fda481db31103ed69032cbc3ed27e75718b1519;p=privoxy.git diff --git a/jcc.c b/jcc.c index b2a28818..4b3e4eba 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.176 2008/05/10 11:37:57 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.187 2008/09/07 12:35:05 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -33,6 +33,49 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.176 2008/05/10 11:37:57 fabiankeil Exp $" * * Revisions : * $Log: jcc.c,v $ + * Revision 1.187 2008/09/07 12:35:05 fabiankeil + * Add mutex lock support for _WIN32. + * + * Revision 1.186 2008/09/04 08:13:58 fabiankeil + * Prepare for critical sections on Windows by adding a + * layer of indirection before the pthread mutex functions. + * + * Revision 1.185 2008/08/30 12:03:07 fabiankeil + * Remove FEATURE_COOKIE_JAR. + * + * Revision 1.184 2008/08/22 15:34:45 fabiankeil + * - Silence LLVM/Clang complaint. + * - Make received_hup_signal static. + * - Hide definitions for basedir, pidfile and received_hup_signal + * from __EMX__ as they only seem to be used in case of #ifdef unix. + * + * Revision 1.183 2008/08/21 07:09:35 fabiankeil + * Accept Shoutcast responses again. Problem reported + * and fix suggested by Stefan in #2062860. + * + * Revision 1.182 2008/06/27 11:13:56 fabiankeil + * Fix possible NULL-pointer dereference reported + * by din_a4 in #2003937. Pointy hat to me. + * + * Revision 1.181 2008/05/21 15:47:15 fabiankeil + * Streamline sed()'s prototype and declare + * the header parse and add structures static. + * + * Revision 1.180 2008/05/21 15:26:32 fabiankeil + * - Mark csp as immutable for send_crunch_response(). + * - Fix comment spelling. + * + * Revision 1.179 2008/05/20 20:13:32 fabiankeil + * Factor update_server_headers() out of sed(), ditch the + * first_run hack and make server_patterns_light static. + * + * Revision 1.178 2008/05/10 13:23:38 fabiankeil + * Don't provide get_header() with the whole client state + * structure when it only needs access to csp->iob. + * + * Revision 1.177 2008/05/10 11:51:12 fabiankeil + * Make the "read the rest of the headers" loop a bit more readable. + * * Revision 1.176 2008/05/10 11:37:57 fabiankeil * - Instead of logging when the IIS5 hack is enabled, log when it fails. * - Remove useless comment. @@ -1101,7 +1144,7 @@ static int client_protocol_is_unsupported(const struct client_state *csp, char * static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers); static jb_err get_server_headers(struct client_state *csp); static const char *crunch_reason(const struct http_response *rsp); -static void send_crunch_response(struct client_state *csp, struct http_response *rsp); +static void send_crunch_response(const struct client_state *csp, struct http_response *rsp); /* * static int request_contains_null_bytes(const struct client_state *csp, char *buf, int len); */ @@ -1134,32 +1177,36 @@ static int32 server_thread(void *data); #define sleep(N) DosSleep(((N) * 100)) #endif -#ifdef FEATURE_PTHREAD -pthread_mutex_t log_mutex; -pthread_mutex_t log_init_mutex; +#ifdef MUTEX_LOCKS_AVAILABLE +/* + * XXX: Does the locking stuff really belong in this file? + */ +privoxy_mutex_t log_mutex; +privoxy_mutex_t log_init_mutex; +privoxy_mutex_t connection_reuse_mutex; #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) -pthread_mutex_t resolver_mutex; +privoxy_mutex_t resolver_mutex; #endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */ #ifndef HAVE_GMTIME_R -pthread_mutex_t gmtime_mutex; +privoxy_mutex_t gmtime_mutex; #endif /* ndef HAVE_GMTIME_R */ #ifndef HAVE_LOCALTIME_R -pthread_mutex_t localtime_mutex; +privoxy_mutex_t localtime_mutex; #endif /* ndef HAVE_GMTIME_R */ #ifndef HAVE_RANDOM -pthread_mutex_t rand_mutex; +privoxy_mutex_t rand_mutex; #endif /* ndef HAVE_RANDOM */ -#endif /* FEATURE_PTHREAD */ +#endif /* def MUTEX_LOCKS_AVAILABLE */ -#if defined(unix) || defined(__EMX__) +#if defined(unix) const char *basedir = NULL; const char *pidfile = NULL; -int received_hup_signal = 0; +static int received_hup_signal = 0; #endif /* defined unix */ /* HTTP snipplets. */ @@ -1268,6 +1315,13 @@ static const struct cruncher crunchers_light[] = { }; +/* + * XXX: Don't we really mean + * + * #if defined(unix) + * + * here? + */ #if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) /********************************************************************* * @@ -1301,7 +1355,9 @@ static void sig_handler(int the_signal) break; case SIGHUP: +#if defined(unix) received_hup_signal = 1; +#endif break; default: @@ -1477,7 +1533,7 @@ static jb_err get_server_headers(struct client_state *csp) int continue_hack_in_da_house = 0; char * header; - while (((header = get_header(csp)) != NULL) || continue_hack_in_da_house) + while (((header = get_header(csp->iob)) != NULL) || continue_hack_in_da_house) { if (header == NULL) { @@ -1494,7 +1550,7 @@ static jb_err get_server_headers(struct client_state *csp) { /* * It's a bodyless continue response, don't - * stop header parsing after reaching it's end. + * stop header parsing after reaching its end. * * As a result Privoxy will concatenate the * next response's head and parse and deliver @@ -1620,7 +1676,7 @@ static const char *crunch_reason(const struct http_response *rsp) * Returns : Nothing. * *********************************************************************/ -static void send_crunch_response(struct client_state *csp, struct http_response *rsp) +static void send_crunch_response(const struct client_state *csp, struct http_response *rsp) { const struct http_request *http = csp->http; char status_code[4]; @@ -1903,16 +1959,67 @@ 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)); } - http->ocmd = strdup(http->cmd); /* XXX: ocmd is a misleading name */ - if (http->ocmd == NULL) + else { - log_error(LOG_LEVEL_FATAL, "Out of memory copying rewritten HTTP request line"); + /* XXX: ocmd is a misleading name */ + http->ocmd = strdup(http->cmd); + if (http->ocmd == NULL) + { + log_error(LOG_LEVEL_FATAL, + "Out of memory copying rewritten HTTP request line"); + } } return err; } +#ifdef FEATURE_CONNECTION_KEEP_ALIVE +/********************************************************************* + * + * Function : server_response_is_complete + * + * Description : Determines whether we should stop reading + * from the server socket. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : TRUE if the response is complete, + * FALSE otherwise. + * + *********************************************************************/ +static int server_response_is_complete(struct client_state *csp) +{ + int content_length_known = (csp->flags & CSP_FLAG_CONTENT_LENGTH_SET); + + if (!strcmpic(csp->http->gpc, "HEAD")) + { + /* + * "HEAD" implies no body, we are thus expecting + * no content. XXX: incomplete "list" of methods? + */ + log_error(LOG_LEVEL_INFO, "Method %s implies no body.", csp->http->gpc); + csp->expected_content_length = 0; + content_length_known = TRUE; + } + + if (csp->http->status == 304) + { + /* + * Expect no body. XXX: incomplete "list" of status codes? + */ + log_error(LOG_LEVEL_INFO, "Status code %d implies no body.", csp->http->status); + csp->expected_content_length = 0; + content_length_known = TRUE; + } + + return (content_length_known && ((0 == csp->expected_content_length) + || (csp->expected_content_length <= (csp->iob->eod - csp->iob->cur)))); +} +#endif /* FEATURE_CONNECTION_KEEP_ALIVE */ + + /********************************************************************* * * Function : chat @@ -1985,7 +2092,7 @@ static void chat(struct client_state *csp) return; } - req = get_header(csp); + req = get_header(csp->iob); } while ((NULL != req) && ('\0' == *req)); @@ -2073,7 +2180,7 @@ static void chat(struct client_state *csp) init_list(headers); for (;;) { - p = get_header(csp); + p = get_header(csp->iob); if (p == NULL) { @@ -2168,7 +2275,7 @@ static void chat(struct client_state *csp) list_append_list_unique(csp->headers, headers); destroy_list(headers); - err = sed(client_patterns, add_client_headers, csp); + err = sed(csp, FILTER_CLIENT_HEADERS); if (JB_ERR_OK != err) { assert(err == JB_ERR_PARSE); @@ -2403,6 +2510,22 @@ static void chat(struct client_state *csp) FD_SET(csp->cfd, &rfds); FD_SET(csp->sfd, &rfds); +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (server_body && server_response_is_complete(csp)) + { + log_error(LOG_LEVEL_CONNECT, + "Stopped reading from server. Expected content length: %d. " + "Actual content length: %d. Most recently received: %d.", + csp->expected_content_length, (csp->iob->eod - csp->iob->cur), len); + len = 0; + /* + * XXX: should not jump around, + * chat() is complicated enough already. + */ + goto reading_done; + } +#endif /* FEATURE_CONNECTION_KEEP_ALIVE */ + n = select((int)maxfd+1, &rfds, NULL, NULL, NULL); if (n < 0) @@ -2483,6 +2606,10 @@ static void chat(struct client_state *csp) return; } +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + reading_done: +#endif /* FEATURE_CONNECTION_KEEP_ALIVE */ + /* Add a trailing zero. This lets filter_popups * use string operations. */ @@ -2529,9 +2656,10 @@ static void chat(struct client_state *csp) csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur); } - if (JB_ERR_OK != sed(server_patterns_light, NULL, csp)) + if (JB_ERR_OK != update_server_headers(csp)) { - log_error(LOG_LEVEL_FATAL, "Failed to parse server headers."); + log_error(LOG_LEVEL_FATAL, + "Failed to update server headers. after filtering."); } hdr = list_to_text(csp->headers); @@ -2541,12 +2669,6 @@ static void chat(struct client_state *csp) log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); } - /* - * Shouldn't happen because this was the second sed run - * and tags are only created for the first one. - */ - assert(!crunch_response_triggered(csp, crunchers_all)); - if (write_socket(csp->cfd, hdr, strlen(hdr)) || write_socket(csp->cfd, p != NULL ? p : csp->iob->cur, csp->content_length)) { @@ -2706,10 +2828,11 @@ static void chat(struct client_state *csp) assert(csp->headers->first->str); assert(!http->ssl); - if (strncmpic(csp->headers->first->str, "HTTP", 4)) + if (strncmpic(csp->headers->first->str, "HTTP", 4) && + strncmpic(csp->headers->first->str, "ICY", 3)) { /* - * It doesn't look like a HTTP response: + * It doesn't look like a HTTP (or Shoutcast) response: * tell the client and log the problem. */ if (strlen(csp->headers->first->str) > 30) @@ -2727,10 +2850,11 @@ static void chat(struct client_state *csp) return; } - /* we have now received the entire header. + /* + * We have now received the entire server header, * filter it and send the result to the client */ - if (JB_ERR_OK != sed(server_patterns, add_server_headers, csp)) + if (JB_ERR_OK != sed(csp, FILTER_SERVER_HEADERS)) { log_error(LOG_LEVEL_FATAL, "Failed to parse server headers."); } @@ -2846,7 +2970,19 @@ static void serve(struct client_state *csp) if (csp->sfd != JB_INVALID_SOCKET) { +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)) + { + remember_connection(csp->sfd, csp->http, forward_url(csp, csp->http)); + } + else + { + forget_connection(csp->sfd); + close_socket(csp->sfd); + } +#else close_socket(csp->sfd); +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ } csp->flags &= ~CSP_FLAG_ACTIVE; @@ -2908,6 +3044,97 @@ static void usage(const char *myname) #endif /* #if !defined(_WIN32) || defined(_WIN_CONSOLE) */ +#ifdef MUTEX_LOCKS_AVAILABLE +/********************************************************************* + * + * Function : privoxy_mutex_lock + * + * Description : Locks a mutex. + * + * Parameters : + * 1 : mutex = The mutex to lock. + * + * Returns : Void. May exit in case of errors. + * + *********************************************************************/ +void privoxy_mutex_lock(privoxy_mutex_t *mutex) +{ +#ifdef FEATURE_PTHREAD + int err = pthread_mutex_lock(mutex); + if (err) + { + if (mutex != &log_mutex) + { + log_error(LOG_LEVEL_FATAL, + "Mutex locking failed: %s.\n", strerror(err)); + } + exit(1); + } +#else + EnterCriticalSection(mutex); +#endif /* def FEATURE_PTHREAD */ +} + + +/********************************************************************* + * + * Function : privoxy_mutex_unlock + * + * Description : Unlocks a mutex. + * + * Parameters : + * 1 : mutex = The mutex to unlock. + * + * Returns : Void. May exit in case of errors. + * + *********************************************************************/ +void privoxy_mutex_unlock(privoxy_mutex_t *mutex) +{ +#ifdef FEATURE_PTHREAD + int err = pthread_mutex_unlock(mutex); + if (err) + { + if (mutex != &log_mutex) + { + log_error(LOG_LEVEL_FATAL, + "Mutex unlocking failed: %s.\n", strerror(err)); + } + exit(1); + } +#else + LeaveCriticalSection(mutex); +#endif /* def FEATURE_PTHREAD */ +} + + +/********************************************************************* + * + * Function : privoxy_mutex_init + * + * Description : Prepares a mutex. + * + * Parameters : + * 1 : mutex = The mutex to initialize. + * + * Returns : Void. May exit in case of errors. + * + *********************************************************************/ +static void privoxy_mutex_init(privoxy_mutex_t *mutex) +{ +#ifdef FEATURE_PTHREAD + int err = pthread_mutex_init(mutex, 0); + if (err) + { + printf("Fatal error. Mutex initialization failed: %s.\n", + strerror(err)); + exit(1); + } +#else + InitializeCriticalSection(mutex); +#endif /* def FEATURE_PTHREAD */ +} +#endif /* def MUTEX_LOCKS_AVAILABLE */ + /********************************************************************* * * Function : initialize_mutexes @@ -2921,15 +3148,13 @@ static void usage(const char *myname) *********************************************************************/ static void initialize_mutexes(void) { - int err = 0; - -#ifdef FEATURE_PTHREAD +#ifdef MUTEX_LOCKS_AVAILABLE /* * Prepare global mutex semaphores */ - err = pthread_mutex_init(&log_mutex, 0); - - if (!err) err = pthread_mutex_init(&log_init_mutex, 0); + privoxy_mutex_init(&log_mutex); + privoxy_mutex_init(&log_init_mutex); + privoxy_mutex_init(&connection_reuse_mutex); /* * XXX: The assumptions below are a bit naive @@ -2940,37 +3165,24 @@ static void initialize_mutexes(void) * thread safe. */ #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) - if (!err) err = pthread_mutex_init(&resolver_mutex, 0); + privoxy_mutex_init(&resolver_mutex); #endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */ /* * XXX: should we use a single mutex for * localtime() and gmtime() as well? */ #ifndef HAVE_GMTIME_R - if (!err) err = pthread_mutex_init(&gmtime_mutex, 0); + privoxy_mutex_init(&gmtime_mutex); #endif /* ndef HAVE_GMTIME_R */ #ifndef HAVE_LOCALTIME_R - if (!err) err = pthread_mutex_init(&localtime_mutex, 0); + privoxy_mutex_init(&localtime_mutex); #endif /* ndef HAVE_GMTIME_R */ #ifndef HAVE_RANDOM - if (!err) err = pthread_mutex_init(&rand_mutex, 0); + privoxy_mutex_init(&rand_mutex); #endif /* ndef HAVE_RANDOM */ -#endif /* FEATURE_PTHREAD */ - - /* - * TODO: mutex support for mingw32 would be swell. - */ - - if (err) - { - printf("Fatal error. Mutex initialization failed: %s.\n", - strerror(err)); - exit(1); - } - - return; +#endif /* def MUTEX_LOCKS_AVAILABLE */ } @@ -3186,15 +3398,6 @@ int main(int argc, const char *argv[]) random_seed = (unsigned int)time(NULL); #ifdef HAVE_RANDOM srandom(random_seed); -#elif defined (_WIN32) - /* - * See pick_from_range() in miscutil.c for details. - */ - log_error(LOG_LEVEL_INFO, - "No thread-safe PRNG implemented for your platform. " - "Using weak \'randomization\' factor which will " - "limit the already questionable usefulness of " - "header-time-randomizing actions (disabled by default)."); #else srand(random_seed); #endif /* ifdef HAVE_RANDOM */ @@ -3508,6 +3711,14 @@ static void listen_loop(void) config = load_config(); +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + /* + * XXX: Should be relocated once it no + * longer needs to emit log messages. + */ + initialize_reusable_connections(); +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + bfd = bind_port_helper(config); #ifdef FEATURE_GRACEFUL_TERMINATION @@ -3539,23 +3750,6 @@ static void listen_loop(void) } #endif -#ifdef __OS2__ -#ifdef FEATURE_COOKIE_JAR - /* - * Need a workaround here: we have to fclose() the jarfile, or we die because it's - * already open. I think unload_configfile() is not being run, which should do - * this work. Until that can get resolved, we'll use this workaround. - */ - if (csp) - if(csp->config) - if (csp->config->jar) - { - fclose(csp->config->jar); - csp->config->jar = NULL; - } -#endif /* FEATURE_COOKIE_JAR */ -#endif /* __OS2__ */ - if ( NULL == (csp = (struct client_state *) zalloc(sizeof(*csp))) ) { log_error(LOG_LEVEL_FATAL, "malloc(%d) for csp failed: %E", sizeof(*csp)); @@ -3853,13 +4047,6 @@ static void listen_loop(void) #endif freez(configfile); -#ifdef FEATURE_COOKIE_JAR - if (NULL != config->jar) - { - fclose(config->jar); - } -#endif - #if defined(_WIN32) && !defined(_WIN_CONSOLE) /* Cleanup - remove taskbar icon etc. */ TermLogWindow();