-const char jcc_rcs[] = "$Id: jcc.c,v 1.157 2007/11/03 17:34:49 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.175 2008/05/09 18:53:59 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
* Purpose : Main file. Contains main() method, main loop, and
* the main connection-handling function.
*
- * Copyright : Written by and Copyright (C) 2001-2007 the SourceForge
+ * Copyright : Written by and Copyright (C) 2001-2008 the SourceForge
* Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
*
* Revisions :
* $Log: jcc.c,v $
+ * Revision 1.175 2008/05/09 18:53:59 fabiankeil
+ * Fix comment grammar.
+ *
+ * Revision 1.174 2008/05/07 18:05:53 fabiankeil
+ * Remove the pointless buffer in client_protocol_is_unsupported().
+ *
+ * Revision 1.173 2008/05/06 15:09:00 fabiankeil
+ * Least-effort fix for bug #1821930 (reported by Lee):
+ * If the response doesn't look like HTTP,
+ * tell the client and log the problem.
+ *
+ * Revision 1.172 2008/04/16 16:38:21 fabiankeil
+ * Don't pass the whole csp structure to flush_socket()
+ * when it only needs a file descriptor and a buffer.
+ *
+ * Revision 1.171 2008/03/27 18:27:25 fabiankeil
+ * Remove kill-popups action.
+ *
+ * Revision 1.170 2008/03/06 16:33:46 fabiankeil
+ * If limit-connect isn't used, don't limit CONNECT requests to port 443.
+ *
+ * Revision 1.169 2008/03/04 18:30:39 fabiankeil
+ * Remove the treat-forbidden-connects-like-blocks action. We now
+ * use the "blocked" page for forbidden CONNECT requests by default.
+ *
+ * Revision 1.168 2008/03/02 12:25:25 fabiankeil
+ * Also use shiny new connect_port_is_forbidden() in jcc.c.
+ *
+ * Revision 1.167 2008/02/23 16:57:12 fabiankeil
+ * Rename url_actions() to get_url_actions() and let it
+ * use the standard parameter ordering.
+ *
+ * Revision 1.166 2008/02/23 16:33:43 fabiankeil
+ * Let forward_url() use the standard parameter ordering
+ * and mark its second parameter immutable.
+ *
+ * Revision 1.165 2008/02/02 19:36:56 fabiankeil
+ * Remove the "Listening ... for local connections only" log message.
+ * Whether or not remote connections are able to reach Privoxy is up
+ * to the operating system.
+ *
+ * Revision 1.164 2007/12/16 18:32:46 fabiankeil
+ * Prevent the log messages for CONNECT requests to unacceptable
+ * ports from printing the limit-connect argument as [null] if
+ * limit-connect hasn't been explicitly enabled.
+ *
+ * Revision 1.163 2007/12/13 01:47:11 david__schmidt
+ * Make sure all console-mode apps get a usage() instance
+ *
+ * Revision 1.162 2007/12/06 17:54:57 fabiankeil
+ * Reword NO_SERVER_DATA_RESPONSE to make it harder
+ * to misunderstand what the message is all about.
+ *
+ * Revision 1.161 2007/12/04 19:44:22 fabiankeil
+ * Unbreak trustfile which previously didn't work without
+ * FEATURE_TOGGLE. Fixes BR#1843585, reported by Lee.
+ *
+ * Revision 1.160 2007/11/29 18:00:29 fabiankeil
+ * Plug memory leak. Spotted by Valgrind, triggered by
+ * Privoxy-Regression-Test feeding proxyfuzz.py.
+ *
+ * Revision 1.159 2007/11/24 14:34:09 fabiankeil
+ * In the HTTP snipplets, refer to the client as client.
+ *
+ * Revision 1.158 2007/11/11 16:44:17 fabiankeil
+ * Emit a log message when activating the MS IIS5 hack.
+ *
* Revision 1.157 2007/11/03 17:34:49 fabiankeil
* Log the "weak randomization factor" warning only
* once for mingw32 and provide some more details.
#include "filters.h"
#include "loaders.h"
#include "parsers.h"
-#include "killpopup.h"
#include "miscutil.h"
#include "errlog.h"
#include "jbsockets.h"
static jb_err change_request_destination(struct client_state *csp);
static void chat(struct client_state *csp);
static void serve(struct client_state *csp);
-#if defined(unix)
+#if !defined(_WIN32) || defined(_WIN_CONSOLE)
static void usage(const char *myname);
#endif
static void initialize_mutexes(void);
"Proxy-Agent: Privoxy/" VERSION "\r\n\r\n";
static const char CHEADER[] =
- "HTTP/1.0 400 Invalid header received from browser\r\n"
+ "HTTP/1.0 400 Invalid header received from client\r\n"
"Proxy-Agent: Privoxy " VERSION "\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
- "Invalid header received from browser.\r\n";
-
-static const char CFORBIDDEN[] =
- "HTTP/1.0 403 Connection not allowable\r\n"
- "Proxy-Agent: Privoxy " VERSION "\r\n"
- "X-Hint: If you read this message interactively, then you know why this happens ,-)\r\n"
- "Connection: close\r\n\r\n";
+ "Invalid header received from client.\r\n";
static const char FTP_RESPONSE[] =
- "HTTP/1.0 400 Invalid request received from browser\r\n"
+ "HTTP/1.0 400 Invalid request received from client\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
"Invalid request. Privoxy doesn't support FTP.\r\n";
static const char GOPHER_RESPONSE[] =
- "HTTP/1.0 400 Invalid request received from browser\r\n"
+ "HTTP/1.0 400 Invalid request received from client\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
"Invalid request. Privoxy doesn't support gopher.\r\n";
/* XXX: should be a template */
static const char MISSING_DESTINATION_RESPONSE[] =
- "HTTP/1.0 400 Bad request received from browser\r\n"
+ "HTTP/1.0 400 Bad request received from client\r\n"
"Proxy-Agent: Privoxy " VERSION "\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
"Empty server or forwarder response.\r\n"
- "The connection was closed without sending any data.\r\n";
+ "The connection has been closed but Privoxy didn't receive any data.\r\n";
+
+/* XXX: should be a template */
+static const char INVALID_SERVER_HEADERS_RESPONSE[] =
+ "HTTP/1.0 502 Server or forwarder response invalid\r\n"
+ "Proxy-Agent: Privoxy " VERSION "\r\n"
+ "Content-Type: text/plain\r\n"
+ "Connection: close\r\n\r\n"
+ "Bad response. The server or forwarder response doesn't look like HTTP.\r\n";
#if 0
/* XXX: should be a template */
static const char NULL_BYTE_RESPONSE[] =
- "HTTP/1.0 400 Bad request received from browser\r\n"
+ "HTTP/1.0 400 Bad request received from client\r\n"
"Proxy-Agent: Privoxy " VERSION "\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
*********************************************************************/
static int client_protocol_is_unsupported(const struct client_state *csp, char *req)
{
- char buf[BUFFER_SIZE];
-
/*
* If it's a FTP or gopher request, we don't support it.
*
*/
if (!strncmpic(req, "GET ftp://", 10) || !strncmpic(req, "GET gopher://", 13))
{
+ const char *response = NULL;
+ const char *protocol = NULL;
+
if (!strncmpic(req, "GET ftp://", 10))
{
- strlcpy(buf, FTP_RESPONSE, sizeof(buf));
- log_error(LOG_LEVEL_ERROR, "%s tried to use Privoxy as FTP proxy: %s",
- csp->ip_addr_str, req);
+ response = FTP_RESPONSE;
+ protocol = "FTP";
}
else
{
- strlcpy(buf, GOPHER_RESPONSE, sizeof(buf));
- log_error(LOG_LEVEL_ERROR, "%s tried to use Privoxy as gopher proxy: %s",
- csp->ip_addr_str, req);
+ response = GOPHER_RESPONSE;
+ protocol = "GOPHER";
}
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, req);
+ log_error(LOG_LEVEL_ERROR,
+ "%s tried to use Privoxy as %s proxy: %s",
+ csp->ip_addr_str, protocol, req);
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, req);
freez(req);
- write_socket(csp->cfd, buf, strlen(buf));
+ write_socket(csp->cfd, response, strlen(response));
return TRUE;
}
return JB_ERR_PARSE;
}
- /* Enlist header */
if (JB_ERR_MEMORY == enlist(csp->headers, header))
{
/*
struct http_request *http;
int len; /* for buffer sizes (and negative error codes) */
jb_err err;
-#ifdef FEATURE_KILL_POPUPS
- int block_popups_now = 0; /* bool, 1==currently blocking popups */
-#endif /* def FEATURE_KILL_POPUPS */
/* Function that does the content filtering for the current request */
filter_function_ptr content_filter = NULL;
if (len <= 0)
{
log_error(LOG_LEVEL_ERROR, "read from client failed: %E");
+ destroy_list(headers);
return;
}
*/
if (add_to_iob(csp, buf, len))
{
+ destroy_list(headers);
return;
}
continue;
* An error response has already been send
* and we're done here.
*/
- return;
+ return;
}
}
else
#endif /* ndef FEATURE_TOGGLE */
{
- url_actions(http, csp);
+ get_url_actions(csp, http);
}
/*
}
/* decide how to route the HTTP request */
- if (NULL == (fwd = forward_url(http, csp)))
+ fwd = forward_url(csp, http);
+ if (NULL == fwd)
{
log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!? This can't happen!");
/* Never get here - LOG_LEVEL_FATAL causes program exit */
*
*/
- /*
- * Check if a CONNECT request is allowable:
- * In the absence of a +limit-connect action, allow only port 443.
- * If there is an action, allow whatever matches the specificaton.
- */
- if(http->ssl)
+ if (http->ssl && connect_port_is_forbidden(csp))
{
- if( ( !(csp->action->flags & ACTION_LIMIT_CONNECT) && csp->http->port != 443)
- || (csp->action->flags & ACTION_LIMIT_CONNECT
- && !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) )
- {
- if (csp->action->flags & ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS)
- {
- /*
- * The response may confuse some clients,
- * but makes unblocking easier.
- *
- * XXX: It seems to work with all major browsers,
- * so we should consider returning a body by default someday ...
- */
- log_error(LOG_LEVEL_INFO, "Request from %s marked for blocking. "
- "limit-connect{%s} doesn't allow CONNECT requests to port %d.",
- csp->ip_addr_str, csp->action->string[ACTION_STRING_LIMIT_CONNECT],
- csp->http->port);
- csp->action->flags |= ACTION_BLOCK;
- http->ssl = 0;
- }
- else
- {
- write_socket(csp->cfd, CFORBIDDEN, strlen(CFORBIDDEN));
- log_error(LOG_LEVEL_INFO, "Request from %s denied. "
- "limit-connect{%s} doesn't allow CONNECT requests to port %d.",
- csp->ip_addr_str, csp->action->string[ACTION_STRING_LIMIT_CONNECT],
- csp->http->port);
- assert(NULL != csp->http->ocmd);
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 403 0", csp->ip_addr_str, csp->http->ocmd);
-
- list_remove_all(csp->headers);
- /*
- * XXX: For consistency we might want to log a crunch message here.
- */
- return;
- }
- }
+ const char *acceptable_connect_ports =
+ csp->action->string[ACTION_STRING_LIMIT_CONNECT];
+ assert(NULL != acceptable_connect_ports);
+ log_error(LOG_LEVEL_INFO, "Request from %s marked for blocking. "
+ "limit-connect{%s} doesn't allow CONNECT requests to port %d.",
+ csp->ip_addr_str, acceptable_connect_ports, csp->http->port);
+ csp->action->flags |= ACTION_BLOCK;
+ http->ssl = 0;
}
if (http->ssl == 0)
*/
if (write_socket(csp->sfd, hdr, strlen(hdr))
- || (flush_socket(csp->sfd, csp) < 0))
+ || (flush_socket(csp->sfd, csp->iob) < 0))
{
log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E",
http->hostport);
*/
buf[len] = '\0';
-#ifdef FEATURE_KILL_POPUPS
- /* Filter the popups on this read. */
- if (block_popups_now)
- {
- filter_popups(buf, csp);
- }
-#endif /* def FEATURE_KILL_POPUPS */
-
/* Normally, this would indicate that we've read
* as much as the server has sent us and we can
* close the client connection. However, Microsoft
* This is NOT the body, so
* Let's pretend the server just sent us a blank line.
*/
- log_error(LOG_LEVEL_INFO,
- "Malformerd HTTP headers detected and MS IIS5 hack enabled. "
- "Expect an invalid response or even no response at all.");
snprintf(buf, sizeof(buf), "\r\n");
len = (int)strlen(buf);
hdrlen = strlen(hdr);
if (write_socket(csp->cfd, hdr, hdrlen)
- || ((flushed = flush_socket(csp->cfd, csp)) < 0)
+ || ((flushed = flush_socket(csp->cfd, csp->iob)) < 0)
|| (write_socket(csp->cfd, buf, (size_t)len)))
{
log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E");
* and there isn't anything
* we can do about it.
*/
+ log_error(LOG_LEVEL_INFO,
+ "MS IIS5 hack didn't produce valid headers.");
break;
}
else
return;
}
+ assert(csp->headers->first->str);
+ assert(!http->ssl);
+ if (strncmpic(csp->headers->first->str, "HTTP", 4))
+ {
+ /*
+ * It doesn't look like a HTTP response:
+ * tell the client and log the problem.
+ */
+ if (strlen(csp->headers->first->str) > 30)
+ {
+ csp->headers->first->str[30] = '\0';
+ }
+ log_error(LOG_LEVEL_ERROR,
+ "Invalid server or forwarder response. Starts with: %s",
+ csp->headers->first->str);
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+ write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ free_http_request(http);
+ return;
+ }
+
/* we have now received the entire header.
* filter it and send the result to the client
*/
if (!http->ssl) /* We talk plaintext */
{
-
-#ifdef FEATURE_KILL_POPUPS
- /* Start blocking popups if appropriate. */
- if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */
- (csp->action->flags & ACTION_NO_POPUPS) != 0) /* Policy allows */
- {
- block_popups_now = 1;
- /*
- * Filter the part of the body that came in the same read
- * as the last headers:
- */
- filter_popups(csp->iob->cur, csp);
- }
-#endif /* def FEATURE_KILL_POPUPS */
content_filter = get_filter_function(csp);
}
/*
*/
if (write_socket(csp->cfd, hdr, strlen(hdr))
- || ((len = flush_socket(csp->cfd, csp)) < 0))
+ || ((len = flush_socket(csp->cfd, csp->iob)) < 0))
{
log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
*/
if (ms_iis5_hack)
{
+ log_error(LOG_LEVEL_INFO,
+ "Closed server connection detected with MS IIS5 hack enabled.");
break;
}
}
#endif
-#if defined(unix)
+#if !defined(_WIN32) || defined(_WIN_CONSOLE)
/*********************************************************************
*
* Function : usage
exit(2);
}
-#endif /* defined(unix) */
+#endif /* #if !defined(_WIN32) || defined(_WIN_CONSOLE) */
/*********************************************************************
* Parse the command line arguments
*
* XXX: simply printing usage information in case of
- * invalid arguments isn't particular user friendly.
+ * invalid arguments isn't particularly user friendly.
*/
while (++argc_pos < argc)
{
int result;
jb_socket bfd;
- if ( (config->haddr != NULL)
- && (config->haddr[0] == '1')
- && (config->haddr[1] == '2')
- && (config->haddr[2] == '7')
- && (config->haddr[3] == '.') )
- {
- log_error(LOG_LEVEL_INFO, "Listening on port %d for local connections only",
- config->hport);
- }
- else if (config->haddr == NULL)
+ if (config->haddr == NULL)
{
log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses",
config->hport);
#ifdef FEATURE_TOGGLE
if (global_toggle_state)
+#endif /* def FEATURE_TOGGLE */
{
csp->flags |= CSP_FLAG_TOGGLED_ON;
}
-#endif /* def FEATURE_TOGGLE */
if (run_loader(csp))
{