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