- Enabled filtering banners by size rather than URL
[privoxy.git] / jcc.c
1 const char jcc_rcs[] = "$Id: jcc.c,v 1.4 2001/05/21 19:34:01 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.4  2001/05/21 19:34:01  jongfoster
37  *    Made failure to bind() a fatal error.
38  *
39  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
40  *    Version 2.9.4 checkin.
41  *    - Merged popupfile and cookiefile, and added control over PCRS
42  *      filtering, in new "permissionsfile".
43  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
44  *      file error you now get a message box (in the Win32 GUI) rather
45  *      than the program exiting with no explanation.
46  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
47  *      skipping.
48  *    - Removed tabs from "config"
49  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
50  *    - Bumped up version number.
51  *
52  *    Revision 1.2  2001/05/17 22:34:44  oes
53  *     - Added hint on GIF char array generation to jcc.c
54  *     - Cleaned CRLF's from the sources and related files
55  *     - Repaired logging for REF and FRC
56  *
57  *    Revision 1.1.1.1  2001/05/15 13:58:56  oes
58  *    Initial import of version 2.9.3 source tree
59  *
60  *
61  *********************************************************************/
62 \f
63
64 #include "config.h"
65
66 #include <stdio.h>
67 #include <sys/types.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <signal.h>
71 #include <fcntl.h>
72 #include <errno.h>
73
74 #ifdef _WIN32
75
76 # include <sys/timeb.h>
77 # include <windows.h>
78 # include <io.h>
79 # include <process.h>
80 # ifdef TOGGLE
81 #  include <time.h>
82 # endif /* def TOGGLE */
83
84 # include "win32.h"
85 # ifndef _WIN_CONSOLE
86 #  include "w32log.h"
87 # endif /* ndef _WIN_CONSOLE */
88
89 #else /* ifndef _WIN32 */
90
91 # include <unistd.h>
92 # include <sys/time.h>
93 # include <sys/wait.h>
94 # include <sys/stat.h>
95 # include <signal.h>
96
97 # ifdef __BEOS__
98 #  include <socket.h>  /* BeOS has select() for sockets only. */
99 #  include <OS.h>      /* declarations for threads and stuff. */
100 # endif
101
102 # ifndef FD_ZERO
103 #  include <select.h>
104 # endif
105
106 #endif
107
108 #include "project.h"
109 #include "jcc.h"
110 #include "filters.h"
111 #include "loaders.h"
112 #include "showargs.h"
113 #include "parsers.h"
114 #include "killpopup.h"
115 #include "miscutil.h"
116 #include "errlog.h"
117 #include "jbsockets.h"
118 #include "gateway.h"
119
120 const char jcc_h_rcs[] = JCC_H_VERSION;
121 const char project_h_rcs[] = PROJECT_H_VERSION;
122
123 const char DEFAULT_USER_AGENT[] ="User-Agent: Mozilla (X11; I; Linux 2.0.32 i586)";
124
125 struct client_state  clients[1];
126 struct file_list     files[1];
127
128 #ifdef STATISTICS
129 int urls_read     = 0;     /* total nr of urls read inc rejected */
130 int urls_rejected = 0;     /* total nr of urls rejected */
131 #endif /* def STATISTICS */
132
133
134 static void listen_loop(void);
135 static void chat(struct client_state *csp);
136 static void serve(struct client_state *csp);
137 #ifdef __BEOS__
138 static int32 server_thread(void *data);
139 #endif /* def __BEOS__ */
140
141 #ifdef _WIN32
142 #define sleep(N)  Sleep(((N) * 1000))
143 #endif
144
145
146 /*********************************************************************
147  *
148  * Function    :  chat
149  *
150  * Description :  Once a connection to the client has been accepted,
151  *                this function is called (via serve()) to handle the
152  *                main business of the communication.  When this 
153  *                function returns, the caller must close the client
154  *                socket handle.
155  *
156  * Parameters  :
157  *          1  :  csp = Current client state (buffers, headers, etc...)
158  *
159  * Returns     :  On success, the number of bytes written are returned (zero
160  *                indicates nothing was written).  On error, -1 is returned,
161  *                and errno is set appropriately.  If count is zero and the
162  *                file descriptor refers to a regular file, 0 will be
163  *                returned without causing any other effect.  For a special
164  *                file, the results are not portable.
165  *
166  *********************************************************************/
167 static void chat(struct client_state *csp)
168 {
169 /* This next line is a little ugly, but it simplifies the if statement below. */
170 /* Basically if TOGGLE, then we want the if to test "csp->toggled_on", else we don't */
171 #ifdef TOGGLE
172 #   define IS_TOGGLED_ON csp->toggled_on &&
173 #else /* ifndef TOGGLE */
174 #   define IS_TOGGLED_ON
175 #endif /* ndef TOGGLE */
176
177 /* This next line is a little ugly, but it simplifies the if statement below. */
178 /* Basically if TRUST_FILES, then we want the if to call "trust_url", else we don't */
179 #ifdef TRUST_FILES
180 #   define IS_TRUSTED_URL (p = trust_url(http, csp)) ||
181 #else /* ifndef TRUST_FILES */
182 #   define IS_TRUSTED_URL
183 #endif /* ndef TRUST_FILES */
184
185    char buf[BUFSIZ], *hdr, *p, *req;
186    char *err = NULL;
187    char *eno;
188    fd_set rfds;
189    int n, maxfd, server_body, ms_iis5_hack = 0;
190    const struct gateway *gw;
191    struct http_request *http;
192 #ifdef KILLPOPUPS
193    int block_popups;         /* bool, 1==will block popups */
194    int block_popups_now = 0; /* bool, 1==currently blocking popups */
195 #endif /* def KILLPOPUPS */
196 #ifdef PCRS
197    int pcrs_filter;   /* bool, 1==will filter through pcrs */
198    int filtering = 0; /* bool, 1==currently filtering through pcrs */
199 #endif /* def PCRS */
200
201    http = csp->http;
202
203    /*
204     * Read the client's request.  Note that since we're not using select() we
205     * could get blocked here if a client connected, then didn't say anything!
206     */
207
208    while (FOREVER)
209    {
210       n = read_socket(csp->cfd, buf, sizeof(buf));
211
212       if (n <= 0) break;      /* error! */
213
214       add_to_iob(csp, buf, n);
215
216       req = get_header(csp);
217
218       if (req == NULL)
219       {
220          break;    /* no HTTP request! */
221       }
222
223       if (*req == '\0')
224       {
225          continue;   /* more to come! */
226       }
227  
228 #ifdef FORCE_LOAD
229       /* If this request contains the FORCE_PREFIX,
230                  * better get rid of it now and set the force flag --oes
231        */
232
233                 if(strstr(req, FORCE_PREFIX))
234       {
235                    strclean(req, FORCE_PREFIX);
236                    log_error(LOG_LEVEL_FORCE, "Enforcing request \"%s\".\n", req);
237                    csp->force = 1;
238                 } 
239       else
240       {
241                    csp->force = 0;
242                 }
243 #endif /* def FORCE_LOAD */
244   
245       parse_http_request(req, http, csp);
246       freez(req);
247       break;
248    }
249
250    if (http->cmd == NULL)
251    {
252       strcpy(buf, CHEADER);
253       write_socket(csp->cfd, buf, strlen(buf));
254       return;
255    }
256
257    /* decide how to route the HTTP request */
258
259    if ((gw = forward_url(http, csp)) == NULL)
260    {
261       log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!?  This can't happen!");
262       /* Never get here - LOG_LEVEL_FATAL causes program exit */
263    }
264
265    /* build the http request to send to the server
266     * we have to do one of the following:
267     *
268     * create = use the original HTTP request to create a new
269     *          HTTP request that has only the path component
270     *          without the http://domainspec
271     * pass   = pass the original HTTP request unchanged
272     *
273     * drop   = drop the HTTP request
274     *
275     * here's the matrix:
276     *                        SSL
277     *                    0        1
278     *                +--------+--------+
279     *                |        |        |
280     *             0  | create | drop   |
281     *                |        |        |
282     *  Forwarding    +--------+--------+
283     *                |        |        |
284     *             1  | pass   | pass   |
285     *                |        |        |
286     *                +--------+--------+
287     *
288     */
289
290    if (gw->forward_host)
291    {
292       /* if forwarding, just pass the request as is */
293       enlist(csp->headers, http->cmd);
294    }
295    else
296    {
297       if (http->ssl == 0)
298       {
299          /* otherwise elide the host information from the url */
300          p = NULL;
301          p = strsav(p, http->gpc);
302          p = strsav(p, " ");
303          p = strsav(p, http->path);
304          p = strsav(p, " ");
305          p = strsav(p, http->ver);
306          enlist(csp->headers, p);
307          freez(p);
308       }
309    }
310
311    /* decide what we're to do with cookies */
312
313 #ifdef TOGGLE
314    if (!csp->toggled_on)
315    {
316       /* Most compatible set of permissions */
317       csp->permissions = PERMIT_COOKIE_SET | PERMIT_COOKIE_READ | PERMIT_POPUPS;
318    }
319    else
320    {
321       csp->permissions = url_permissions(http, csp);
322    }
323 #else /* ifndef TOGGLE */
324    csp->permissions = url_permissions(http, csp);
325 #endif /* ndef TOGGLE */
326
327 #ifdef KILLPOPUPS
328    block_popups               = ((csp->permissions & PERMIT_POPUPS) == 0);
329 #endif /* def KILLPOPUPS */
330 #ifdef PCRS
331    pcrs_filter                = (csp->rlist != NULL) &&  /* There are expressions to be used */
332                                 ((csp->permissions & PERMIT_RE_FILTER) != 0);
333 #endif /* def PCRS */
334
335
336    /* grab the rest of the client's headers */
337
338    while (FOREVER)
339    {
340       if ( ( p = get_header(csp) ) && ( *p == '\0' ) )
341       {
342          n = read_socket(csp->cfd, buf, sizeof(buf));
343          if (n <= 0)
344          {
345             log_error(LOG_LEVEL_ERROR, "read from client failed: %E");
346             return;
347          }
348          add_to_iob(csp, buf, n);
349          continue;
350       }
351
352       if (p == NULL) break;
353
354       enlist(csp->headers, p);
355       freez(p);
356    }
357
358    /* filter it as required */
359
360    hdr = sed(client_patterns, add_client_headers, csp);
361
362    destroy_list(csp->headers);
363
364         /* Check the request against all rules, unless
365          * we're toggled off or in force mode. 
366     */
367  
368    if (IS_TOGGLED_ON
369 #ifdef FORCE_LOAD
370        (!csp->force) && 
371 #endif /* def FORCE_LOAD */
372        ( (p = intercept_url(http, csp)) ||
373          IS_TRUSTED_URL
374          (p = block_url(http, csp))
375 #ifdef FAST_REDIRECTS
376          || (fast_redirects && (p = redirect_url(http, csp))) 
377 #endif /* def FAST_REDIRECTS */
378       ))
379    {
380 #ifdef STATISTICS
381       csp->rejected = 1;
382 #endif /* def STATISTICS */
383
384       log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path);
385
386 #if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
387       /* Block as image?  */
388       if ( (tinygif > 0) && block_imageurl(http, csp) )
389       {
390          /* Send "blocked" image */
391          log_error(LOG_LEVEL_GPC, "%s%s image crunch!",
392                    http->hostport, http->path);
393
394          if ((tinygif == 2) || strstr(http->path, "ijb-send-banner"))
395          {
396             write_socket(csp->cfd, JBGIF, sizeof(JBGIF)-1);
397          }
398          if (tinygif == 1)
399          {
400             write_socket(csp->cfd, BLANKGIF, sizeof(BLANKGIF)-1);
401          }
402          else if ((tinygif == 3) && (tinygifurl))
403          {
404             p = (char *)malloc(strlen(HTTP_REDIRECT_TEMPLATE) + strlen(tinygifurl));
405                  sprintf(p, HTTP_REDIRECT_TEMPLATE, tinygifurl);
406             write_socket(csp->cfd, p, strlen(p));
407          }
408       }
409       else
410 #endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
411       /* Block as HTML */
412       {
413          /* Send HTML "blocked" message, interception, or redirection result */
414          write_socket(csp->cfd, p, strlen(p));
415       }
416
417       log_error(LOG_LEVEL_LOG, "%s", p);
418
419       freez(p);
420       freez(hdr);
421       return;
422    }
423
424    log_error(LOG_LEVEL_GPC, "%s%s", http->hostport, http->path);
425
426    if (gw->forward_host)
427    {
428       log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s",
429                gw->forward_host, gw->forward_port, http->hostport);
430    }
431    else
432    {
433       log_error(LOG_LEVEL_CONNECT, "to %s", http->hostport);
434    }
435
436    /* here we connect to the server, gateway, or the forwarder */
437
438    csp->sfd = (gw->conn)(gw, http, csp);
439
440    if (csp->sfd < 0)
441    {
442       log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E",
443                 http->hostport);
444
445       if (errno == EINVAL)
446       {
447          err = zalloc(strlen(CNXDOM) + strlen(http->host));
448          sprintf(err, CNXDOM, http->host);
449       }
450       else
451       {
452          eno = safe_strerror(errno);
453          err = zalloc(strlen(CFAIL) + strlen(http->hostport) + strlen(eno));
454          sprintf(err, CFAIL, http->hostport, eno);
455       }
456
457       write_socket(csp->cfd, err, strlen(err));
458
459       log_error(LOG_LEVEL_LOG, err);
460
461       freez(err);
462       freez(hdr);
463       return;
464    }
465
466    log_error(LOG_LEVEL_CONNECT, "OK");
467
468    if (gw->forward_host || (http->ssl == 0))
469    {
470       /* write the client's (modified) header to the server
471        * (along with anything else that may be in the buffer)
472        */
473
474       n = strlen(hdr);
475
476       if ((write_socket(csp->sfd, hdr, n) != n)
477           || (flush_socket(csp->sfd, csp   ) <  0))
478       {
479          log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E",
480                     http->hostport);
481
482          eno = safe_strerror(errno);
483          err = zalloc(strlen(CFAIL) + strlen(http->hostport) + strlen(eno));
484          sprintf(err, CFAIL, http->hostport, eno);
485          write_socket(csp->cfd, err, strlen(err));
486
487          freez(err);
488          freez(hdr);
489          return;
490       }
491    }
492    else
493    {
494       /*
495        * We're running an SSL tunnel and we're not forwarding,
496        * so just send the "connect succeeded" message to the
497        * client, flush the rest, and get out of the way.
498        */
499       if (write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1) < 0)
500       {
501          freez(hdr);
502          return;
503       }
504       IOB_RESET(csp);
505    }
506
507    /* we're finished with the client's header */
508    freez(hdr);
509
510    maxfd = ( csp->cfd > csp->sfd ) ? csp->cfd : csp->sfd;
511
512    /* pass data between the client and server
513     * until one or the other shuts down the connection.
514     */
515
516    server_body = 0;
517
518    while (FOREVER)
519    {
520       FD_ZERO(&rfds);
521
522       FD_SET(csp->cfd, &rfds);
523       FD_SET(csp->sfd, &rfds);
524
525       n = select(maxfd+1, &rfds, NULL, NULL, NULL);
526
527       if (n < 0)
528       {
529          log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
530          return;
531       }
532
533       /* this is the body of the browser's request
534        * just read it and write it.
535        */
536
537       if (FD_ISSET(csp->cfd, &rfds))
538       {
539          n = read_socket(csp->cfd, buf, sizeof(buf));
540
541          if (n <= 0) break; /* "game over, man" */
542
543          if (write_socket(csp->sfd, buf, n) != n)
544          {
545             log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
546             return;
547          }
548          continue;
549       }
550
551       /*
552        * The server wants to talk.  It could be the header or the body.
553        * If `hdr' is null, then it's the header otherwise it's the body.
554        * FIXME: Does `hdr' really mean `host'?
555        */
556
557
558       if (FD_ISSET(csp->sfd, &rfds))
559       {
560          fflush( 0 );
561          n = read_socket(csp->sfd, buf, sizeof(buf) - 1);
562
563          if (n < 0)
564          {
565             log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
566
567             eno = safe_strerror(errno);
568             sprintf(buf, CFAIL, http->hostport, eno);
569             freez(eno);
570             write_socket(csp->cfd, buf, strlen(buf));
571             return;
572          }
573
574          /* Add a trailing zero.  This lets filter_popups
575           * use string operations.
576           */
577          buf[n] = '\0';
578
579 #ifdef KILLPOPUPS
580          /* Filter the popups on this read. */
581          if (block_popups_now)
582          {
583             filter_popups(buf, n);
584          }
585 #endif /* def KILLPOPUPS */
586
587          /* Normally, this would indicate that we've read
588           * as much as the server has sent us and we can
589           * close the client connection.  However, Microsoft
590           * in its wisdom has released IIS/5 with a bug that
591           * prevents it from sending the trailing \r\n in
592           * a 302 redirect header (and possibly other headers).
593           * To work around this if we've haven't parsed
594           * a full header we'll append a trailing \r\n
595           * and see if this now generates a valid one.
596           *
597           * This hack shouldn't have any impacts.  If we've
598           * already transmitted the header or if this is a
599           * SSL connection, then we won't bother with this
600           * hack.  So we only work on partially received
601           * headers.  If we append a \r\n and this still
602           * doesn't generate a valid header, then we won't
603           * transmit anything to the client.
604           */
605          if (n == 0)
606          {
607             /* This hack must only be enforced for headers. */
608             if (server_body || http->ssl)
609             {
610 #ifdef PCRS
611                if (filtering)
612                {
613                   re_process_buffer(csp);
614                }
615 #endif /* def PCRS */
616                break; /* "game over, man" */
617             }
618
619             /* Let's pretend the server just sent us a blank line. */
620             n = sprintf(buf, "\r\n");
621
622             /*
623              * Now, let the normal header parsing algorithm below do its
624              * job.  If it fails, we'll exit instead of continuing.
625              */
626
627             ms_iis5_hack = 1;
628          }
629
630          /*
631           * If this is an SSL connection or we're in the body
632           * of the server document, just write it to the client.
633           */
634
635          if (server_body || http->ssl)
636          {
637 #ifdef PCRS
638             if (filtering)
639             {
640                add_to_iob(csp, buf, n); /* Buffer the body for filtering */
641             }
642             else
643 #endif /* def PCRS */
644             {
645                /* just write */
646                if (write_socket(csp->cfd, buf, n) != n)
647                {
648                   log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
649                   return;
650                }
651             }
652             continue;
653          }
654          else
655          {
656             /* we're still looking for the end of the
657              * server's header ... (does that make header
658              * parsing an "out of body experience" ?
659              */
660
661             /* buffer up the data we just read */
662             add_to_iob(csp, buf, n);
663
664             /* get header lines from the iob */
665
666             while ((p = get_header(csp)))
667             {
668                if (*p == '\0')
669                {
670                   /* see following note */
671                   break;
672                }
673                enlist(csp->headers, p);
674                freez(p);
675             }
676
677             /* NOTE: there are no "empty" headers so
678              * if the pointer `p' is not NULL we must
679              * assume that we reached the end of the
680              * buffer before we hit the end of the header.
681              */
682
683             if (p)
684             {
685                if (ms_iis5_hack)
686                {
687                   /* Well, we tried our MS IIS/5
688                    * hack and it didn't work.
689                    * The header is incomplete
690                    * and there isn't anything
691                    * we can do about it.
692                    */
693                   break;
694                }
695                else
696                {
697                   /* Since we have to wait for
698                    * more from the server before
699                    * we can parse the headers
700                    * we just continue here.
701                    */
702                   continue;
703                }
704             }
705
706             /* we have now received the entire header.
707              * filter it and send the result to the client
708              */
709
710             hdr = sed(server_patterns, add_server_headers, csp);
711             n   = strlen(hdr);
712
713             /* write the server's (modified) header to
714              * the client (along with anything else that
715              * may be in the buffer)
716              */
717
718 #ifdef KILLPOPUPS
719             /* Start blocking popups if appropriate. */
720
721             if (csp->is_text  &&  /* It's a text / * MIME-Type */
722                 !http->ssl    &&  /* We talk plaintext */
723                 block_popups)
724             {
725                block_popups_now = 1;
726             }
727
728 #endif /* def KILLPOPUPS */
729
730 #ifdef PCRS
731             /* Start re_filtering this if appropriate. */
732
733             if (csp->is_text  &&  /* It's a text / * MIME-Type */
734                 !http->ssl    &&  /* We talk plaintext */
735                 pcrs_filter)      /* Policy allows */
736             {
737                filtering = 1;
738             }
739
740 /* This next line is a little ugly, but it simplifies the if statement below. */
741 /* Basically if using PCRS, we want the OR condition to require "!filtering"  */
742 #define NOT_FILTERING_AND !filtering &&
743
744 #else /* not def PCRS */
745
746 #define NOT_FILTERING_AND
747
748 #endif /* def PCRS */
749
750
751             if ((write_socket(csp->cfd, hdr, n) != n)
752                 || (NOT_FILTERING_AND (flush_socket(csp->cfd, csp) < 0)))
753             {
754                log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
755
756                /* the write failed, so don't bother
757                 * mentioning it to the client...
758                 * it probably can't hear us anyway.
759                 */
760                freez(hdr);
761                return;
762             }
763
764             /* we're finished with the server's header */
765
766             freez(hdr);
767             server_body = 1;
768
769             /* If this was a MS IIS/5 hack then it means
770              * the server has already closed the
771              * connection.  Nothing more to read.  Time
772              * to bail.
773              */
774             if (ms_iis5_hack)
775             {
776                break;
777             }
778          }
779          continue;
780       }
781
782       return; /* huh? we should never get here */
783    }
784
785
786 }
787
788
789 /*********************************************************************
790  *
791  * Function    :  serve
792  *
793  * Description :  This is little more than chat.  We only "serve" to
794  *                to close any socket that chat may have opened.
795  *
796  * Parameters  :
797  *          1  :  csp = Current client state (buffers, headers, etc...)
798  *
799  * Returns     :  N/A
800  *
801  *********************************************************************/
802 static void serve(struct client_state *csp)
803 {
804    chat(csp);
805    close_socket(csp->cfd);
806
807    if (csp->sfd >= 0)
808    {
809       close_socket(csp->sfd);
810    }
811
812    csp->active = 0;
813
814 }
815
816
817 #ifdef __BEOS__
818
819 /*********************************************************************
820  *
821  * Function    :  server_thread
822  *
823  * Description :  We only exist to call `serve' in a threaded environment.
824  *
825  * Parameters  :
826  *          1  :  data = Current client state (buffers, headers, etc...)
827  *
828  * Returns     :  Always 0.
829  *
830  *********************************************************************/
831 static int32 server_thread(void *data)
832 {
833    serve((struct client_state *) data);
834    return 0;
835
836 }
837
838 #endif
839
840
841 /*********************************************************************
842  *
843  * Function    :  main
844  *
845  * Description :  Load the config file and start the listen loop.
846  *                This function is a lot more *sane* with the `load_config'
847  *                and `listen_loop' functions; although it stills does
848  *                a *little* too much for my taste.
849  *
850  * Parameters  :
851  *          1  :  argc = Number of parameters (including $0).
852  *          2  :  argv = Array of (char *)'s to the parameters.
853  *
854  * Returns     :  1 if : can't open config file, unrecognized directive,
855  *                stats requested in multi-thread mode, can't open the
856  *                log file, can't open the jar file, listen port is invalid,
857  *                any load fails, and can't bind port.
858  *
859  *                Else main never returns, the process must be signaled
860  *                to terminate execution.  Or, on Windows, use the 
861  *                "File", "Exit" menu option.
862  *
863  *********************************************************************/
864 #ifdef __MINGW32__
865 int _main(int argc, const char *argv[])
866 #else
867 int main(int argc, const char *argv[])
868 #endif
869 {
870    configfile =
871 #ifndef _WIN32
872    "config"
873 #else
874    "junkbstr.txt"
875 #endif
876       ;
877
878 #if !defined(_WIN32) || defined(_WIN_CONSOLE)
879    if ((argc >= 2) && (strcmp(argv[1], "--help")==0))
880    {
881       printf("JunkBuster proxy version " VERSION ".\n\n"
882          "Usage: %s [configfile]\n\n"
883          "See " HOME_PAGE_URL " for details.\n"
884          "This program is distributed under the GNU GPL, version 2 or later.\n",
885          argv[0]);
886       exit(2);
887    }
888    if ((argc >= 2) && (strcmp(argv[1], "--version")==0))
889    {
890       printf(VERSION "\n");
891       exit(2);
892    }
893 #endif /* !defined(_WIN32) || defined(_WIN_CONSOLE) */
894
895    Argc = argc;
896    Argv = argv;
897
898    if (argc > 1)
899    {
900       configfile = argv[1];
901    }
902
903    remove_all_loaders();
904    memset( proxy_args, 0, sizeof( proxy_args ) );
905    files->next = NULL;
906
907    load_config( 0 );
908
909    /*
910     * Since load_config acts as a signal handler too, it returns
911     * its status in configret.  Check it for an error in loading.
912     */
913    if ( 0 != configret )
914    {
915       /* load config failed!  Exit with error. */
916       return( 1 );
917    }
918
919 #ifdef _WIN32
920    InitWin32();
921 #endif
922
923
924 #ifndef _WIN32
925    signal(SIGPIPE, SIG_IGN);
926    signal(SIGCHLD, SIG_IGN);
927    signal(SIGHUP, load_config);
928
929 #else /* ifdef _WIN32 */
930 # ifdef _WIN_CONSOLE
931    /*
932     * We *are* in a windows console app.
933     * Print a verbose messages about FAQ's and such
934     */
935    printf(win32_blurb);
936 # endif /* def _WIN_CONSOLE */
937 #endif /* def _WIN32 */
938
939
940    listen_loop();
941
942    /* NOTREACHED */
943    return(-1);
944
945 }
946
947
948 /*********************************************************************
949  *
950  * Function    :  listen_loop
951  *
952  * Description :  bind the listen port and enter a "FOREVER" listening loop.
953  *
954  * Parameters  :  N/A
955  *
956  * Returns     :  Never.
957  *
958  *********************************************************************/
959 static void listen_loop(void)
960 {
961    struct client_state *csp = NULL;
962    int bfd;
963
964    log_error(LOG_LEVEL_CONNECT, "bind (%s, %d)",
965              haddr ? haddr : "INADDR_ANY", hport);
966
967    bfd = bind_port(haddr, hport);
968    config_changed = 0;
969
970    if (bfd < 0)
971    {
972       log_error(LOG_LEVEL_FATAL, "can't bind %s:%d: %E "
973          "- There may be another junkbuster or some other "
974          "proxy running on port %d", 
975          (NULL != haddr) ? haddr : "INADDR_ANY", hport, hport
976       );
977       /* shouldn't get here */
978       return;
979    }
980
981
982    while (FOREVER)
983    {
984 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
985       while (waitpid(-1, NULL, WNOHANG) > 0)
986       {
987          /* zombie children */
988       }
989 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
990       sweep();
991
992       if ( NULL == (csp = (struct client_state *) malloc(sizeof(*csp))) )
993       {
994          log_error(LOG_LEVEL_ERROR, "malloc(%d) for csp failed: %E", sizeof(*csp));
995          continue;
996       }
997
998       memset(csp, '\0', sizeof(*csp));
999
1000       csp->active = 1;
1001       csp->sfd    = -1;
1002
1003       if ( config_changed )
1004       {
1005          /*
1006           * Since we were listening to the "old port", we will not see
1007           * a "listen" param change until the next IJB request.  So, at
1008           * least 1 more request must be made for us to find the new
1009           * setting.  I am simply closing the old socket and binding the
1010           * new one.
1011           *
1012           * Which-ever is correct, we will serve 1 more page via the
1013           * old settings.  This should probably be a "show-proxy-args"
1014           * request.  This should not be a so common of an operation
1015           * that this will hurt people's feelings.
1016           */
1017          close_socket(bfd);
1018
1019          log_error(LOG_LEVEL_CONNECT, "bind (%s, %d)",
1020                    haddr ? haddr : "INADDR_ANY", hport);
1021          bfd = bind_port(haddr, hport);
1022
1023          config_changed = 0;
1024       }
1025
1026       log_error(LOG_LEVEL_CONNECT, "accept connection ... ");
1027
1028       if (!accept_connection(csp, bfd))
1029       {
1030          log_error(LOG_LEVEL_CONNECT, "accept failed: %E");
1031
1032 #ifdef AMIGA
1033          if(!childs)
1034          {
1035             exit(1); 
1036          }
1037 #endif
1038          continue;
1039       }
1040       else
1041       {
1042          log_error(LOG_LEVEL_CONNECT, "OK");
1043       }
1044
1045 #if defined(TOGGLE)
1046       /* by haroon - most of credit to srt19170 */
1047       csp->toggled_on = g_bToggleIJB;
1048 #endif
1049
1050       /* add it to the list of clients */
1051       csp->next = clients->next;
1052       clients->next = csp;
1053
1054       if (run_loader(csp))
1055       {
1056          log_error(LOG_LEVEL_FATAL, "a loader failed - must exit");
1057          /* Never get here - LOG_LEVEL_FATAL causes program exit */
1058       }
1059
1060       if (multi_threaded)
1061       {
1062          int child_id;
1063
1064 /* this is a switch () statment in the C preprocessor - ugh */
1065 #undef SELECTED_ONE_OPTION
1066
1067 #if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
1068 #define SELECTED_ONE_OPTION
1069          child_id = _beginthread(
1070             (void*)serve,
1071             64 * 1024,
1072             csp);
1073 #endif
1074
1075 #if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION)
1076 #define SELECTED_ONE_OPTION
1077          {
1078             thread_id tid = spawn_thread
1079                (server_thread, "server", B_NORMAL_PRIORITY, csp);
1080
1081             if ((tid >= 0) && (resume_thread(tid) == B_OK))
1082             {
1083                child_id = (int) tid;
1084             }
1085             else
1086             {
1087                child_id = -1;
1088             }
1089          }
1090 #endif
1091
1092 #if defined(AMIGA) && !defined(SELECTED_ONE_OPTION)
1093 #define SELECTED_ONE_OPTION
1094          csp->cfd = ReleaseSocket(csp->cfd, -1);
1095          if((child_id = (int)CreateNewProcTags(
1096             NP_Entry, (ULONG)server_thread,
1097             NP_Output, Output(),
1098             NP_CloseOutput, FALSE,
1099             NP_Name, (ULONG)"junkbuster child",
1100             NP_StackSize, 20*1024,
1101             TAG_DONE)))
1102          {
1103             childs++;
1104             ((struct Task *)child_id)->tc_UserData = csp;
1105             Signal((struct Task *)child_id, SIGF_SINGLE);
1106             Wait(SIGF_SINGLE);
1107          }
1108 #endif
1109
1110 #if !defined(SELECTED_ONE_OPTION)
1111          child_id = fork();
1112 #endif
1113
1114 #undef SELECTED_ONE_OPTION
1115 /* end of cpp switch () */
1116
1117          if (child_id < 0) /* failed */
1118          {
1119             char buf[BUFSIZ];
1120
1121             log_error(LOG_LEVEL_ERROR, "can't fork: %E");
1122
1123             sprintf(buf , "JunkBuster: can't fork: errno = %d", errno);
1124
1125             write_socket(csp->cfd, buf, strlen(buf));
1126             close_socket(csp->cfd);
1127             csp->active = 0;
1128             sleep(5);
1129             continue;
1130          }
1131
1132 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
1133          /* This block is only needed when using fork().
1134           * When using threads, the server thread was
1135           * created and run by the call to _beginthread().
1136           */
1137          if (child_id == 0)   /* child */
1138          {
1139             serve(csp);
1140             _exit(0);
1141
1142          }
1143          else  /* parent */
1144          {
1145             /* in a fork()'d environment, the parent's
1146              * copy of the client socket and the CSP
1147              * are not used.
1148              */
1149
1150 #if !defined(_WIN32) && defined(__CYGWIN__)
1151             wait( NULL );
1152 #endif /* !defined(_WIN32) && defined(__CYGWIN__) */
1153             close_socket(csp->cfd);
1154             csp->active = 0;
1155          }
1156 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
1157       }
1158       else
1159       {
1160          serve(csp);
1161       }
1162    }
1163    /* NOTREACHED */
1164
1165 }
1166
1167
1168 /*
1169   Local Variables:
1170   tab-width: 3
1171   end:
1172 */