-const char jcc_rcs[] = "$Id: jcc.c,v 1.67 2002/03/04 18:18:57 oes Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.106 2006/11/06 19:58:23 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
* the main connection-handling function.
*
* Copyright : Written by and Copyright (C) 2001 the SourceForge
- * IJBSWA team. http://ijbswa.sourceforge.net
+ * Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
* by and Copyright (C) 1997 Anonymous Coders and
*
* Revisions :
* $Log: jcc.c,v $
+ * Revision 1.106 2006/11/06 19:58:23 fabiankeil
+ * Move pthread.h inclusion from jcc.c to jcc.h.
+ * Fixes build on x86-freebsd1 (FreeBSD 5.4-RELEASE).
+ *
+ * Revision 1.105 2006/11/06 14:26:02 fabiankeil
+ * Don't exit after receiving the second SIGHUP on Solaris.
+ *
+ * Fixes BR 1052235, but the same problem may exist on other
+ * systems. Once 3.0.6 is out we should use sigset()
+ * where available and see if it breaks anything.
+ *
+ * Revision 1.104 2006/09/23 13:26:38 roro
+ * Replace TABs by spaces in source code.
+ *
+ * Revision 1.103 2006/09/21 12:54:43 fabiankeil
+ * Fix +redirect{}. Didn't work with -fast-redirects.
+ *
+ * Revision 1.102 2006/09/06 13:03:04 fabiankeil
+ * Respond with 400 and a short text message
+ * if the client tries to use Privoxy as FTP proxy.
+ *
+ * Revision 1.101 2006/09/06 09:23:37 fabiankeil
+ * Make number of retries in case of forwarded-connect problems
+ * a config file option (forwarded-connect-retries) and use 0 as
+ * default.
+ *
+ * Revision 1.100 2006/09/03 19:42:59 fabiankeil
+ * Set random(3) seed.
+ *
+ * Revision 1.99 2006/09/02 15:36:42 fabiankeil
+ * Follow the OpenBSD port's lead and protect the resolve
+ * functions on OpenBSD as well.
+ *
+ * Revision 1.98 2006/08/24 11:01:34 fabiankeil
+ * --user fix. Only use the user as group if no group is specified.
+ * Solves BR 1492612. Thanks to Spinor S. and David Laight.
+ *
+ * Revision 1.97 2006/08/18 15:23:17 david__schmidt
+ * Windows service (re-)integration
+ *
+ * The new args are:
+ *
+ * --install[:service_name]
+ * --uninstall[:service_name]
+ * --service
+ *
+ * They work as follows:
+ * --install will create a service for you and then terminate.
+ * By default the service name will be "privoxy" (without the quotes).
+ * However you can run multiple services if you wish, just by adding
+ * a colon and then a name (no spaces).
+ *
+ * --uninstall follows the exact same rules a --install.
+ *
+ * --service is used when the program is executed by the service
+ * control manager, and in normal circumstances would never be
+ * used as a command line argument.
+ *
+ * Revision 1.96 2006/08/15 20:12:36 david__schmidt
+ * Windows service integration
+ *
+ * Revision 1.95 2006/08/03 02:46:41 david__schmidt
+ * Incorporate Fabian Keil's patch work:\rhttp://www.fabiankeil.de/sourcecode/privoxy/
+ *
+ * Revision 1.94 2006/07/18 14:48:46 david__schmidt
+ * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ * with what was really the latest development (the v_3_0_branch branch)
+ *
+ * Revision 1.92.2.16 2005/04/03 20:10:50 david__schmidt
+ * Thanks to Jindrich Makovicka for a race condition fix for the log
+ * file. The race condition remains for non-pthread implementations.
+ * Reference patch #1175720.
+ *
+ * Revision 1.92.2.15 2004/10/03 12:53:32 david__schmidt
+ * Add the ability to check jpeg images for invalid
+ * lengths of comment blocks. Defensive strategy
+ * against the exploit:
+ * Microsoft Security Bulletin MS04-028
+ * Buffer Overrun in JPEG Processing (GDI+) Could
+ * Allow Code Execution (833987)
+ * Enabled with +inspect-jpegs in actions files.
+ *
+ * Revision 1.92.2.14 2003/12/12 12:52:53 oes
+ * - Fixed usage info for non-unix platforms
+ * - Fixed small cmdline parsing bug
+ *
+ * Revision 1.92.2.13 2003/11/27 19:20:27 oes
+ * Diagnostics: Now preserve the returncode of pthread_create
+ * in errno. Closes BR #775721. Thanks to Geoffrey Hausheer.
+ *
+ * Revision 1.92.2.12 2003/07/11 11:34:19 oes
+ * No longer ignore SIGCHLD. Fixes bug #769381
+ *
+ * Revision 1.92.2.11 2003/05/14 12:32:02 oes
+ * Close jarfile on graceful exit, remove stray line
+ *
+ * Revision 1.92.2.10 2003/05/08 15:13:46 oes
+ * Cosmetics: Killed a warning, a typo and an allocation left at exit
+ *
+ * Revision 1.92.2.9 2003/04/03 15:08:42 oes
+ * No longer rely on non-POSIX.1 extensions of getcwd().
+ * Fixes bug #711001
+ *
+ * Revision 1.92.2.8 2003/03/31 13:12:32 oes
+ * Replaced setenv() by posix-compliant putenv()
+ * Thanks to Neil McCalden (nmcc AT users.sf.net).
+ *
+ * Revision 1.92.2.7 2003/03/17 16:48:59 oes
+ * Added chroot ability, thanks to patch by Sviatoslav Sviridov
+ *
+ * Revision 1.92.2.6 2003/03/11 11:55:00 oes
+ * Clean-up and extension of improvements for forked mode:
+ * - Child's return code now consists of flags RC_FLAG_*
+ * - Reporting toggle to parent now properly #ifdef'ed
+ * - Children now report blocking to parent. This enables
+ * statistics in forked mode
+ *
+ * Revision 1.92.2.5 2003/03/10 23:45:32 oes
+ * Fixed bug #700381: Non-Threaded version now capable of being toggled.
+ * Children now report having been toggled through _exit(17), parents
+ * watch for that code and toggle themselves if found.
+ *
+ * Revision 1.92.2.4 2003/03/07 03:41:04 david__schmidt
+ * Wrapping all *_r functions (the non-_r versions of them) with
+ * mutex semaphores for OSX. Hopefully this will take care of all
+ * of those pesky crash reports.
+ *
+ * Revision 1.92.2.3 2003/02/28 12:53:06 oes
+ * Fixed two mostly harmless mem leaks
+ *
+ * Revision 1.92.2.2 2002/11/20 14:37:47 oes
+ * Fix: Head of global clients list now initialized to NULL
+ *
+ * Revision 1.92.2.1 2002/09/25 14:52:24 oes
+ * Added basic support for OPTIONS and TRACE HTTP methods:
+ * - New interceptor direct_response() added in chat().
+ * - sed() moved to earlier in the process, so that the
+ * Host: header is evaluated before actions and forwarding
+ * are decided on.
+ *
+ * Revision 1.92 2002/05/08 16:00:46 oes
+ * Chat's buffer handling:
+ * - Fixed bug with unchecked out-of-mem conditions
+ * while reading client request & server headers
+ * - No longer predict if the buffer limit will be exceeded
+ * in the next read -- check add_to_iob's new
+ * return code. If buffer couldn't be extended
+ * (policy or out-of-mem) while
+ * - reading from client: abort
+ * - reading server headers: send error page
+ * - buffering server body for filter: flush,
+ * and if that fails: send error page
+ *
+ * Revision 1.91 2002/04/08 20:35:58 swa
+ * fixed JB spelling
+ *
+ * Revision 1.90 2002/04/02 14:57:28 oes
+ * Made sending wafers independent of FEATURE_COOKIE_JAR
+ *
+ * Revision 1.89 2002/03/31 17:18:59 jongfoster
+ * Win32 only: Enabling STRICT to fix a VC++ compile warning.
+ *
+ * Revision 1.88 2002/03/27 14:32:43 david__schmidt
+ * More compiler warning message maintenance
+ *
+ * Revision 1.87 2002/03/26 22:29:54 swa
+ * we have a new homepage!
+ *
+ * Revision 1.86 2002/03/25 17:04:55 david__schmidt
+ * Workaround for closing the jarfile before load_config() comes around again
+ *
+ * Revision 1.85 2002/03/24 15:23:33 jongfoster
+ * Name changes
+ *
+ * Revision 1.84 2002/03/24 13:25:43 swa
+ * name change related issues
+ *
+ * Revision 1.83 2002/03/16 23:54:06 jongfoster
+ * Adding graceful termination feature, to help look for memory leaks.
+ * If you enable this (which, by design, has to be done by hand
+ * editing config.h) and then go to http://i.j.b/die, then the program
+ * will exit cleanly after the *next* request. It should free all the
+ * memory that was used.
+ *
+ * Revision 1.82 2002/03/13 00:27:05 jongfoster
+ * Killing warnings
+ *
+ * Revision 1.81 2002/03/12 01:42:50 oes
+ * Introduced modular filters
+ *
+ * Revision 1.80 2002/03/11 22:07:05 david__schmidt
+ * OS/2 port maintenance:
+ * - Fixed EMX build - it had decayed a little
+ * - Fixed inexplicable crash during FD_ZERO - must be due to a bad macro.
+ * substituted a memset for now.
+ *
+ * Revision 1.79 2002/03/09 20:03:52 jongfoster
+ * - Making various functions return int rather than size_t.
+ * (Undoing a recent change). Since size_t is unsigned on
+ * Windows, functions like read_socket that return -1 on
+ * error cannot return a size_t.
+ *
+ * THIS WAS A MAJOR BUG - it caused frequent, unpredictable
+ * crashes, and also frequently caused JB to jump to 100%
+ * CPU and stay there. (Because it thought it had just
+ * read ((unsigned)-1) == 4Gb of data...)
+ *
+ * - The signature of write_socket has changed, it now simply
+ * returns success=0/failure=nonzero.
+ *
+ * - Trying to get rid of a few warnings --with-debug on
+ * Windows, I've introduced a new type "jb_socket". This is
+ * used for the socket file descriptors. On Windows, this
+ * is SOCKET (a typedef for unsigned). Everywhere else, it's
+ * an int. The error value can't be -1 any more, so it's
+ * now JB_INVALID_SOCKET (which is -1 on UNIX, and in
+ * Windows it maps to the #define INVALID_SOCKET.)
+ *
+ * - The signature of bind_port has changed.
+ *
+ * Revision 1.78 2002/03/08 21:35:04 oes
+ * Added optional group supplement to --user option. Will now use default group of user if no group given
+ *
+ * Revision 1.77 2002/03/07 03:52:06 oes
+ * - Fixed compiler warnings etc
+ * - Improved handling of failed DNS lookups
+ *
+ * Revision 1.76 2002/03/06 22:54:35 jongfoster
+ * Automated function-comment nitpicking.
+ *
+ * Revision 1.75 2002/03/06 10:02:19 oes
+ * Fixed stupid bug when --user was not given
+ *
+ * Revision 1.74 2002/03/06 00:49:31 jongfoster
+ * Fixing warning on Windows
+ * Making #ifdefs that refer to the same variable consistently
+ * use #ifdef unix rather than mixing #ifdef unix & #ifndef OS2
+ *
+ * Revision 1.73 2002/03/05 23:57:30 hal9
+ * Stray character 's' on line 1618 was breaking build.
+ *
+ * Revision 1.72 2002/03/05 21:33:45 david__schmidt
+ * - Re-enable OS/2 building after new parms were added
+ * - Fix false out of memory report when resolving CGI templates when no IP
+ * address is available of failed attempt (a la no such domain)
+ *
+ * Revision 1.71 2002/03/05 18:13:56 oes
+ * Added --user option
+ *
+ * Revision 1.70 2002/03/05 04:52:42 oes
+ * Deleted non-errlog debugging code
+ *
+ * Revision 1.69 2002/03/04 23:50:00 jongfoster
+ * Splitting off bind_port() call into bind_port_helper(), with
+ * improved logging.
+ *
+ * Revision 1.68 2002/03/04 20:17:32 oes
+ * Fixed usage info
+ *
* Revision 1.67 2002/03/04 18:18:57 oes
* - Removed _DEBUG mode
* - Cleand up cmdline parsing
#include <fcntl.h>
#include <errno.h>
-#ifdef FEATURE_PTHREAD
-#include <pthread.h>
-#endif /* def FEATURE_PTHREAD */
-
#ifdef _WIN32
# ifndef FEATURE_PTHREAD
+# ifndef STRICT
+# define STRICT
+# endif
# include <windows.h>
# include <process.h>
# endif /* ndef FEATURE_PTHREAD */
# ifndef _WIN_CONSOLE
# include "w32log.h"
# endif /* ndef _WIN_CONSOLE */
+# include "w32svrapi.h"
#else /* ifndef _WIN32 */
#include <sys/termios.h>
#endif /* sun */
+#ifdef unix
+#include <pwd.h>
+#include <grp.h>
+#endif
+
# include <signal.h>
# ifdef __BEOS__
int urls_rejected = 0; /* total nr of urls rejected */
#endif /* def FEATURE_STATISTICS */
+#ifdef FEATURE_GRACEFUL_TERMINATION
+int g_terminate = 0;
+#endif
static void listen_loop(void);
static void chat(struct client_state *csp);
#define sleep(N) DosSleep(((N) * 100))
#endif
-#if defined(unix)
-const char *basedir;
+#ifdef FEATURE_PTHREAD
+pthread_mutex_t log_mutex;
+pthread_mutex_t log_init_mutex;
+
+#ifndef HAVE_GMTIME_R
+pthread_mutex_t gmtime_mutex;
+#endif /* ndef HAVE_GMTIME_R */
+
+#ifndef HAVE_LOCALTIME_R
+pthread_mutex_t localtime_mutex;
+#endif /* ndef HAVE_GMTIME_R */
+
+#ifndef HAVE_GETHOSTBYADDR_R
+pthread_mutex_t gethostbyaddr_mutex;
+#endif /* ndef HAVE_GETHOSTBYADDR_R */
+
+#ifndef HAVE_GETHOSTBYNAME_R
+pthread_mutex_t gethostbyname_mutex;
+#endif /* ndef HAVE_GETHOSTBYNAME_R */
+#endif /* FEATURE_PTHREAD */
+
+#if defined(unix) || defined(__EMX__)
+const char *basedir = NULL;
const char *pidfile = NULL;
int received_hup_signal = 0;
#endif /* defined unix */
* to be reopened by the main thread on HUP.
*
* Parameters :
- * 1 : the_signal - the signal cause this function to call
+ * 1 : the_signal = the signal cause this function to call
*
* Returns : -
*
#define IS_ENABLED_AND IS_TOGGLED_ON_AND IS_NOT_FORCED_AND
char buf[BUFFER_SIZE];
- char *hdr, *p, *req;
+ char *hdr;
+ char *p;
+ char *req;
fd_set rfds;
- int n, maxfd, server_body;
+ int n;
+ jb_socket maxfd;
+ int server_body;
int ms_iis5_hack = 0;
int byte_count = 0;
+ unsigned int forwarded_connect_retries = 0;
+ unsigned int max_forwarded_connect_retries = csp->config->forwarded_connect_retries;
const struct forward_spec * fwd;
struct http_request *http;
+ int len; /* for buffer sizes */
#ifdef FEATURE_KILL_POPUPS
int block_popups; /* bool, 1==will block popups */
int block_popups_now = 0; /* bool, 1==currently blocking popups */
int pcrs_filter; /* bool, 1==will filter through pcrs */
int gif_deanimate; /* bool, 1==will deanimate gifs */
+ int jpeg_inspect; /* bool, 1==will inspect jpegs */
/* Function that does the content filtering for the current request */
char *(*content_filter)() = NULL;
* could get blocked here if a client connected, then didn't say anything!
*/
- while (FOREVER)
+ for (;;)
{
- n = read_socket(csp->cfd, buf, sizeof(buf));
-
- if (n <= 0) break; /* error! */
+ len = read_socket(csp->cfd, buf, sizeof(buf));
- add_to_iob(csp, buf, n);
+ if (len <= 0) break; /* error! */
+
+ /*
+ * If there is no memory left for buffering the
+ * request, there is nothing we can do but hang up
+ */
+ if (add_to_iob(csp, buf, len))
+ {
+ return;
+ }
req = get_header(csp);
log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 400 0", csp->ip_addr_str);
+ free_http_request(http);
+ return;
+ }
+
+ if (!strncmpic(http->cmd, "GET ftp://", 10))
+ {
+ strcpy(buf, FTP_RESPONSE);
+ write_socket(csp->cfd, buf, strlen(buf));
+
+ log_error(LOG_LEVEL_ERROR, "%s tried to use Privoxy as FTP proxy: %s",
+ csp->ip_addr_str, http->cmd);
+
+ free_http_request(http);
return;
}
|| (csp->action->flags & ACTION_LIMIT_CONNECT
&& !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) )
{
- strcpy(buf, CFORBIDDEN);
- write_socket(csp->cfd, buf, strlen(buf));
-
- log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str);
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str);
-
- return;
+ if (csp->action->flags & ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS)
+ {
+ /* The response will violate the specs, 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;
+ http->ssl = 0;
+ }
+ else
+ {
+ strcpy(buf, CFORBIDDEN);
+ write_socket(csp->cfd, buf, strlen(buf));
+ log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str);
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str);
+ return;
+ }
}
}
{
string_append(&http->cmd, http->path);
}
-
string_append(&http->cmd, " ");
string_append(&http->cmd, http->ver);
if (http->cmd == NULL)
{
- log_error(LOG_LEVEL_FATAL, "Out of memory rewiting SSL command");
+ log_error(LOG_LEVEL_FATAL, "Out of memory writing HTTP command");
}
+ log_error(LOG_LEVEL_HEADER, "New HTTP Request-Line: %s", http->cmd);
}
enlist(csp->headers, http->cmd);
-#ifdef FEATURE_COOKIE_JAR
/*
- * If we're logging cookies in a cookie jar, and the user has not
- * supplied any wafers, and the user has not told us to suppress the
- * vanilla wafer, then send the vanilla wafer.
+ * If the user has not supplied any wafers, and the user has not
+ * told us to suppress the vanilla wafer, then send the vanilla wafer.
*/
- if ((csp->config->jarfile != NULL)
- && list_is_empty(csp->action->multi[ACTION_MULTI_WAFER])
+ if (list_is_empty(csp->action->multi[ACTION_MULTI_WAFER])
&& ((csp->action->flags & ACTION_VANILLA_WAFER) != 0))
{
enlist(csp->action->multi[ACTION_MULTI_WAFER], VANILLA_WAFER);
}
-#endif /* def FEATURE_COOKIE_JAR */
#ifdef FEATURE_KILL_POPUPS
#endif /* def FEATURE_KILL_POPUPS */
pcrs_filter = (csp->rlist != NULL) && /* There are expressions to be used */
- ((csp->action->flags & ACTION_FILTER) != 0);
+ (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]));
gif_deanimate = ((csp->action->flags & ACTION_DEANIMATE) != 0);
+ jpeg_inspect = ((csp->action->flags & ACTION_JPEG_INSPECT) != 0);
+
/* grab the rest of the client's headers */
- while (FOREVER)
+ for (;;)
{
- if ( ( p = get_header(csp) ) && ( *p == '\0' ) )
+ if ( ( ( p = get_header(csp) ) != NULL) && ( *p == '\0' ) )
{
- n = read_socket(csp->cfd, buf, sizeof(buf));
- if (n <= 0)
+ len = read_socket(csp->cfd, buf, sizeof(buf));
+ if (len <= 0)
{
log_error(LOG_LEVEL_ERROR, "read from client failed: %E");
return;
}
- add_to_iob(csp, buf, n);
+
+ /*
+ * If there is no memory left for buffering the
+ * request, there is nothing we can do but hang up
+ */
+ if (add_to_iob(csp, buf, len))
+ {
+ return;
+ }
continue;
}
enlist(csp->headers, p);
freez(p);
}
+
/*
* We have a request. Now, check to see if we need to
* intercept it, i.e. If ..
*/
if (
- /* a CGI call was detected and answered */
- (NULL != (rsp = dispatch_cgi(csp)))
+ /* We may not forward the request by rfc2616 sect 14.31 */
+ (NULL != (rsp = direct_response(csp)))
+
+ /* or a CGI call was detected and answered */
+ || (NULL != (rsp = dispatch_cgi(csp)))
/* or we are enabled and... */
|| (IS_ENABLED_AND (
/* ..or a fast redirect kicked in */
#ifdef FEATURE_FAST_REDIRECTS
- || (((csp->action->flags & ACTION_FAST_REDIRECTS) != 0) &&
- (NULL != (rsp = redirect_url(csp))))
+ || ( NULL != (rsp = redirect_url(csp)))
#endif /* def FEATURE_FAST_REDIRECTS */
))
)
{
/* Write the answer to the client */
- if ((write_socket(csp->cfd, rsp->head, rsp->head_length) != rsp->head_length)
- || (write_socket(csp->cfd, rsp->body, rsp->content_length) != rsp->content_length))
+ 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);
}
csp->flags |= CSP_FLAG_REJECTED;
#endif /* def FEATURE_STATISTICS */
- /* Log (FIXME: All intercept reasons apprear as "crunch" with Status 200) */
+ /* 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);
return;
}
+ hdr = sed(client_patterns, add_client_headers, csp);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header");
+ }
+
+ list_remove_all(csp->headers);
+
log_error(LOG_LEVEL_GPC, "%s%s", http->hostport, http->path);
if (fwd->forward_host)
/* here we connect to the server, gateway, or the forwarder */
- csp->sfd = forwarded_connect(fwd, http, csp);
+ while ( (csp->sfd = forwarded_connect(fwd, http, csp))
+ && (errno == EINVAL) && (forwarded_connect_retries++ < max_forwarded_connect_retries))
+ {
+ log_error(LOG_LEVEL_ERROR, "failed request #%u to connect to %s. Trying again.",
+ forwarded_connect_retries, http->hostport);
+ }
- if (csp->sfd < 0)
+ if (csp->sfd == JB_INVALID_SOCKET)
{
log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E",
http->hostport);
/* Write the answer to the client */
if(rsp)
{
- if ((write_socket(csp->cfd, rsp->head, rsp->head_length) != rsp->head_length)
- || (write_socket(csp->cfd, rsp->body, rsp->content_length) != rsp->content_length))
+ 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);
}
}
free_http_response(rsp);
+ freez(hdr);
return;
}
log_error(LOG_LEVEL_CONNECT, "OK");
- hdr = sed(client_patterns, add_client_headers, csp);
- if (hdr == NULL)
- {
- /* FIXME Should handle error properly */
- log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header");
- }
-
- list_remove_all(csp->headers);
-
if (fwd->forward_host || (http->ssl == 0))
{
/* write the client's (modified) header to the server
* (along with anything else that may be in the buffer)
*/
- n = strlen(hdr);
-
- if ((write_socket(csp->sfd, hdr, n) != n)
- || (flush_socket(csp->sfd, csp ) < 0))
+ if (write_socket(csp->sfd, hdr, strlen(hdr))
+ || (flush_socket(csp->sfd, csp) < 0))
{
log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E",
http->hostport);
if(rsp)
{
- if ((write_socket(csp->cfd, rsp->head, rsp->head_length) != rsp->head_length)
- || (write_socket(csp->cfd, rsp->body, rsp->content_length) != rsp->content_length))
+ 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);
}
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) < 0)
+ if (write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1))
{
freez(hdr);
return;
server_body = 0;
- while (FOREVER)
+ for (;;)
{
+#ifdef __OS2__
+ /*
+ * FD_ZERO here seems to point to an errant macro which crashes.
+ * So do this by hand for now...
+ */
+ memset(&rfds,0x00,sizeof(fd_set));
+#else
FD_ZERO(&rfds);
-
+#endif
FD_SET(csp->cfd, &rfds);
FD_SET(csp->sfd, &rfds);
- n = select(maxfd+1, &rfds, NULL, NULL, NULL);
+ n = select((int)maxfd+1, &rfds, NULL, NULL, NULL);
if (n < 0)
{
if (FD_ISSET(csp->cfd, &rfds))
{
- n = read_socket(csp->cfd, buf, sizeof(buf));
+ len = read_socket(csp->cfd, buf, sizeof(buf));
- if (n <= 0)
+ if (len <= 0)
{
break; /* "game over, man" */
}
- if (write_socket(csp->sfd, buf, n) != n)
+ if (write_socket(csp->sfd, buf, (size_t)len))
{
log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
return;
if (FD_ISSET(csp->sfd, &rfds))
{
fflush( 0 );
- n = read_socket(csp->sfd, buf, sizeof(buf) - 1);
+ len = read_socket(csp->sfd, buf, sizeof(buf) - 1);
- if (n < 0)
+ if (len < 0)
{
log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
if(rsp)
{
- if ((write_socket(csp->cfd, rsp->head, rsp->head_length) != rsp->head_length)
- || (write_socket(csp->cfd, rsp->body, rsp->content_length) != rsp->content_length))
+ 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);
}
/* Add a trailing zero. This lets filter_popups
* use string operations.
*/
- buf[n] = '\0';
+ buf[len] = '\0';
#ifdef FEATURE_KILL_POPUPS
/* Filter the popups on this read. */
* doesn't generate a valid header, then we won't
* transmit anything to the client.
*/
- if (n == 0)
+ if (len == 0)
{
if (server_body || http->ssl)
csp->content_length = csp->iob->eod - csp->iob->cur;
}
- hdr = sed(server_patterns, add_server_headers, csp);
+ hdr = sed(server_patterns_light, NULL, csp);
+
if (hdr == NULL)
{
/* FIXME Should handle error properly */
log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
}
- n = strlen(hdr);
-
- if ((write_socket(csp->cfd, hdr, n) != n)
- || (write_socket(csp->cfd, p != NULL ? p : csp->iob->cur, csp->content_length) != (int)csp->content_length))
+ if (write_socket(csp->cfd, hdr, strlen(hdr))
+ || write_socket(csp->cfd, p != NULL ? p : csp->iob->cur, csp->content_length))
{
log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
+ freez(hdr);
+ freez(p);
return;
}
freez(hdr);
- if (NULL != p) {
- freez(p);
- }
+ freez(p);
}
break; /* "game over, man" */
* This is NOT the body, so
* Let's pretend the server just sent us a blank line.
*/
- n = sprintf(buf, "\r\n");
+ len = sprintf(buf, "\r\n");
/*
* Now, let the normal header parsing algorithm below do its
{
if (content_filter)
{
- add_to_iob(csp, buf, n);
-
/*
- * If the buffer limit will be reached on the next read,
- * switch to non-filtering mode, i.e. make & write the
- * header, flush the socket and get out of the way.
+ * If there is no memory left for buffering the content, or the buffer limit
+ * has been reached, switch to non-filtering mode, i.e. make & write the
+ * header, flush the iob and buf, and get out of the way.
*/
- if (((size_t)(csp->iob->eod - csp->iob->buf)) + (size_t)BUFFER_SIZE > csp->config->buffer_limit)
+ if (add_to_iob(csp, buf, len))
{
- log_error(LOG_LEVEL_ERROR, "Buffer size limit reached! Flushing and stepping back.");
+ size_t hdrlen;
+ int flushed;
+
+ log_error(LOG_LEVEL_ERROR, "Flushing header and buffers. Stepping back from filtering.");
hdr = sed(server_patterns, add_server_headers, csp);
if (hdr == NULL)
{
- /* FIXME Should handle error properly */
- log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ /*
+ * 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();
+
+ 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;
}
- n = strlen(hdr);
- byte_count += n;
+ hdrlen = strlen(hdr);
- if (((write_socket(csp->cfd, hdr, n) != n)
- || (n = flush_socket(csp->cfd, csp) < 0)))
+ if (write_socket(csp->cfd, hdr, hdrlen)
+ || ((flushed = flush_socket(csp->cfd, csp)) < 0)
+ || (write_socket(csp->cfd, buf, (size_t) len)))
{
- log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
+ log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E");
freez(hdr);
return;
}
+ byte_count += hdrlen + flushed + len;
freez(hdr);
- byte_count += n;
-
content_filter = NULL;
server_body = 1;
}
else
{
- if (write_socket(csp->cfd, buf, n) != n)
+ if (write_socket(csp->cfd, buf, (size_t)len))
{
log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
return;
}
}
- byte_count += n;
+ byte_count += len;
continue;
}
else
* parsing an "out of body experience" ?
*/
- /* buffer up the data we just read */
- add_to_iob(csp, buf, n);
+ /*
+ * buffer up the data we just read. If that fails,
+ * there's little we can do but send our static
+ * out-of-memory page.
+ */
+ if (add_to_iob(csp, buf, len))
+ {
+ 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);
+ }
+ return;
+ }
/* get header lines from the iob */
- while ((p = get_header(csp)))
+ while ((p = get_header(csp)) != NULL)
{
if (*p == '\0')
{
log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
}
- n = strlen(hdr);
-
- /* write the server's (modified) header to
- * the client (along with anything else that
- * may be in the buffer)
- */
-
#ifdef FEATURE_KILL_POPUPS
/* Start blocking popups if appropriate. */
/* Buffer and gif_deanimate this if appropriate. */
- if ((csp->content_type & CT_GIF) && /* It's a image/gif MIME-Type */
+ if ((csp->content_type & CT_GIF) && /* It's an image/gif MIME-Type */
!http->ssl && /* We talk plaintext */
gif_deanimate) /* Policy allows */
{
content_filter = gif_deanimate_response;
}
+ /* Buffer and jpg_inspect this if appropriate. */
+
+ if ((csp->content_type & CT_JPEG) && /* It's an image/jpeg MIME-Type */
+ !http->ssl && /* We talk plaintext */
+ jpeg_inspect) /* Policy allows */
+ {
+ content_filter = jpeg_inspect_response;
+ }
/*
* Only write if we're not buffering for content modification
*/
- if (!content_filter && ((write_socket(csp->cfd, hdr, n) != n)
- || (n = flush_socket(csp->cfd, csp) < 0)))
+ if (!content_filter)
{
- log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
-
- /* the write failed, so don't bother
- * mentioning it to the client...
- * it probably can't hear us anyway.
+ /* write the server's (modified) header to
+ * the client (along with anything else that
+ * may be in the buffer)
*/
- freez(hdr);
- return;
- }
- if(!content_filter) byte_count += n;
+ if (write_socket(csp->cfd, hdr, strlen(hdr))
+ || ((len = flush_socket(csp->cfd, csp)) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
+
+ /* the write failed, so don't bother
+ * mentioning it to the client...
+ * it probably can't hear us anyway.
+ */
+ freez(hdr);
+ return;
+ }
+
+ byte_count += len;
+ }
/* we're finished with the server's header */
chat(csp);
close_socket(csp->cfd);
- if (csp->sfd >= 0)
+ if (csp->sfd != JB_INVALID_SOCKET)
{
close_socket(csp->sfd);
}
*********************************************************************/
void usage(const char *myname)
{
- printf("JunkBuster proxy version " VERSION " (" HOME_PAGE_URL ")\n"
- "Usage: %s [--help] [--version] [--no-daemon] [--pidfile pidfile] [configfile]\n"
+ printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"
+#if !defined(unix)
+ "Usage: %s [--help] [--version] [configfile]\n"
+#else
+ "Usage: %s [--help] [--version] [--no-daemon] [--pidfile pidfile] [--user user[.group]] [configfile]\n"
+#endif
"Aborting.\n", myname);
exit(2);
#endif
{
int argc_pos = 0;
+#ifdef HAVE_RANDOM
+ unsigned int random_seed;
+#endif /* ifdef HAVE_RANDOM */
+#ifdef unix
+ struct passwd *pw = NULL;
+ struct group *grp = NULL;
+ char *p;
+ int do_chroot = 0;
+#endif
Argc = argc;
Argv = argv;
*/
while (++argc_pos < argc)
{
+#ifdef _WIN32
+ /* Check to see if the service must be installed or uninstalled */
+ if (strncmp(argv[argc_pos], "--install", 9) == 0)
+ {
+ const char *pName = argv[argc_pos] + 9;
+ if (*pName == ':')
+ pName++;
+ exit( (install_service(pName)) ? 0 : 1 );
+ }
+ else if (strncmp(argv[argc_pos], "--uninstall", + 11) == 0)
+ {
+ const char *pName = argv[argc_pos] + 11;
+ if (*pName == ':')
+ pName++;
+ exit((uninstall_service(pName)) ? 0 : 1);
+ }
+ else if (strcmp(argv[argc_pos], "--service" ) == 0)
+ {
+ bRunAsService = TRUE;
+ w32_set_service_cwd();
+ atexit(w32_service_exit_notify);
+ }
+ else
+#endif /* defined(_WIN32) */
+
+
#if !defined(_WIN32) || defined(_WIN_CONSOLE)
if (strcmp(argv[argc_pos], "--help") == 0)
else if(strcmp(argv[argc_pos], "--version") == 0)
{
- printf("Junkbuster version " VERSION " (" HOME_PAGE_URL ")\n");
+ printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n");
exit(0);
}
- else if (strcmp(argv[argc_pos], "--no-daemon" ) == 0)
+#if defined(unix)
+
+ else if (strcmp(argv[argc_pos], "--no-daemon" ) == 0)
{
no_daemon = 1;
}
pidfile = strdup(argv[argc_pos]);
}
-#endif /* !defined(_WIN32) || defined(_WIN_CONSOLE) */
+ else if (strcmp(argv[argc_pos], "--user" ) == 0)
+ {
+ if (++argc_pos == argc) usage(argv[argc_pos]);
- else
+ if ((NULL != (p = strchr(argv[argc_pos], '.'))) && *(p + 1) != '0')
+ {
+ *p++ = '\0';
+ if (NULL == (grp = getgrnam(p)))
+ {
+ log_error(LOG_LEVEL_FATAL, "Group %s not found.", p);
+ }
+ }
+
+ if (NULL == (pw = getpwnam(argv[argc_pos])))
+ {
+ log_error(LOG_LEVEL_FATAL, "User %s not found.", argv[argc_pos]);
+ }
+
+ if (p != NULL) *--p = '\0';
+ }
+
+ else if (strcmp(argv[argc_pos], "--chroot" ) == 0)
+ {
+ do_chroot = 1;
+ }
+
+#endif /* defined(unix) */
+#endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
{
configfile = argv[argc_pos];
}
#if defined(unix)
if ( *configfile != '/' )
{
- char *abs_file;
-
- DBG(1, ("configfile before '%s'\n",configfile) );
+ char *abs_file, cwd[1024];
/* make config-filename absolute here */
- if ( !(basedir = getcwd( NULL, 1024 )))
+ if ( !(getcwd(cwd, sizeof(cwd))))
{
perror("get working dir failed");
exit( 1 );
}
- DBG(1, ("working dir '%s'\n",basedir) );
- if ( !(abs_file = malloc( strlen( basedir ) + strlen( configfile ) + 5 )))
+
+ if (!(basedir = strdup(cwd))
+ || (!(abs_file = malloc( strlen( basedir ) + strlen( configfile ) + 5 ))))
{
perror("malloc failed");
exit( 1 );
strcat( abs_file, "/" );
strcat( abs_file, configfile );
configfile = abs_file;
- DBG(1, ("configfile after '%s'\n",configfile) );
}
#endif /* defined unix */
files->next = NULL;
+ clients->next = NULL;
#ifdef AMIGA
InitAmiga();
InitWin32();
#endif
+#ifdef FEATURE_PTHREAD
+ /*
+ * Prepare global mutex semaphores
+ */
+ pthread_mutex_init(&log_mutex,0);
+ pthread_mutex_init(&log_init_mutex,0);
+
+#ifndef HAVE_GMTIME_R
+ pthread_mutex_init(&gmtime_mutex,0);
+#endif /* ndef HAVE_GMTIME_R */
+
+#ifndef HAVE_LOCALTIME_R
+ pthread_mutex_init(&localtime_mutex,0);
+#endif /* ndef HAVE_GMTIME_R */
+
+#ifndef HAVE_GETHOSTBYADDR_R
+ pthread_mutex_init(&gethostbyaddr_mutex,0);
+#endif /* ndef HAVE_GETHOSTBYADDR_R */
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ pthread_mutex_init(&gethostbyname_mutex,0);
+#endif /* ndef HAVE_GETHOSTBYNAME_R */
+#endif /* FEATURE_PTHREAD */
+
+#ifdef HAVE_RANDOM
+ random_seed = (unsigned int)time(NULL);
+ srandom(random_seed);
+#endif /* ifdef HAVE_RANDOM */
+
/*
* Unix signal handling
*
* Catch the abort, interrupt and terminate signals for a graceful exit
* Catch the hangup signal so the errlog can be reopened.
- * Ignore the broken pipe and child signals
- * FIXME: Isn't ignoring the default for SIGCHLD anyway and why ignore SIGPIPE?
+ * Ignore the broken pipe signals (FIXME: Why?)
*/
#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA)
{
int idx;
const int catched_signals[] = { SIGABRT, SIGTERM, SIGINT, SIGHUP, 0 };
- const int ignored_signals[] = { SIGPIPE, SIGCHLD, 0 };
+ const int ignored_signals[] = { SIGPIPE, 0 };
for (idx = 0; catched_signals[idx] != 0; idx++)
{
+#ifdef sun /* FIXME: Is it safe to check for HAVE_SIGSET instead? */
+ if (sigset(catched_signals[idx], sig_handler) == SIG_ERR)
+#else
if (signal(catched_signals[idx], sig_handler) == SIG_ERR)
+#endif /* ifdef sun */
{
log_error(LOG_LEVEL_FATAL, "Can't set signal-handler for signal %d: %E", catched_signals[idx]);
}
close( 1 );
chdir("/");
- write_pid_file();
-
} /* -END- if (!no_daemon) */
+
+ /*
+ * As soon as we have written the PID file, we can switch
+ * to the user and group ID indicated by the --user option
+ */
+ write_pid_file();
+
+ if (NULL != pw)
+ {
+ if (setgid((NULL != grp) ? grp->gr_gid : pw->pw_gid))
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot setgid(): Insufficient permissions.");
+ }
+ if (do_chroot)
+ {
+ if (!pw->pw_dir)
+ {
+ log_error(LOG_LEVEL_FATAL, "Home directory for %s undefined", pw->pw_name);
+ }
+ if (chroot(pw->pw_dir) < 0)
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot chroot to %s", pw->pw_dir);
+ }
+ if (chdir ("/"))
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot chdir /");
+ }
+ }
+ if (setuid(pw->pw_uid))
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot setuid(): Insufficient permissions.");
+ }
+ if (do_chroot)
+ {
+ char putenv_dummy[64];
+
+ strcpy(putenv_dummy, "HOME=/");
+ if (putenv(putenv_dummy) != 0)
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot putenv(): HOME");
+ }
+
+ snprintf(putenv_dummy, 64, "USER=%s", pw->pw_name);
+ if (putenv(putenv_dummy) != 0)
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot putenv(): USER");
+ }
+ }
+ }
+ else if (do_chroot)
+ {
+ log_error(LOG_LEVEL_FATAL, "Cannot chroot without --user argument.");
+ }
}
#endif /* defined unix */
- DBG(1, ("call listen_loop() \n") );
+#ifdef _WIN32
+ /* This will be FALSE unless the command line specified --service
+ */
+ if (bRunAsService)
+ {
+ /* Yup, so now we must attempt to establish a connection
+ * with the service dispatcher. This will only work if this
+ * process was launched by the service control manager to
+ * actually run as a service. If this isn't the case, i've
+ * known it take around 30 seconds or so for the call to return.
+ */
+
+ /* The StartServiceCtrlDispatcher won't return until the service is stopping */
+ if (w32_start_service_ctrl_dispatcher(w32ServiceDispatchTable))
+ {
+ /* Service has run, and at this point is now being stopped, so just return */
+ return 0;
+ }
+
+#ifdef _WIN_CONSOLE
+ printf("Warning: Failed to connect to Service Control Dispatcher\nwhen starting as a service!\n");
+#endif
+ /* An error occurred. Usually it's because --service was wrongly specified
+ * and we were unable to connect to the Service Control Dispatcher because
+ * it wasn't expecting us and is therefore not listening.
+ *
+ * For now, just continue below to call the listen_loop function.
+ */
+ }
+#endif /* def _WIN32 */
+
listen_loop();
/* NOTREACHED */
}
+/*********************************************************************
+ *
+ * Function : bind_port_helper
+ *
+ * Description : Bind the listen port. Handles logging, and aborts
+ * on failure.
+ *
+ * Parameters :
+ * 1 : config = Privoxy configuration. Specifies port
+ * to bind to.
+ *
+ * Returns : Port that was opened.
+ *
+ *********************************************************************/
+static jb_socket bind_port_helper(struct configuration_spec * config)
+{
+ 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)
+ {
+ log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses",
+ config->hport);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_INFO, "Listening on port %d on IP address %s",
+ config->hport, config->haddr);
+ }
+
+ result = bind_port(config->haddr, config->hport, &bfd);
+
+ if (result < 0)
+ {
+ switch(result)
+ {
+ case -3 :
+ log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: "
+ "There may be another Privoxy or some other "
+ "proxy running on port %d",
+ (NULL != config->haddr) ? config->haddr : "INADDR_ANY",
+ config->hport, config->hport);
+
+ case -2 :
+ log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: "
+ "The hostname is not resolvable",
+ (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport);
+
+ default :
+ log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: because %E",
+ (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport);
+ }
+
+ /* shouldn't get here */
+ return JB_INVALID_SOCKET;
+ }
+
+ config->need_bind = 0;
+
+ return bfd;
+}
+
+
+#ifdef _WIN32
+/* Without this simple workaround we get this compiler warning from _beginthread
+ * warning C4028: formal parameter 1 different from declaration
+ */
+void w32_service_listen_loop(void *p)
+{
+ listen_loop();
+}
+#endif /* def _WIN32 */
+
+
/*********************************************************************
*
* Function : listen_loop
static void listen_loop(void)
{
struct client_state *csp = NULL;
- int bfd;
+ jb_socket bfd;
struct configuration_spec * config;
config = load_config();
- log_error(LOG_LEVEL_CONNECT, "bind (%s, %d)",
- config->haddr ? config->haddr : "INADDR_ANY", config->hport);
-
- bfd = bind_port(config->haddr, config->hport);
+ bfd = bind_port_helper(config);
- if (bfd < 0)
- {
- log_error(LOG_LEVEL_FATAL, "can't bind %s:%d: %E "
- "- There may be another junkbuster or some other "
- "proxy running on port %d",
- (NULL != config->haddr) ? config->haddr : "INADDR_ANY",
- config->hport, config->hport
- );
- /* shouldn't get here */
- return;
- }
-
- config->need_bind = 0;
-
- while (FOREVER)
+#ifdef FEATURE_GRACEFUL_TERMINATION
+ while (!g_terminate)
+#else
+ for (;;)
+#endif
{
#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
while (waitpid(-1, NULL, WNOHANG) > 0)
*/
sweep();
+#if defined(unix)
/*
* Re-open the errlog after HUP signal
*/
init_error_log(Argv[0], config->logfile, config->debug);
received_hup_signal = 0;
}
+#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))) )
{
}
csp->flags |= CSP_FLAG_ACTIVE;
- csp->sfd = -1;
+ csp->sfd = JB_INVALID_SOCKET;
csp->config = config = load_config();
close_socket(bfd);
- log_error(LOG_LEVEL_CONNECT, "bind (%s, %d)",
- config->haddr ? config->haddr : "INADDR_ANY", config->hport);
- bfd = bind_port(config->haddr, config->hport);
-
- if (bfd < 0)
- {
- log_error(LOG_LEVEL_FATAL, "can't bind %s:%d: %E "
- "- There may be another junkbuster or some other "
- "proxy running on port %d",
- (NULL != config->haddr) ? config->haddr : "INADDR_ANY",
- config->hport, config->hport
- );
- /* shouldn't get here */
- return;
- }
-
- config->need_bind = 0;
+ bfd = bind_port_helper(config);
}
log_error(LOG_LEVEL_CONNECT, "accept connection ... ");
}
#ifdef FEATURE_TOGGLE
- if (g_bToggleIJB)
+ if (global_toggle_state)
{
csp->flags |= CSP_FLAG_TOGGLED_ON;
}
pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
- child_id = (pthread_create(&the_thread, &attrs,
- (void*)serve, csp) ? -1 : 0);
+ errno = pthread_create(&the_thread, &attrs,
+ (void*)serve, csp);
+ child_id = errno ? -1 : 0;
pthread_attr_destroy(&attrs);
}
#endif
#if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
child_id = _beginthread(
- (void*)serve,
+ (void (*)(void *))serve,
64 * 1024,
csp);
#endif
#if defined(__OS2__) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
child_id = _beginthread(
- serve,
+ (void(* _Optlink)(void*))serve,
NULL,
64 * 1024,
csp);
#if defined(AMIGA) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
csp->cfd = ReleaseSocket(csp->cfd, -1);
+
if((child_id = (int)CreateNewProcTags(
NP_Entry, (ULONG)server_thread,
NP_Output, Output(),
NP_CloseOutput, FALSE,
- NP_Name, (ULONG)"junkbuster child",
+ NP_Name, (ULONG)"privoxy child",
NP_StackSize, 200*1024,
TAG_DONE)))
{
*/
if (child_id == 0) /* child */
{
+ int rc = 0;
+#ifdef FEATURE_TOGGLE
+ int inherited_toggle_state = global_toggle_state;
+#endif /* def FEATURE_TOGGLE */
+
serve(csp);
- _exit(0);
+ /*
+ * If we've been toggled or we've blocked the request, tell Mom
+ */
+
+#ifdef FEATURE_TOGGLE
+ if (inherited_toggle_state != global_toggle_state)
+ {
+ rc |= RC_FLAG_TOGGLED;
+ }
+#endif /* def FEATURE_TOGGLE */
+
+#ifdef FEATURE_STATISTICS
+ if (csp->flags & CSP_FLAG_REJECTED)
+ {
+ rc |= RC_FLAG_BLOCKED;
+ }
+#endif /* ndef FEATURE_STATISTICS */
+
+ _exit(rc);
}
else if (child_id > 0) /* parent */
{
* copy of the client socket and the CSP
* are not used.
*/
+ int child_status;
+#if !defined(_WIN32) && !defined(__CYGWIN__)
+
+ wait( &child_status );
+
+ /*
+ * Evaluate child's return code: If the child has
+ * - been toggled, toggle ourselves
+ * - blocked its request, bump up the stats counter
+ */
+
+#ifdef FEATURE_TOGGLE
+ if (WIFEXITED(child_status) && (WEXITSTATUS(child_status) & RC_FLAG_TOGGLED))
+ {
+ global_toggle_state = !global_toggle_state;
+ }
+#endif /* def FEATURE_TOGGLE */
+
+#ifdef FEATURE_STATISTICS
+ urls_read++;
+ if (WIFEXITED(child_status) && (WEXITSTATUS(child_status) & RC_FLAG_BLOCKED))
+ {
+ urls_rejected++;
+ }
+#endif /* def FEATURE_STATISTICS */
-#if !defined(_WIN32) && defined(__CYGWIN__)
- wait( NULL );
#endif /* !defined(_WIN32) && defined(__CYGWIN__) */
close_socket(csp->cfd);
csp->flags &= ~CSP_FLAG_ACTIVE;
log_error(LOG_LEVEL_ERROR, "can't fork: %E");
- sprintf(buf , "JunkBuster: can't fork: errno = %d", errno);
+ sprintf(buf , "Privoxy: can't fork: errno = %d", errno);
write_socket(csp->cfd, buf, strlen(buf));
close_socket(csp->cfd);
serve(csp);
}
}
- /* NOTREACHED */
+
+ /* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */
+
+ /* Clean up. Aim: free all memory (no leaks) */
+#ifdef FEATURE_GRACEFUL_TERMINATION
+
+ log_error(LOG_LEVEL_ERROR, "Graceful termination requested");
+
+ unload_current_config_file();
+ unload_current_actions_file();
+ unload_current_re_filterfile();
+#ifdef FEATURE_TRUST
+ unload_current_trust_file();
+#endif
+
+ if (config->multi_threaded)
+ {
+ int i = 60;
+ do
+ {
+ sleep(1);
+ sweep();
+ } while ((clients->next != NULL) && (--i > 0));
+
+ if (i <= 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "Graceful termination failed - still some live clients after 1 minute wait.");
+ }
+ }
+ sweep();
+ sweep();
+
+#if defined(unix)
+ freez(basedir);
+#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();
+#endif
+
+ exit(0);
+#endif /* FEATURE_GRACEFUL_TERMINATION */
}