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