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