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