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