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