Fixed LOG_LEVEL_LOG
[privoxy.git] / jcc.c
1 const char jcc_rcs[] = "$Id: jcc.c,v 1.10 2001/05/26 15:26:15 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
5  *
6  * Purpose     :  Main file.  Contains main() method, main loop, and 
7  *                the main connection-handling function.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                IJBSWA team.  http://ijbswa.sourceforge.net
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and 
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it 
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: jcc.c,v $
36  *    Revision 1.10  2001/05/26 15:26:15  jongfoster
37  *    ACL feature now provides more security by immediately dropping
38  *    connections from untrusted hosts.
39  *
40  *    Revision 1.9  2001/05/26 00:28:36  jongfoster
41  *    Automatic reloading of config file.
42  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
43  *    Most of the global variables have been moved to a new
44  *    struct configuration_spec, accessed through csp->config->globalname
45  *    Most of the globals remaining are used by the Win32 GUI.
46  *
47  *    Revision 1.8  2001/05/25 22:43:18  jongfoster
48  *    Fixing minor memory leak and buffer overflow.
49  *
50  *    Revision 1.7  2001/05/25 22:34:30  jongfoster
51  *    Hard tabs->Spaces
52  *
53  *    Revision 1.6  2001/05/23 00:13:58  joergs
54  *    AmigaOS support fixed.
55  *
56  *    Revision 1.5  2001/05/22 18:46:04  oes
57  *
58  *    - Enabled filtering banners by size rather than URL
59  *      by adding patterns that replace all standard banner
60  *      sizes with the "Junkbuster" gif to the re_filterfile
61  *
62  *    - Enabled filtering WebBugs by providing a pattern
63  *      which kills all 1x1 images
64  *
65  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
66  *      which is selected by the (nonstandard and therefore
67  *      capital) letter 'U' in the option string.
68  *      It causes the quantifiers to be ungreedy by default.
69  *      Appending a ? turns back to greedy (!).
70  *
71  *    - Added a new interceptor ijb-send-banner, which
72  *      sends back the "Junkbuster" gif. Without imagelist or
73  *      MSIE detection support, or if tinygif = 1, or the
74  *      URL isn't recognized as an imageurl, a lame HTML
75  *      explanation is sent instead.
76  *
77  *    - Added new feature, which permits blocking remote
78  *      script redirects and firing back a local redirect
79  *      to the browser.
80  *      The feature is conditionally compiled, i.e. it
81  *      can be disabled with --disable-fast-redirects,
82  *      plus it must be activated by a "fast-redirects"
83  *      line in the config file, has its own log level
84  *      and of course wants to be displayed by show-proxy-args
85  *      Note: Boy, all the #ifdefs in 1001 locations and
86  *      all the fumbling with configure.in and acconfig.h
87  *      were *way* more work than the feature itself :-(
88  *
89  *    - Because a generic redirect template was needed for
90  *      this, tinygif = 3 now uses the same.
91  *
92  *    - Moved GIFs, and other static HTTP response templates
93  *      to project.h
94  *
95  *    - Some minor fixes
96  *
97  *    - Removed some >400 CRs again (Jon, you really worked
98  *      a lot! ;-)
99  *
100  *    Revision 1.4  2001/05/21 19:34:01  jongfoster
101  *    Made failure to bind() a fatal error.
102  *
103  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
104  *    Version 2.9.4 checkin.
105  *    - Merged popupfile and cookiefile, and added control over PCRS
106  *      filtering, in new "permissionsfile".
107  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
108  *      file error you now get a message box (in the Win32 GUI) rather
109  *      than the program exiting with no explanation.
110  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
111  *      skipping.
112  *    - Removed tabs from "config"
113  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
114  *    - Bumped up version number.
115  *
116  *    Revision 1.2  2001/05/17 22:34:44  oes
117  *     - Added hint on GIF char array generation to jcc.c
118  *     - Cleaned CRLF's from the sources and related files
119  *     - Repaired logging for REF and FRC
120  *
121  *    Revision 1.1.1.1  2001/05/15 13:58:56  oes
122  *    Initial import of version 2.9.3 source tree
123  *
124  *
125  *********************************************************************/
126 \f
127
128 #include "config.h"
129
130 #include <stdio.h>
131 #include <sys/types.h>
132 #include <stdlib.h>
133 #include <string.h>
134 #include <signal.h>
135 #include <fcntl.h>
136 #include <errno.h>
137
138 #ifdef _WIN32
139
140 # include <sys/timeb.h>
141 # include <windows.h>
142 # include <io.h>
143 # include <process.h>
144 # ifdef TOGGLE
145 #  include <time.h>
146 # endif /* def TOGGLE */
147
148 # include "win32.h"
149 # ifndef _WIN_CONSOLE
150 #  include "w32log.h"
151 # endif /* ndef _WIN_CONSOLE */
152
153 #else /* ifndef _WIN32 */
154
155 # include <unistd.h>
156 # include <sys/time.h>
157 # include <sys/wait.h>
158 # include <sys/stat.h>
159 # include <signal.h>
160
161 # ifdef __BEOS__
162 #  include <socket.h>  /* BeOS has select() for sockets only. */
163 #  include <OS.h>      /* declarations for threads and stuff. */
164 # endif
165
166 # ifndef FD_ZERO
167 #  include <select.h>
168 # endif
169
170 #endif
171
172 #include "project.h"
173 #include "jcc.h"
174 #include "filters.h"
175 #include "loaders.h"
176 #include "showargs.h"
177 #include "parsers.h"
178 #include "killpopup.h"
179 #include "miscutil.h"
180 #include "errlog.h"
181 #include "jbsockets.h"
182 #include "gateway.h"
183
184 const char jcc_h_rcs[] = JCC_H_VERSION;
185 const char project_h_rcs[] = PROJECT_H_VERSION;
186
187 const char DEFAULT_USER_AGENT[] ="User-Agent: Mozilla (X11; I; Linux 2.0.32 i586)";
188
189 struct client_state  clients[1];
190 struct file_list     files[1];
191
192 #ifdef STATISTICS
193 int urls_read     = 0;     /* total nr of urls read inc rejected */
194 int urls_rejected = 0;     /* total nr of urls rejected */
195 #endif /* def STATISTICS */
196
197
198 static void listen_loop(void);
199 static void chat(struct client_state *csp);
200 #ifdef AMIGA
201 void serve(struct client_state *csp);
202 #else /* ifndef AMIGA */
203 static void serve(struct client_state *csp);
204 #endif /* def AMIGA */
205
206 #ifdef __BEOS__
207 static int32 server_thread(void *data);
208 #endif /* def __BEOS__ */
209
210 #ifdef _WIN32
211 #define sleep(N)  Sleep(((N) * 1000))
212 #endif
213
214
215 /*********************************************************************
216  *
217  * Function    :  chat
218  *
219  * Description :  Once a connection to the client has been accepted,
220  *                this function is called (via serve()) to handle the
221  *                main business of the communication.  When this 
222  *                function returns, the caller must close the client
223  *                socket handle.
224  *
225  * Parameters  :
226  *          1  :  csp = Current client state (buffers, headers, etc...)
227  *
228  * Returns     :  On success, the number of bytes written are returned (zero
229  *                indicates nothing was written).  On error, -1 is returned,
230  *                and errno is set appropriately.  If count is zero and the
231  *                file descriptor refers to a regular file, 0 will be
232  *                returned without causing any other effect.  For a special
233  *                file, the results are not portable.
234  *
235  *********************************************************************/
236 static void chat(struct client_state *csp)
237 {
238 /* This next line is a little ugly, but it simplifies the if statement below. */
239 /* Basically if TOGGLE, then we want the if to test "csp->toggled_on", else we don't */
240 #ifdef TOGGLE
241 #   define IS_TOGGLED_ON csp->toggled_on &&
242 #else /* ifndef TOGGLE */
243 #   define IS_TOGGLED_ON
244 #endif /* ndef TOGGLE */
245
246 /* This next line is a little ugly, but it simplifies the if statement below. */
247 /* Basically if TRUST_FILES, then we want the if to call "trust_url", else we don't */
248 #ifdef TRUST_FILES
249 #   define IS_TRUSTED_URL (p = trust_url(http, csp)) ||
250 #else /* ifndef TRUST_FILES */
251 #   define IS_TRUSTED_URL
252 #endif /* ndef TRUST_FILES */
253
254    char buf[BUFSIZ], *hdr, *p, *req;
255    char *err = NULL;
256    char *eno;
257    fd_set rfds;
258    int n, maxfd, server_body;
259    int ms_iis5_hack = 0;
260    int byte_count = 0;
261    const struct gateway *gw;
262    struct http_request *http;
263 #ifdef KILLPOPUPS
264    int block_popups;         /* bool, 1==will block popups */
265    int block_popups_now = 0; /* bool, 1==currently blocking popups */
266 #endif /* def KILLPOPUPS */
267 #ifdef PCRS
268    int pcrs_filter;   /* bool, 1==will filter through pcrs */
269    int filtering = 0; /* bool, 1==currently filtering through pcrs */
270 #endif /* def PCRS */
271
272    http = csp->http;
273
274    /*
275     * Read the client's request.  Note that since we're not using select() we
276     * could get blocked here if a client connected, then didn't say anything!
277     */
278
279    while (FOREVER)
280    {
281       n = read_socket(csp->cfd, buf, sizeof(buf));
282
283       if (n <= 0) break;      /* error! */
284
285       add_to_iob(csp, buf, n);
286
287       req = get_header(csp);
288
289       if (req == NULL)
290       {
291          break;    /* no HTTP request! */
292       }
293
294       if (*req == '\0')
295       {
296          continue;   /* more to come! */
297       }
298  
299 #ifdef FORCE_LOAD
300       /* If this request contains the FORCE_PREFIX,
301        * better get rid of it now and set the force flag --oes
302        */
303
304       if (strstr(req, FORCE_PREFIX))
305       {
306          strclean(req, FORCE_PREFIX);
307          log_error(LOG_LEVEL_FORCE, "Enforcing request \"%s\".\n", req);
308          csp->force = 1;
309       } 
310       else
311       {
312          csp->force = 0;
313       }
314 #endif /* def FORCE_LOAD */
315   
316       parse_http_request(req, http, csp);
317       freez(req);
318       break;
319    }
320
321    if (http->cmd == NULL)
322    {
323       strcpy(buf, CHEADER);
324       write_socket(csp->cfd, buf, strlen(buf));
325
326       log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 400 0", csp->ip_addr_str);
327
328       return;
329    }
330
331    /* decide how to route the HTTP request */
332
333    if ((gw = forward_url(http, csp)) == NULL)
334    {
335       log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!?  This can't happen!");
336       /* Never get here - LOG_LEVEL_FATAL causes program exit */
337    }
338
339    /* build the http request to send to the server
340     * we have to do one of the following:
341     *
342     * create = use the original HTTP request to create a new
343     *          HTTP request that has only the path component
344     *          without the http://domainspec
345     * pass   = pass the original HTTP request unchanged
346     *
347     * drop   = drop the HTTP request
348     *
349     * here's the matrix:
350     *                        SSL
351     *                    0        1
352     *                +--------+--------+
353     *                |        |        |
354     *             0  | create | drop   |
355     *                |        |        |
356     *  Forwarding    +--------+--------+
357     *                |        |        |
358     *             1  | pass   | pass   |
359     *                |        |        |
360     *                +--------+--------+
361     *
362     */
363
364    if (gw->forward_host)
365    {
366       /* if forwarding, just pass the request as is */
367       enlist(csp->headers, http->cmd);
368    }
369    else
370    {
371       if (http->ssl == 0)
372       {
373          /* otherwise elide the host information from the url */
374          p = NULL;
375          p = strsav(p, http->gpc);
376          p = strsav(p, " ");
377          p = strsav(p, http->path);
378          p = strsav(p, " ");
379          p = strsav(p, http->ver);
380          enlist(csp->headers, p);
381          freez(p);
382       }
383    }
384
385    /* decide what we're to do with cookies */
386
387 #ifdef TOGGLE
388    if (!csp->toggled_on)
389    {
390       /* Most compatible set of permissions */
391       csp->permissions = PERMIT_COOKIE_SET | PERMIT_COOKIE_READ | PERMIT_POPUPS;
392    }
393    else
394    {
395       csp->permissions = url_permissions(http, csp);
396    }
397 #else /* ifndef TOGGLE */
398    csp->permissions = url_permissions(http, csp);
399 #endif /* ndef TOGGLE */
400
401 #ifdef KILLPOPUPS
402    block_popups               = ((csp->permissions & PERMIT_POPUPS) == 0);
403 #endif /* def KILLPOPUPS */
404 #ifdef PCRS
405    pcrs_filter                = (csp->rlist != NULL) &&  /* There are expressions to be used */
406                                 ((csp->permissions & PERMIT_RE_FILTER) != 0);
407 #endif /* def PCRS */
408
409
410    /* grab the rest of the client's headers */
411
412    while (FOREVER)
413    {
414       if ( ( p = get_header(csp) ) && ( *p == '\0' ) )
415       {
416          n = read_socket(csp->cfd, buf, sizeof(buf));
417          if (n <= 0)
418          {
419             log_error(LOG_LEVEL_ERROR, "read from client failed: %E");
420             return;
421          }
422          add_to_iob(csp, buf, n);
423          continue;
424       }
425
426       if (p == NULL) break;
427
428       enlist(csp->headers, p);
429       freez(p);
430    }
431
432    /* filter it as required */
433
434    hdr = sed(client_patterns, add_client_headers, csp);
435
436    destroy_list(csp->headers);
437
438    /* Check the request against all rules, unless
439     * we're toggled off or in force mode. 
440     */
441  
442    if (IS_TOGGLED_ON
443 #ifdef FORCE_LOAD
444        (!csp->force) && 
445 #endif /* def FORCE_LOAD */
446        ( (p = intercept_url(http, csp)) ||
447          IS_TRUSTED_URL
448          (p = block_url(http, csp))
449 #ifdef FAST_REDIRECTS
450          || (csp->config->fast_redirects && (p = redirect_url(http, csp))) 
451 #endif /* def FAST_REDIRECTS */
452       ))
453    {
454 #ifdef STATISTICS
455       csp->rejected = 1;
456 #endif /* def STATISTICS */
457
458       log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path);
459
460       log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 1", 
461                                csp->ip_addr_str, http->cmd); 
462
463 #if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
464       /* Block as image?  */
465       if ( (csp->config->tinygif > 0) && block_imageurl(http, csp) )
466       {
467          /* Send "blocked" image */
468          log_error(LOG_LEVEL_GPC, "%s%s image crunch!",
469                    http->hostport, http->path);
470
471          if ((csp->config->tinygif == 2) || strstr(http->path, "ijb-send-banner"))
472          {
473             write_socket(csp->cfd, JBGIF, sizeof(JBGIF)-1);
474          }
475          else if (csp->config->tinygif == 1)
476          {
477             write_socket(csp->cfd, BLANKGIF, sizeof(BLANKGIF)-1);
478          }
479          else if ((csp->config->tinygif == 3) && (csp->config->tinygifurl))
480          {
481             freez(p);
482             p = (char *)malloc(sizeof(HTTP_REDIRECT_TEMPLATE) 
483               + strlen(csp->config->tinygifurl));
484             sprintf(p, HTTP_REDIRECT_TEMPLATE, csp->config->tinygifurl);
485             write_socket(csp->cfd, p, strlen(p));
486          }
487          else
488          {
489             write_socket(csp->cfd, JBGIF, sizeof(JBGIF)-1);
490          }
491       }
492       else
493 #endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
494       /* Block as HTML */
495       {
496          /* Send HTML "blocked" message, interception, or redirection result */
497          write_socket(csp->cfd, p, strlen(p));
498       }
499
500       freez(p);
501       freez(hdr);
502       return;
503    }
504
505    log_error(LOG_LEVEL_GPC, "%s%s", http->hostport, http->path);
506
507    if (gw->forward_host)
508    {
509       log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s",
510                gw->forward_host, gw->forward_port, http->hostport);
511    }
512    else
513    {
514       log_error(LOG_LEVEL_CONNECT, "to %s", http->hostport);
515    }
516
517    /* here we connect to the server, gateway, or the forwarder */
518
519    csp->sfd = (gw->conn)(gw, http, csp);
520
521    if (csp->sfd < 0)
522    {
523       log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E",
524                 http->hostport);
525
526       if (errno == EINVAL)
527       {
528          err = zalloc(strlen(CNXDOM) + strlen(http->host));
529          sprintf(err, CNXDOM, http->host);
530
531          log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 404 0", 
532                    csp->ip_addr_str, http->cmd);
533       }
534       else
535       {
536          eno = safe_strerror(errno);
537          err = zalloc(strlen(CFAIL) + strlen(http->hostport) + strlen(eno));
538          sprintf(err, CFAIL, http->hostport, eno);
539
540          log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", 
541                    csp->ip_addr_str, http->cmd);
542       }
543
544       write_socket(csp->cfd, err, strlen(err));
545
546       freez(err);
547       freez(hdr);
548       return;
549    }
550
551    log_error(LOG_LEVEL_CONNECT, "OK");
552
553    if (gw->forward_host || (http->ssl == 0))
554    {
555       /* write the client's (modified) header to the server
556        * (along with anything else that may be in the buffer)
557        */
558
559       n = strlen(hdr);
560
561       if ((write_socket(csp->sfd, hdr, n) != n)
562           || (flush_socket(csp->sfd, csp   ) <  0))
563       {
564          log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E",
565                     http->hostport);
566
567          eno = safe_strerror(errno);
568          err = zalloc(strlen(CFAIL) + strlen(http->hostport) + strlen(eno));
569          sprintf(err, CFAIL, http->hostport, eno);
570          write_socket(csp->cfd, err, strlen(err));
571
572          log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", 
573                    csp->ip_addr_str, http->cmd); 
574
575          freez(err);
576          freez(hdr);
577          return;
578       }
579    }
580    else
581    {
582       /*
583        * We're running an SSL tunnel and we're not forwarding,
584        * so just send the "connect succeeded" message to the
585        * client, flush the rest, and get out of the way.
586        */
587       log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 2\n", 
588                 csp->ip_addr_str, http->cmd); 
589
590       if (write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1) < 0)
591       {
592          freez(hdr);
593          return;
594       }
595       IOB_RESET(csp);
596    }
597
598    /* we're finished with the client's header */
599    freez(hdr);
600
601    maxfd = ( csp->cfd > csp->sfd ) ? csp->cfd : csp->sfd;
602
603    /* pass data between the client and server
604     * until one or the other shuts down the connection.
605     */
606
607    server_body = 0;
608
609    while (FOREVER)
610    {
611       FD_ZERO(&rfds);
612
613       FD_SET(csp->cfd, &rfds);
614       FD_SET(csp->sfd, &rfds);
615
616       n = select(maxfd+1, &rfds, NULL, NULL, NULL);
617
618       if (n < 0)
619       {
620          log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
621          return;
622       }
623
624       /* this is the body of the browser's request
625        * just read it and write it.
626        */
627
628       if (FD_ISSET(csp->cfd, &rfds))
629       {
630          n = read_socket(csp->cfd, buf, sizeof(buf));
631
632          if (n <= 0)
633          {
634             break; /* "game over, man" */
635          }
636
637          if (write_socket(csp->sfd, buf, n) != n)
638          {
639             log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
640             return;
641          }
642          continue;
643       }
644
645       /*
646        * The server wants to talk.  It could be the header or the body.
647        * If `hdr' is null, then it's the header otherwise it's the body.
648        * FIXME: Does `hdr' really mean `host'?
649        */
650
651
652       if (FD_ISSET(csp->sfd, &rfds))
653       {
654          fflush( 0 );
655          n = read_socket(csp->sfd, buf, sizeof(buf) - 1);
656
657          if (n < 0)
658          {
659             log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
660
661             eno = safe_strerror(errno);
662             sprintf(buf, CFAIL, http->hostport, eno);
663             freez(eno);
664
665             log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", 
666                       csp->ip_addr_str, http->cmd); 
667
668             write_socket(csp->cfd, buf, strlen(buf));
669             return;
670          }
671
672          /* Add a trailing zero.  This lets filter_popups
673           * use string operations.
674           */
675          buf[n] = '\0';
676
677 #ifdef KILLPOPUPS
678          /* Filter the popups on this read. */
679          if (block_popups_now)
680          {
681             filter_popups(buf, n);
682          }
683 #endif /* def KILLPOPUPS */
684
685          /* Normally, this would indicate that we've read
686           * as much as the server has sent us and we can
687           * close the client connection.  However, Microsoft
688           * in its wisdom has released IIS/5 with a bug that
689           * prevents it from sending the trailing \r\n in
690           * a 302 redirect header (and possibly other headers).
691           * To work around this if we've haven't parsed
692           * a full header we'll append a trailing \r\n
693           * and see if this now generates a valid one.
694           *
695           * This hack shouldn't have any impacts.  If we've
696           * already transmitted the header or if this is a
697           * SSL connection, then we won't bother with this
698           * hack.  So we only work on partially received
699           * headers.  If we append a \r\n and this still
700           * doesn't generate a valid header, then we won't
701           * transmit anything to the client.
702           */
703          if (n == 0)
704          {
705             /* This hack must only be enforced for headers. */
706             if (server_body || http->ssl)
707             {
708 #ifdef PCRS
709                if (filtering)
710                {
711                   re_process_buffer(csp);
712                }
713 #endif /* def PCRS */
714                break; /* "game over, man" */
715             }
716
717             /* Let's pretend the server just sent us a blank line. */
718             n = sprintf(buf, "\r\n");
719
720             /*
721              * Now, let the normal header parsing algorithm below do its
722              * job.  If it fails, we'll exit instead of continuing.
723              */
724
725             ms_iis5_hack = 1;
726          }
727
728          /*
729           * If this is an SSL connection or we're in the body
730           * of the server document, just write it to the client.
731           */
732
733          if (server_body || http->ssl)
734          {
735 #ifdef PCRS
736             if (filtering)
737             {
738                add_to_iob(csp, buf, n); /* Buffer the body for filtering */
739             }
740             else
741 #endif /* def PCRS */
742             {
743                /* just write */
744                if (write_socket(csp->cfd, buf, n) != n)
745                {
746                   log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
747                   return;
748                }
749             }
750             byte_count += n;
751             continue;
752          }
753          else
754          {
755             /* we're still looking for the end of the
756              * server's header ... (does that make header
757              * parsing an "out of body experience" ?
758              */
759
760             /* buffer up the data we just read */
761             add_to_iob(csp, buf, n);
762
763             /* get header lines from the iob */
764
765             while ((p = get_header(csp)))
766             {
767                if (*p == '\0')
768                {
769                   /* see following note */
770                   break;
771                }
772                enlist(csp->headers, p);
773                freez(p);
774             }
775
776             /* NOTE: there are no "empty" headers so
777              * if the pointer `p' is not NULL we must
778              * assume that we reached the end of the
779              * buffer before we hit the end of the header.
780              */
781
782             if (p)
783             {
784                if (ms_iis5_hack)
785                {
786                   /* Well, we tried our MS IIS/5
787                    * hack and it didn't work.
788                    * The header is incomplete
789                    * and there isn't anything
790                    * we can do about it.
791                    */
792                   break;
793                }
794                else
795                {
796                   /* Since we have to wait for
797                    * more from the server before
798                    * we can parse the headers
799                    * we just continue here.
800                    */
801                   continue;
802                }
803             }
804
805             /* we have now received the entire header.
806              * filter it and send the result to the client
807              */
808
809             hdr = sed(server_patterns, add_server_headers, csp);
810             n   = strlen(hdr);
811
812             /* write the server's (modified) header to
813              * the client (along with anything else that
814              * may be in the buffer)
815              */
816
817 #ifdef KILLPOPUPS
818             /* Start blocking popups if appropriate. */
819
820             if (csp->is_text  &&  /* It's a text / * MIME-Type */
821                 !http->ssl    &&  /* We talk plaintext */
822                 block_popups)
823             {
824                block_popups_now = 1;
825             }
826
827 #endif /* def KILLPOPUPS */
828
829 #ifdef PCRS
830             /* Start re_filtering this if appropriate. */
831
832             if (csp->is_text  &&  /* It's a text / * MIME-Type */
833                 !http->ssl    &&  /* We talk plaintext */
834                 pcrs_filter)      /* Policy allows */
835             {
836                filtering = 1;
837             }
838
839 /* This next line is a little ugly, but it simplifies the if statement below. */
840 /* Basically if using PCRS, we want the OR condition to require "!filtering"  */
841 #define NOT_FILTERING_AND !filtering &&
842
843 #else /* not def PCRS */
844
845 #define NOT_FILTERING_AND
846
847 #endif /* def PCRS */
848
849             if ((write_socket(csp->cfd, hdr, n) != n)
850                 || (NOT_FILTERING_AND (n = flush_socket(csp->cfd, csp) < 0)))
851             {
852                log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
853
854                /* the write failed, so don't bother
855                 * mentioning it to the client...
856                 * it probably can't hear us anyway.
857                 */
858                freez(hdr);
859                return;
860             }
861
862             NOT_FILTERING_AND (byte_count += n);
863
864             /* we're finished with the server's header */
865
866             freez(hdr);
867             server_body = 1;
868
869             /* If this was a MS IIS/5 hack then it means
870              * the server has already closed the
871              * connection.  Nothing more to read.  Time
872              * to bail.
873              */
874             if (ms_iis5_hack)
875             {
876                break;
877             }
878          }
879          continue;
880       }
881
882       return; /* huh? we should never get here */
883    }
884
885    log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %d", 
886              csp->ip_addr_str, http->cmd, byte_count); 
887 }
888
889
890 /*********************************************************************
891  *
892  * Function    :  serve
893  *
894  * Description :  This is little more than chat.  We only "serve" to
895  *                to close any socket that chat may have opened.
896  *
897  * Parameters  :
898  *          1  :  csp = Current client state (buffers, headers, etc...)
899  *
900  * Returns     :  N/A
901  *
902  *********************************************************************/
903 #ifdef AMIGA
904 void serve(struct client_state *csp)
905 #else /* ifndef AMIGA */
906 static void serve(struct client_state *csp)
907 #endif /* def AMIGA */
908 {
909    chat(csp);
910    close_socket(csp->cfd);
911
912    if (csp->sfd >= 0)
913    {
914       close_socket(csp->sfd);
915    }
916
917    csp->active = 0;
918
919 }
920
921
922 #ifdef __BEOS__
923
924 /*********************************************************************
925  *
926  * Function    :  server_thread
927  *
928  * Description :  We only exist to call `serve' in a threaded environment.
929  *
930  * Parameters  :
931  *          1  :  data = Current client state (buffers, headers, etc...)
932  *
933  * Returns     :  Always 0.
934  *
935  *********************************************************************/
936 static int32 server_thread(void *data)
937 {
938    serve((struct client_state *) data);
939    return 0;
940
941 }
942
943 #endif
944
945
946 /*********************************************************************
947  *
948  * Function    :  main
949  *
950  * Description :  Load the config file and start the listen loop.
951  *                This function is a lot more *sane* with the `load_config'
952  *                and `listen_loop' functions; although it stills does
953  *                a *little* too much for my taste.
954  *
955  * Parameters  :
956  *          1  :  argc = Number of parameters (including $0).
957  *          2  :  argv = Array of (char *)'s to the parameters.
958  *
959  * Returns     :  1 if : can't open config file, unrecognized directive,
960  *                stats requested in multi-thread mode, can't open the
961  *                log file, can't open the jar file, listen port is invalid,
962  *                any load fails, and can't bind port.
963  *
964  *                Else main never returns, the process must be signaled
965  *                to terminate execution.  Or, on Windows, use the 
966  *                "File", "Exit" menu option.
967  *
968  *********************************************************************/
969 #ifdef __MINGW32__
970 int _main(int argc, const char *argv[])
971 #else
972 int main(int argc, const char *argv[])
973 #endif
974 {
975    configfile =
976 #ifdef AMIGA
977    "AmiTCP:db/junkbuster.config"
978 #elif !defined(_WIN32)
979    "config"
980 #else
981    "junkbstr.txt"
982 #endif
983       ;
984
985 #if !defined(_WIN32) || defined(_WIN_CONSOLE)
986    if ((argc >= 2) && (strcmp(argv[1], "--help")==0))
987    {
988       printf("JunkBuster proxy version " VERSION ".\n\n"
989          "Usage: %s [configfile]\n\n"
990          "See " HOME_PAGE_URL " for details.\n"
991          "This program is distributed under the GNU GPL, version 2 or later.\n",
992          argv[0]);
993       exit(2);
994    }
995    if ((argc >= 2) && (strcmp(argv[1], "--version")==0))
996    {
997       printf(VERSION "\n");
998       exit(2);
999    }
1000 #endif /* !defined(_WIN32) || defined(_WIN_CONSOLE) */
1001
1002 #ifdef AMIGA
1003    InitAmiga();
1004 #endif
1005
1006    Argc = argc;
1007    Argv = argv;
1008
1009    if (argc > 1)
1010    {
1011       configfile = argv[1];
1012    }
1013
1014    files->next = NULL;
1015
1016 #ifdef _WIN32
1017    InitWin32();
1018 #endif
1019
1020
1021 #ifndef _WIN32
1022    signal(SIGPIPE, SIG_IGN);
1023    signal(SIGCHLD, SIG_IGN);
1024
1025 #else /* ifdef _WIN32 */
1026 # ifdef _WIN_CONSOLE
1027    /*
1028     * We *are* in a windows console app.
1029     * Print a verbose messages about FAQ's and such
1030     */
1031    printf(win32_blurb);
1032 # endif /* def _WIN_CONSOLE */
1033 #endif /* def _WIN32 */
1034
1035
1036    listen_loop();
1037
1038    /* NOTREACHED */
1039    return(-1);
1040
1041 }
1042
1043
1044 /*********************************************************************
1045  *
1046  * Function    :  listen_loop
1047  *
1048  * Description :  bind the listen port and enter a "FOREVER" listening loop.
1049  *
1050  * Parameters  :  N/A
1051  *
1052  * Returns     :  Never.
1053  *
1054  *********************************************************************/
1055 static void listen_loop(void)
1056 {
1057    struct client_state *csp = NULL;
1058    int bfd;
1059    struct configuration_spec * config;
1060
1061    config = load_config();
1062
1063    log_error(LOG_LEVEL_CONNECT, "bind (%s, %d)",
1064              config->haddr ? config->haddr : "INADDR_ANY", config->hport);
1065
1066    bfd = bind_port(config->haddr, config->hport);
1067
1068    if (bfd < 0)
1069    {
1070       log_error(LOG_LEVEL_FATAL, "can't bind %s:%d: %E "
1071          "- There may be another junkbuster or some other "
1072          "proxy running on port %d", 
1073          (NULL != config->haddr) ? config->haddr : "INADDR_ANY", 
1074          config->hport, config->hport
1075       );
1076       /* shouldn't get here */
1077       return;
1078    }
1079
1080    config->need_bind = 0;
1081
1082
1083    while (FOREVER)
1084    {
1085 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
1086       while (waitpid(-1, NULL, WNOHANG) > 0)
1087       {
1088          /* zombie children */
1089       }
1090 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
1091       sweep();
1092
1093       if ( NULL == (csp = (struct client_state *) malloc(sizeof(*csp))) )
1094       {
1095          log_error(LOG_LEVEL_FATAL, "malloc(%d) for csp failed: %E", sizeof(*csp));
1096          continue;
1097       }
1098
1099       memset(csp, '\0', sizeof(*csp));
1100
1101       csp->active = 1;
1102       csp->sfd    = -1;
1103
1104       csp->config = config = load_config();
1105
1106       if ( config->need_bind )
1107       {
1108          /*
1109           * Since we were listening to the "old port", we will not see
1110           * a "listen" param change until the next IJB request.  So, at
1111           * least 1 more request must be made for us to find the new
1112           * setting.  I am simply closing the old socket and binding the
1113           * new one.
1114           *
1115           * Which-ever is correct, we will serve 1 more page via the
1116           * old settings.  This should probably be a "show-proxy-args"
1117           * request.  This should not be a so common of an operation
1118           * that this will hurt people's feelings.
1119           */
1120
1121          close_socket(bfd);
1122
1123          log_error(LOG_LEVEL_CONNECT, "bind (%s, %d)",
1124                    config->haddr ? config->haddr : "INADDR_ANY", config->hport);
1125          bfd = bind_port(config->haddr, config->hport);
1126
1127          if (bfd < 0)
1128          {
1129             log_error(LOG_LEVEL_FATAL, "can't bind %s:%d: %E "
1130                "- There may be another junkbuster or some other "
1131                "proxy running on port %d", 
1132                (NULL != config->haddr) ? config->haddr : "INADDR_ANY", 
1133                config->hport, config->hport
1134             );
1135             /* shouldn't get here */
1136             return;
1137          }
1138
1139          config->need_bind = 0;
1140       }
1141
1142       log_error(LOG_LEVEL_CONNECT, "accept connection ... ");
1143
1144       if (!accept_connection(csp, bfd))
1145       {
1146          log_error(LOG_LEVEL_CONNECT, "accept failed: %E");
1147
1148 #ifdef AMIGA
1149          if(!childs)
1150          {
1151             exit(1); 
1152          }
1153 #endif
1154          freez(csp);
1155          continue;
1156       }
1157       else
1158       {
1159          log_error(LOG_LEVEL_CONNECT, "OK");
1160       }
1161
1162 #if defined(TOGGLE)
1163       /* by haroon - most of credit to srt19170 */
1164       csp->toggled_on = g_bToggleIJB;
1165 #endif
1166
1167       if (run_loader(csp))
1168       {
1169          log_error(LOG_LEVEL_FATAL, "a loader failed - must exit");
1170          /* Never get here - LOG_LEVEL_FATAL causes program exit */
1171       }
1172
1173       if (block_acl(NULL,csp))
1174       {
1175          log_error(LOG_LEVEL_CONNECT, "Connection dropped due to ACL");
1176          close_socket(csp->cfd);
1177          freez(csp);
1178          continue;
1179       }
1180
1181       /* add it to the list of clients */
1182       csp->next = clients->next;
1183       clients->next = csp;
1184
1185       if (config->multi_threaded)
1186       {
1187          int child_id;
1188
1189 /* this is a switch () statment in the C preprocessor - ugh */
1190 #undef SELECTED_ONE_OPTION
1191
1192 #if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
1193 #define SELECTED_ONE_OPTION
1194          child_id = _beginthread(
1195             (void*)serve,
1196             64 * 1024,
1197             csp);
1198 #endif
1199
1200 #if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION)
1201 #define SELECTED_ONE_OPTION
1202          {
1203             thread_id tid = spawn_thread
1204                (server_thread, "server", B_NORMAL_PRIORITY, csp);
1205
1206             if ((tid >= 0) && (resume_thread(tid) == B_OK))
1207             {
1208                child_id = (int) tid;
1209             }
1210             else
1211             {
1212                child_id = -1;
1213             }
1214          }
1215 #endif
1216
1217 #if defined(AMIGA) && !defined(SELECTED_ONE_OPTION)
1218 #define SELECTED_ONE_OPTION
1219          csp->cfd = ReleaseSocket(csp->cfd, -1);
1220          if((child_id = (int)CreateNewProcTags(
1221             NP_Entry, (ULONG)server_thread,
1222             NP_Output, Output(),
1223             NP_CloseOutput, FALSE,
1224             NP_Name, (ULONG)"junkbuster child",
1225             NP_StackSize, 20*1024,
1226             TAG_DONE)))
1227          {
1228             childs++;
1229             ((struct Task *)child_id)->tc_UserData = csp;
1230             Signal((struct Task *)child_id, SIGF_SINGLE);
1231             Wait(SIGF_SINGLE);
1232          }
1233 #endif
1234
1235 #if !defined(SELECTED_ONE_OPTION)
1236          child_id = fork();
1237 #endif
1238
1239 #undef SELECTED_ONE_OPTION
1240 /* end of cpp switch () */
1241
1242          if (child_id < 0) /* failed */
1243          {
1244             char buf[BUFSIZ];
1245
1246             log_error(LOG_LEVEL_ERROR, "can't fork: %E");
1247
1248             sprintf(buf , "JunkBuster: can't fork: errno = %d", errno);
1249
1250             write_socket(csp->cfd, buf, strlen(buf));
1251             close_socket(csp->cfd);
1252             csp->active = 0;
1253             sleep(5);
1254             continue;
1255          }
1256
1257 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
1258          /* This block is only needed when using fork().
1259           * When using threads, the server thread was
1260           * created and run by the call to _beginthread().
1261           */
1262          if (child_id == 0)   /* child */
1263          {
1264             serve(csp);
1265             _exit(0);
1266
1267          }
1268          else  /* parent */
1269          {
1270             /* in a fork()'d environment, the parent's
1271              * copy of the client socket and the CSP
1272              * are not used.
1273              */
1274
1275 #if !defined(_WIN32) && defined(__CYGWIN__)
1276             wait( NULL );
1277 #endif /* !defined(_WIN32) && defined(__CYGWIN__) */
1278             close_socket(csp->cfd);
1279             csp->active = 0;
1280          }
1281 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
1282       }
1283       else
1284       {
1285          serve(csp);
1286       }
1287    }
1288    /* NOTREACHED */
1289
1290 }
1291
1292
1293 /*
1294   Local Variables:
1295   tab-width: 3
1296   end:
1297 */