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