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