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