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