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