File is now ignored if _WIN_CONSOLE is defined.
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.1.1.1 2001/05/15 13:58:52 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *                Functions declared include:
8  *                   `acl_addr', `add_stats', `block_acl', `block_imageurl',
9  *                   `block_url', `url_permissions', `domaincmp', `dsplit',
10  *                   `filter_popups', `forward_url',
11  *                   `ij_untrusted_url', `intercept_url', `re_process_buffer',
12  *                   `show_proxy_args', and `trust_url'
13  *
14  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
15  *                IJBSWA team.  http://ijbswa.sourceforge.net
16  *
17  *                Based on the Internet Junkbuster originally written
18  *                by and Copyright (C) 1997 Anonymous Coders and 
19  *                Junkbusters Corporation.  http://www.junkbusters.com
20  *
21  *                This program is free software; you can redistribute it 
22  *                and/or modify it under the terms of the GNU General
23  *                Public License as published by the Free Software
24  *                Foundation; either version 2 of the License, or (at
25  *                your option) any later version.
26  *
27  *                This program is distributed in the hope that it will
28  *                be useful, but WITHOUT ANY WARRANTY; without even the
29  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
30  *                PARTICULAR PURPOSE.  See the GNU General Public
31  *                License for more details.
32  *
33  *                The GNU General Public License should be included with
34  *                this file.  If not, you can view it at
35  *                http://www.gnu.org/copyleft/gpl.html
36  *                or write to the Free Software Foundation, Inc., 59
37  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
38  *
39  * Revisions   :
40  *    $Log: filters.c,v $
41  *    Revision 1.1.1.1  2001/05/15 13:58:52  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 <ctype.h>
54 #include <string.h>
55
56 #ifndef _WIN32
57 #include <unistd.h>
58 #include <netinet/in.h>
59 #else
60 #include <winsock2.h>
61 #endif
62
63 #include "project.h"
64 #include "filters.h"
65 #include "encode.h"
66 #include "jcc.h"
67 #include "showargs.h"
68 #include "parsers.h"
69 #include "ssplit.h"
70 #include "gateway.h"
71 #include "jbsockets.h"
72 #include "errlog.h"
73 #include "jbsockets.h"
74
75 #ifdef _WIN32
76 #include "win32.h"
77 #endif
78
79 const char filters_h_rcs[] = FILTERS_H_VERSION;
80
81 /* Fix a problem with Solaris.  There should be no effect on other
82  * platforms.
83  * Solaris's isspace() is a macro which uses it's argument directly
84  * as an array index.  Therefore we need to make sure that high-bit
85  * characters generate +ve values, and ideally we also want to make
86  * the argument match the declared parameter type of "int".
87  */
88 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
89
90
91 static const char CBLOCK[] = 
92 #ifdef AMIGA 
93        "HTTP/1.0 403 Request for blocked URL\n" 
94 #else /* ifndef AMIGA */
95        "HTTP/1.0 202 Request for blocked URL\n"
96 #endif /* ndef AMIGA */
97        "Pragma: no-cache\n"
98        "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
99        "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
100        "Content-Type: text/html\n\n"
101        "<html>\n"
102        "<head>\n"
103        "<title>Internet Junkbuster: Request for blocked URL</title>\n"
104        "</head>\n"
105        WHITEBG
106        "<center><h1>"
107        BANNER
108        "</h1></center>\n"
109       "<p align=center>Your request for <b>%s%s</b><br>\n"
110       "was blocked because it matches the following pattern "
111       "in the blockfile: <b>%s</b>\n</p>"
112 #ifdef FORCE_LOAD
113        "<p align=center><a href=\"http://" FORCE_PREFIX
114         "%s%s\">Go there anyway.</a></p>"
115 #endif /* def FORCE_LOAD */
116       "</body>\n"
117       "</html>\n";
118
119 #ifdef TRUST_FILES
120 static const char CTRUST[] =
121 #ifdef AMIGA 
122        "HTTP/1.0 403 Request for untrusted URL\n"
123 #else /* ifndef AMIGA */
124        "HTTP/1.0 202 Request for untrusted URL\n"
125 #endif /* ndef AMIGA */
126        "Pragma: no-cache\n"
127        "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
128        "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
129        "Content-Type: text/html\n\n"
130        "<html>\n"
131        "<head>\n"
132        "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
133        "</head>\n"
134        WHITEBG
135        "<center>"
136        "<a href=http://internet.junkbuster.com/ij-untrusted-url?%s+%s+%s>"
137        BANNER
138        "</a>"
139        "</center>"
140        "</body>\n"
141        "</html>\n";
142 #endif /* def TRUST_FILES */
143
144
145 #ifdef ACL_FILES
146 /*********************************************************************
147  *
148  * Function    :  block_acl
149  *
150  * Description :  Block this request?
151  *                Decide yes or no based on ACL file.
152  *
153  * Parameters  :
154  *          1  :  src = Address the browser/user agent is requesting.
155  *          2  :  dst = The proxy or gateway address this is going to.
156  *          3  :  csp = Current client state (buffers, headers, etc...)
157  *
158  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
159  *
160  *********************************************************************/
161 int block_acl(struct access_control_addr *src, struct access_control_addr *dst, struct client_state *csp)
162 {
163    struct file_list *fl;
164    struct access_control_list *a, *acl;
165    struct access_control_addr s[1], d[1];
166
167    /* if not using an access control list, then permit the connection */
168    if (((fl = csp->alist) == NULL) || ((acl = fl->f) == NULL))
169    {
170       return(0);
171    }
172
173    /* search the list */
174    for (a = acl->next ; a ; a = a->next)
175    {
176       *s = *src;
177       *d = *dst;
178
179       s->addr &= a->src->mask;
180       d->addr &= a->dst->mask;
181
182       if ((s->addr  == a->src->addr)
183       && (d->addr  == a->dst->addr)
184       && ((s->port == a->src->port)
185        || (s->port == 0)
186        || (a->src->port == 0))
187       && ((d->port == a->dst->port)
188        || (d->port == 0)
189        || (a->dst->port == 0)))
190       {
191          if (a->action == ACL_PERMIT)
192          {
193             return(0);
194          }
195          else
196          {
197             return(1);
198          }
199       }
200    }
201
202    return(1);
203
204 }
205
206
207 /*********************************************************************
208  *
209  * Function    :  acl_addr
210  *
211  * Description :  Called from `load_aclfile'.  FIXME: I can't say more.
212  *
213  * Parameters  :
214  *          1  :  aspec = (what?)
215  *          2  :  aca = (what?)
216  *
217  * Returns     :  0 => Ok, everything else is an error.
218  *
219  *********************************************************************/
220 int acl_addr(char *aspec, struct access_control_addr *aca)
221 {
222    int i, masklength, port;
223    char *p;
224
225    masklength = 32;
226    port       =  0;
227
228    if ((p = strchr(aspec, '/')))
229    {
230       *p++ = '\0';
231
232       if (ijb_isdigit(*p) == 0)
233       {
234          return(-1);
235       }
236       masklength = atoi(p);
237    }
238
239    if ((masklength < 0) || (masklength > 32))
240    {
241       return(-1);
242    }
243
244    if ((p = strchr(aspec, ':')))
245    {
246       *p++ = '\0';
247
248       if (ijb_isdigit(*p) == 0)
249       {
250          return(-1);
251       }
252       port = atoi(p);
253    }
254
255    aca->port = port;
256
257    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
258
259    if (aca->addr == -1)
260    {
261       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
262       return(-1);
263    }
264
265    /* build the netmask */
266    aca->mask = 0;
267    for (i=1; i <= masklength ; i++)
268    {
269       aca->mask |= (1 << (32 - i));
270    }
271
272    /* now mask off the host portion of the ip address
273     * (i.e. save on the network portion of the address).
274     */
275    aca->addr = aca->addr & aca->mask;
276
277    return(0);
278
279 }
280 #endif /* def ACL_FILES */
281
282
283 /*********************************************************************
284  *
285  * Function    :  block_url
286  *
287  * Description :  Called from `chat'.  Check to see if we need to block this.
288  *
289  * Parameters  :
290  *          1  :  http = http_request request to "check" for blocked
291  *          2  :  csp = Current client state (buffers, headers, etc...)
292  *
293  * Returns     :  NULL => unblocked, else string to HTML block description.
294  *
295  *********************************************************************/
296 char *block_url(struct http_request *http, struct client_state *csp)
297 {
298    struct file_list *fl;
299    struct block_spec *b;
300    struct url_spec url[1];
301    char *p;
302    int n;
303
304    if (((fl = csp->blist) == NULL) || ((b = fl->f) == NULL))
305    {
306       return(NULL);
307    }
308
309    *url = dsplit(http->host);
310
311    /* if splitting the domain fails, punt */
312    if (url->dbuf == NULL) return(NULL);
313
314    for (b = b->next; b ; b = b->next)
315    {
316       if ((b->url->port == 0) || (b->url->port == http->port))
317       {
318          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
319          {
320             if ((b->url->path == NULL) ||
321 #ifdef REGEX
322                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
323 #else
324                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
325 #endif
326             )
327             {
328                freez(url->dbuf);
329                freez(url->dvec);
330
331                if (b->reject == 0) return(NULL);
332
333                n  = strlen(CBLOCK);
334                n += strlen(http->hostport);
335                n += strlen(http->path);
336                n += strlen(b->url->spec);
337 #ifdef FORCE_LOAD
338                n += strlen(http->hostport);
339                n += strlen(http->path);
340 #endif /* def FORCE_LOAD */
341
342                p = (char *)malloc(n);
343
344 #ifdef FORCE_LOAD
345                sprintf(p, CBLOCK, http->hostport, http->path, b->url->spec, http->hostport, http->path);
346 #else
347                sprintf(p, CBLOCK, http->hostport, http->path, b->url->spec);
348 #endif /* def FORCE_LOAD */
349
350                return(p);
351             }
352          }
353       }
354    }
355    freez(url->dbuf);
356    freez(url->dvec);
357    return(NULL);
358
359 }
360
361
362 #if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
363 /*********************************************************************
364  *
365  * Function    :  block_imageurl
366  *
367  * Description :  Given a URL which is blocked, decide whether to 
368  *                send the "blocked" image or HTML.
369  *
370  * Parameters  :
371  *          1  :  http = URL to check.
372  *          2  :  csp = Current client state (buffers, headers, etc...)
373  *
374  * Returns     :  True (nonzero) if URL is in image list, false (0)
375  *                otherwise
376  *
377  *********************************************************************/
378 int block_imageurl(struct http_request *http, struct client_state *csp)
379 {
380 #ifdef DETECT_MSIE_IMAGES
381    if ((csp->accept_types 
382        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
383        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
384    {
385       return 1;
386    }
387    else if ((csp->accept_types 
388        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
389        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
390    {
391       return 0;
392    }
393 #endif
394
395 #if defined(USE_IMAGE_LIST)
396    return block_imageurl_using_imagelist(http, csp);
397 #else
398    /* Don't know - assume HTML */
399    return 0;
400 #endif
401 }
402 #endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
403
404
405 #ifdef USE_IMAGE_LIST
406 /*********************************************************************
407  *
408  * Function    :  block_imageurl
409  *
410  * Description :  Test if a URL is in the imagelist.
411  *
412  * Parameters  :
413  *          1  :  http = URL to check.
414  *          2  :  csp = Current client state (buffers, headers, etc...)
415  *
416  * Returns     :  True (nonzero) if URL is in image list, false (0)
417  *                otherwise
418  *
419  *********************************************************************/
420 int block_imageurl_using_imagelist(struct http_request *http, struct client_state *csp)
421 {
422    struct file_list *fl;
423    struct block_spec *b;
424    struct url_spec url[1];
425
426    if (((fl = csp->ilist) == NULL) || ((b  = fl->f) == NULL))
427    {
428       return(0);
429    }
430
431    *url = dsplit(http->host);
432
433    /* if splitting the domain fails, punt */
434    if (url->dbuf == NULL) return(0);
435
436    for (b = b->next; b ; b = b->next)
437    {
438
439       if ((b->url->port == 0) || (b->url->port == http->port))
440       {
441          /* port matches, check domain */
442          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
443          {
444             /* domain matches, check path */
445             if ((b->url->path == NULL) ||
446 #ifdef REGEX
447                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
448 #else
449                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
450 #endif
451             )
452             {
453                /* Matches */
454                freez(url->dbuf);
455                freez(url->dvec);
456
457                if (b->reject == 0) return(0);
458
459                return(1);
460             }
461          }
462       }
463    }
464    freez(url->dbuf);
465    freez(url->dvec);
466    return(0);
467
468 }
469 #endif /* def USE_IMAGE_LIST */
470
471
472 #ifdef PCRS
473 /*********************************************************************
474  *
475  * Function    :  re_process_buffer
476  *
477  * Description :  Apply all jobs from the joblist (aka. Perl regexp's) to
478  *                the text buffer that's been accumulated in csp->iob->buf.
479  *                Then, write the modified buffer out to the client
480  *                (Maybe this should happen from jcc.c via flush_socket
481  *                for better readability).
482  *
483  * Parameters  :
484  *          1  :  csp = Current client state (buffers, headers, etc...)
485  *
486  * Returns     :  N/A
487  *
488  *********************************************************************/
489 void re_process_buffer(struct client_state *csp)
490 {
491    int hits=0;
492    int size = csp->iob->eod - csp->iob->cur;
493    char *old=csp->iob->cur, *new = NULL;
494    pcrs_job *job, *joblist;
495
496    struct file_list *fl;
497    struct re_filterfile_spec *b;
498
499    /* Sanity first ;-) */
500    if (size <= 0)\r
501    {\r
502       return;\r
503    }
504
505    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
506    {
507       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
508       return;
509    }
510
511    joblist = b->joblist;
512
513
514    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
515               csp->http->hostport, csp->http->path, size);
516
517    /* Apply all jobs from the joblist */
518    for (job = joblist; NULL != job; job = job->next)
519    {
520       hits += pcrs_exec_substitution(job, old, size, &new, &size);
521       if (old != csp->iob->cur) free(old);
522       old=new;
523    }
524
525    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
526
527    if (write_socket(csp->cfd, old, size) != size)
528    {
529       log_error(LOG_LEVEL_ERROR, "write to client failed.");
530    }
531
532    /* fwiw, reset the iob */
533    IOB_RESET(csp);
534    freez(new);
535    return;
536
537 }
538 #endif /* def PCRS */
539
540
541 #ifdef TRUST_FILES
542 /*********************************************************************
543  *
544  * Function    :  trust_url
545  *
546  * Description :  Should we "trust" this URL?  See "trustfile" line in config.
547  *
548  * Parameters  :
549  *          1  :  http = http_request request for requested URL
550  *          2  :  csp = Current client state (buffers, headers, etc...)
551  *
552  * Returns     :  NULL => trusted, else string to HTML "untrusted" description.
553  *
554  *********************************************************************/
555 char *trust_url(struct http_request *http, struct client_state *csp)
556 {
557    struct file_list *fl;
558    struct block_spec *b;
559    struct url_spec url[1], **tl, *t;
560    char *p, *h;
561    char *hostport, *path, *refer;
562    struct http_request rhttp[1];
563    int n;
564
565    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
566    {
567       return(NULL);
568    }
569
570    *url = dsplit(http->host);
571
572    /* if splitting the domain fails, punt */
573    if (url->dbuf == NULL) return(NULL);
574
575    memset(rhttp, '\0', sizeof(*rhttp));
576
577    for (b = b->next; b ; b = b->next)
578    {
579       if ((b->url->port == 0) || (b->url->port == http->port))
580       {
581          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
582          {
583             if ((b->url->path == NULL) ||
584 #ifdef REGEX
585                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
586 #else
587                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
588 #endif
589             )
590             {
591                freez(url->dbuf);
592                freez(url->dvec);
593
594                if (b->reject == 0) return(NULL);
595
596                hostport = url_encode(http->hostport);
597                path     = url_encode(http->path);
598
599                if (csp->referrer)
600                {
601                   refer = url_encode(csp->referrer);
602                }
603                else
604                {
605                   refer = url_encode("undefined");
606                }
607
608                n  = strlen(CTRUST);
609                n += strlen(hostport);
610                n += strlen(path);
611                n += strlen(refer);
612
613                p = (char *)malloc(n);
614
615                sprintf(p, CTRUST, hostport, path, refer);
616
617                freez(hostport);
618                freez(path);
619                freez(refer);
620
621                return(p);
622             }
623          }
624       }
625    }
626
627    freez(url->dbuf);
628    freez(url->dvec);
629
630    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
631    {
632       /* no referrer was supplied */
633       goto trust_url_not_trusted;
634    }
635
636    /* forge a URL from the referrer so we can use
637     * convert_url() to parse it into its components.
638     */
639
640    p = NULL;
641    p = strsav(p, "GET ");
642    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
643    p = strsav(p, " HTTP/1.0");
644
645    parse_http_request(p, rhttp, csp);
646
647    if (rhttp->cmd == NULL)
648    {
649       freez(p);
650       goto trust_url_not_trusted;
651    }
652
653    freez(p);
654
655    *url = dsplit(rhttp->host);
656
657    /* if splitting the domain fails, punt */
658    if (url->dbuf == NULL) goto trust_url_not_trusted;
659
660    for (tl = trust_list; (t = *tl) ; tl++)
661    {
662       if ((t->port == 0) || (t->port == rhttp->port))
663       {
664          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
665          {
666             if ((t->path == NULL) ||
667 #ifdef REGEX
668                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
669 #else
670                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
671 #endif
672             )
673             {
674                /* if the URL's referrer is from a trusted referrer, then
675                 * add the target spec to the trustfile as an unblocked
676                 * domain and return NULL (which means it's OK).
677                 */
678
679                FILE *fp;
680
681                freez(url->dbuf);
682                freez(url->dvec);
683
684                if ((fp = fopen(trustfile, "a")))
685                {
686                   h = NULL;
687
688                   h = strsav(h, "~");
689                   h = strsav(h, http->hostport);
690
691                   p = http->path;
692                   if ((*p++ == '/')
693                   && (*p++ == '~'))
694                   {
695                   /* since this path points into a user's home space
696                    * be sure to include this spec in the trustfile.
697                    */
698                      if ((p = strchr(p, '/')))
699                      {
700                         *p = '\0';
701                         h = strsav(h, http->path);
702                         h = strsav(h, "/");
703                      }
704                   }
705
706                   free_http_request(rhttp);
707
708                   fprintf(fp, "%s\n", h);
709                   freez(h);
710                   fclose(fp);
711                }
712                return(NULL);
713             }
714          }
715       }
716    }
717
718 trust_url_not_trusted:
719    free_http_request(rhttp);
720
721    hostport = url_encode(http->hostport);
722    path     = url_encode(http->path);
723
724    if (csp->referrer)
725    {
726       refer = url_encode(csp->referrer);
727    }
728    else
729    {
730       refer = url_encode("undefined");
731    }
732
733    n  = strlen(CTRUST);
734    n += strlen(hostport);
735    n += strlen(path);
736    n += strlen(refer);
737
738    p = (char *)malloc(n);
739    sprintf(p, CTRUST, hostport, path, refer);
740
741    freez(hostport);
742    freez(path);
743    freez(refer);
744
745    return(p);
746
747 }
748 #endif /* def TRUST_FILES */
749
750
751 /*********************************************************************
752  *
753  * Function    :  intercept_url
754  *
755  * Description :  checks the URL `basename' against a list of URLs to
756  *                snarf. If it matches, it calls the associated function
757  *                which returns an HTML page to send back to the client.
758  *                Right now, we snarf:
759  *                      "show-proxy-args", and
760  *                      "ij-untrusted-url" (optional w/TRUST_FILES)
761  *
762  * Parameters  :
763  *          1  :  http = http_request request, check `basename's of blocklist
764  *          2  :  csp = Current client state (buffers, headers, etc...)
765  *
766  * Returns     :  NULL for no recognized URLs, or an HTML description page.
767  *
768  *********************************************************************/
769 char *intercept_url(struct http_request *http, struct client_state *csp)
770 {
771    char *basename;
772    const struct interceptors *v;
773
774    basename = strrchr(http->path, '/');
775
776    if (basename == NULL) return(NULL);
777
778    basename ++; /* first char past the last slash */
779
780    if (*basename)
781    {
782       for (v = intercept_patterns; v->str; v++)
783       {
784          if (strncmp(basename, v->str, v->len) == 0)
785          {
786             return((v->interceptor)(http, csp));
787          }
788       }
789    }
790
791    return(NULL);
792
793 }
794
795
796 /*********************************************************************
797  *
798  * Function    :  url_permissions
799  *
800  * Description :  Gets the permissions for this URL.
801  *
802  * Parameters  :
803  *          1  :  http = http_request request for blocked URLs
804  *          2  :  csp = Current client state (buffers, headers, etc...)
805  *
806  * Returns     :  permissions bitmask specifiying what this URL can do.\r
807  *                If not on list, will be default_permissions.
808  *
809  *********************************************************************/
810 int url_permissions(struct http_request *http, struct client_state *csp)
811 {
812    struct file_list *fl;
813    struct permissions_spec *b;
814    struct url_spec url[1];
815
816    if (((fl = csp->permissions_list) == NULL) || ((b = fl->f) == NULL))
817    {
818       return(default_permissions);
819    }
820
821    *url = dsplit(http->host);
822
823    /* if splitting the domain fails, punt */
824    if (url->dbuf == NULL)\r
825    {\r
826       return(default_permissions);\r
827    }
828
829    for (b = b->next; NULL != b; b = b->next)
830    {
831       if ((b->url->port == 0) || (b->url->port == http->port))
832       {
833          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
834          {
835             if ((b->url->path == NULL) ||
836 #ifdef REGEX
837                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
838 #else
839                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
840 #endif
841             )
842             {
843                freez(url->dbuf);
844                freez(url->dvec);
845                return(b->permissions);
846             }
847          }
848       }
849    }
850
851    freez(url->dbuf);
852    freez(url->dvec);
853    return(default_permissions);
854
855 }
856
857
858 /*********************************************************************
859  *
860  * Function    :  forward_url
861  *
862  * Description :  Should we forward this to another proxy?
863  *
864  * Parameters  :
865  *          1  :  http = http_request request for current URL
866  *          2  :  csp = Current client state (buffers, headers, etc...)
867  *
868  * Returns     :  Return gw_default for no forward match,
869  *                else a gateway pointer to a specific forwarding proxy.
870  *
871  *********************************************************************/
872 const struct gateway *forward_url(struct http_request *http, struct client_state *csp)
873 {
874    struct file_list *fl;
875    struct forward_spec *b;
876    struct url_spec url[1];
877
878    if (((fl = csp->flist) == NULL) || ((b = fl->f) == NULL))
879    {
880       return(gw_default);
881    }
882
883    *url = dsplit(http->host);
884
885    /* if splitting the domain fails, punt */
886    if (url->dbuf == NULL) return(gw_default);
887
888    for (b = b->next; b ; b = b->next)
889    {
890       if ((b->url->port == 0) || (b->url->port == http->port))
891       {
892          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
893          {
894             if ((b->url->path == NULL) ||
895 #ifdef REGEX
896                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
897 #else
898                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
899 #endif
900             )
901             {
902                freez(url->dbuf);
903                freez(url->dvec);
904                return(b->gw);
905             }
906          }
907       }
908    }
909
910    freez(url->dbuf);
911    freez(url->dvec);
912    return(gw_default);
913
914 }
915
916
917 /*********************************************************************
918  *
919  * Function    :  dsplit
920  *
921  * Description :  Takes a domain and returns a pointer to a url_spec
922  *                structure populated with dbuf, dcnt and dvec.  The
923  *                other fields in the structure that is returned are zero.
924  *
925  * Parameters  :
926  *          1  :  domain = a URL address
927  *
928  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
929  *
930  *********************************************************************/
931 struct url_spec dsplit(char *domain)
932 {
933    struct url_spec ret[1];
934    char *v[BUFSIZ];
935    int size;
936    char *p;
937
938    memset(ret, '\0', sizeof(*ret));
939
940    if ((p = strrchr(domain, '.')))
941    {
942       if (*(++p) == '\0')
943       {
944          ret->toplevel = 1;
945       }
946    }
947
948    ret->dbuf = strdup(domain);
949
950    /* map to lower case */
951    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
952
953    /* split the domain name into components */
954    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
955
956    if (ret->dcnt <= 0)
957    {
958       memset(ret, '\0', sizeof(ret));
959       return(*ret);
960    }
961
962    /* save a copy of the pointers in dvec */
963    size = ret->dcnt * sizeof(*ret->dvec);
964
965    if ((ret->dvec = (char **)malloc(size)))
966    {
967       memcpy(ret->dvec, v, size);
968    }
969
970    return(*ret);
971
972 }
973
974
975 /*********************************************************************
976  *
977  * Function    :  domaincmp
978  *
979  * Description :  Compare domain names.
980  *                domaincmp("a.b.c" , "a.b.c")  => 0 (MATCH)
981  *                domaincmp("a*.b.c", "a.b.c")  => 0 (MATCH)
982  *                domaincmp("b.c"   , "a.b.c")  => 0 (MATCH)
983  *                domaincmp(""      , "a.b.c")  => 0 (MATCH)
984  *
985  * Parameters  :
986  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
987  *          2  :  fqdn = domain name against which the patterns are compared.
988  *
989  * Returns     :  0 => domains are equivalent, else no match.
990  *
991  *********************************************************************/
992 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
993 {
994    char **pv, **fv;  /* vectors  */
995    int    pn,   fn;  /* counters */
996    char  *p,   *f;   /* chars    */
997
998    pv = pattern->dvec;
999    pn = pattern->dcnt;
1000
1001    fv = fqdn->dvec;
1002    fn = fqdn->dcnt;
1003
1004    while ((pn > 0) && (fn > 0))
1005    {
1006       p = pv[--pn];
1007       f = fv[--fn];
1008
1009       while (*p && *f && (*p == tolower(*f)))
1010       {
1011          p++, f++;
1012       }
1013
1014       if ((*p != tolower(*f)) && (*p != '*')) return(1);
1015    }
1016
1017    if (pn > 0) return(1);
1018
1019    return(0);
1020
1021 }
1022
1023
1024 /* intercept functions */
1025
1026 /*********************************************************************
1027  *
1028  * Function    :  show_proxy_args
1029  *
1030  * Description :  This "crunch"es "http:/any.thing/show-proxy-args" and
1031  *                returns a web page describing the current status of IJB.
1032  *
1033  * Parameters  :
1034  *          1  :  http = ignored
1035  *          2  :  csp = Current client state (buffers, headers, etc...)
1036  *
1037  * Returns     :  A string that contains the current status of IJB.
1038  *
1039  *********************************************************************/
1040 char *show_proxy_args(struct http_request *http, struct client_state *csp)
1041 {
1042    char *s = NULL;
1043
1044 #ifdef SPLIT_PROXY_ARGS
1045    FILE * fp;
1046    char buf[BUFSIZ];
1047    char * p;
1048    const char * filename = NULL;
1049    const char * file_description = NULL;
1050    char * query_string = strrchr(http->path, '?');
1051    char which_file = '\0';
1052
1053
1054    if (query_string != NULL)
1055    {
1056       /* first char past the last '?' (maybe '\0')*/
1057       which_file = query_string[1];
1058    }
1059    switch (which_file)
1060    {
1061    case 'b':
1062       if (csp->blist)
1063       {
1064          filename = csp->blist->filename;
1065          file_description = "Block List";
1066       }
1067       break;
1068    case 'p':
1069       if (csp->permissions_list)
1070       {
1071          filename = csp->permissions_list->filename;
1072          file_description = "Permissions List";
1073       }
1074       break;
1075    case 'f':
1076       if (csp->flist)
1077       {
1078          filename = csp->flist->filename;
1079          file_description = "Forward List";
1080       }
1081       break;
1082
1083 #ifdef ACL_FILES
1084    case 'a':
1085       if (csp->alist)
1086       {
1087          filename = csp->alist->filename;
1088          file_description = "Access Control List";
1089       }
1090       break;
1091 #endif /* def ACL_FILES */
1092
1093 #ifdef USE_IMAGE_LIST
1094    case 'i':
1095       if (csp->ilist)
1096       {
1097          filename = csp->ilist->filename;
1098          file_description = "Image List";
1099       }
1100       break;
1101 #endif /* def USE_IMAGE_LIST */
1102
1103 #ifdef PCRS
1104    case 'r':
1105       if (csp->rlist)
1106       {
1107          filename = csp->rlist->filename;
1108          file_description = "RE Filter List";
1109       }
1110       break;
1111 #endif /* def PCRS */
1112
1113 #ifdef TRUST_FILES
1114    case 't':
1115       if (csp->tlist)
1116       {
1117          filename = csp->tlist->filename;
1118          file_description = "Trust List";
1119       }
1120       break;
1121 #endif /* def TRUST_FILES */
1122    }
1123
1124    if (filename)
1125    {
1126       /* Display specified file */
1127       /* FIXME: Add HTTP headers so this isn't cached */
1128       s = strsav(s,
1129          "HTTP/1.0 200 OK\n"
1130          "Server: IJ/" VERSION "\n"
1131          "Content-type: text/html\n"
1132          "Pragma: no-cache\n"
1133          "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
1134          "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1135          "\n"
1136
1137          "<html>"
1138          "<head>"
1139          "<title>Internet Junkbuster Proxy Status - ");
1140       s = strsav(s, file_description);
1141       s = strsav(s, 
1142          "</title>"
1143          "</head>\n"
1144          "<body bgcolor=\"#f8f8f0\" link=\"#000078\" alink=\"#ff0022\" vlink=\"#787878\">\n"
1145          "<center>\n"
1146          "<h1>" BANNER "\n");
1147       s = strsav(s, file_description);
1148       s = strsav(s, 
1149          "</h1></center>\n"
1150          "<p><a href=\"show-proxy-args\">Back to proxy status</a></p>\n"
1151          "<h2>");
1152       s = strsav(s, file_description);
1153       s = strsav(s,
1154          "</h2>\n"
1155          "Contents of file &quot;<code>");
1156       p = html_encode(filename);
1157       s = strsav(s, p);
1158       freez(p);
1159       s = strsav(s,
1160          "</code>&quot;:<br>\n"
1161          "</p>\n"
1162          "<pre>");
1163       
1164       if ((fp = fopen(filename, "r")) == NULL)
1165       {
1166          s = strsav(s, "</pre><h1>ERROR OPENING FILE!</h1><pre>");
1167       }
1168       else
1169       {
1170          while (fgets(buf, sizeof(buf), fp))
1171          {
1172             p = html_encode(buf);
1173             if (p)
1174             {
1175                s = strsav(s, p);
1176                freez(p);
1177                s = strsav(s, "<br>");
1178             }
1179          }
1180          fclose(fp);
1181       }
1182
1183       s = strsav(s,
1184          "</pre>\n"
1185          "<br>\n"
1186          "<p><a href=\"show-proxy-args\">Back to proxy status</a></p>\n"
1187          "<br>\n"
1188          "<small><small><p>\n"
1189          "Code and documentation of the " BANNER " Proxy"
1190          "<sup><small>TM</small></sup>\n"
1191          "<a href=\"http://www.junkbusters.com/ht/en/legal.html#copy\">\n" "Copyright</a>&#169; 1997 Junkbusters Corporation\n"
1192          "<a href=\"http://www.junkbusters.com/ht/en/legal.html#marks\"><sup><small>TM</small></sup></a><br>\n"
1193          "Copying and distribution permitted under the"
1194          "<a href=\"http://www.gnu.org/copyleft/gpl.html\">\n"
1195          "<small>GNU</small></a> "
1196          "General Public License.\n"
1197          "</small>"
1198          "<address><kbd>webmaster@junkbusters.com</kbd></address>"
1199          "</small>"
1200          "</body></html>\n");
1201       return(s);
1202    }
1203 #endif /* def SPLIT_PROXY_ARGS */
1204    
1205    s = strsav(s, proxy_args->header);
1206    s = strsav(s, proxy_args->invocation);
1207 #ifdef STATISTICS
1208    s = add_stats(s);
1209 #endif /* def STATISTICS */
1210    s = strsav(s, proxy_args->gateways);
1211
1212 #ifdef SPLIT_PROXY_ARGS
1213    s = strsav(s, 
1214       "<h2>The following files are in use:</h2>\n"
1215       "<p>(Click a filename to view it)</p>\n"
1216       "<ul>\n");
1217
1218    if (csp->blist)
1219    {
1220       s = strsav(s, "<li>Block List: <a href=\"show-proxy-args?block\"><code>");
1221       s = strsav(s, csp->blist->filename);
1222       s = strsav(s, "</code></a></li>\n");
1223    }
1224
1225    if (csp->permissions_list)
1226    {
1227       s = strsav(s, "<li>Permissions List: <a href=\"show-proxy-args?permit\"><code>");
1228       s = strsav(s, csp->permissions_list->filename);
1229       s = strsav(s, "</code></a></li>\n");
1230    }
1231
1232    if (csp->flist)
1233    {
1234       s = strsav(s, "<li>Forward List: <a href=\"show-proxy-args?forward\"><code>");
1235       s = strsav(s, csp->flist->filename);
1236       s = strsav(s, "</code></a></li>\n");
1237    }
1238
1239 #ifdef ACL_FILES
1240    if (csp->alist)
1241    {
1242       s = strsav(s, "<li>Access Control List: <a href=\"show-proxy-args?acl\"><code>");
1243       s = strsav(s, csp->alist->filename);
1244       s = strsav(s, "</code></a></li>\n");
1245    }
1246 #endif /* def ACL_FILES */
1247
1248 #ifdef USE_IMAGE_LIST
1249    if (csp->ilist)
1250    {
1251       s = strsav(s, "<li>Image List: <a href=\"show-proxy-args?image\"><code>");
1252       s = strsav(s, csp->ilist->filename);
1253       s = strsav(s, "</code></a></li>\n");
1254    }
1255 #endif /* def USE_IMAGE_LIST */
1256 \r
1257 #ifdef PCRS
1258    if (csp->rlist)
1259    {
1260       s = strsav(s, "<li>RE Filter List: <a href=\"show-proxy-args?re\"><code>");
1261       s = strsav(s, csp->rlist->filename);
1262       s = strsav(s, "</code></a></li>\n");
1263    }
1264 #endif /* def PCRS */
1265
1266 #ifdef TRUST_FILES
1267    if (csp->tlist)
1268    {
1269       s = strsav(s, "<li>Trust List: <a href=\"show-proxy-args?trust\"><code>");
1270       s = strsav(s, csp->tlist->filename);
1271       s = strsav(s, "</code></a></li>\n");
1272    }
1273 #endif /* def TRUST_FILES */
1274
1275    s = strsav(s, "</ul>");
1276
1277 #else /* ifndef SPLIT_PROXY_ARGS */
1278    if (csp->blist)
1279    {
1280       s = strsav(s, csp->blist->proxy_args);
1281    }
1282
1283    if (csp->clist)
1284    {
1285       s = strsav(s, csp->clist->proxy_args);
1286    }
1287
1288    if (csp->flist)
1289    {
1290       s = strsav(s, csp->flist->proxy_args);
1291    }
1292
1293 #ifdef ACL_FILES
1294    if (csp->alist)
1295    {
1296       s = strsav(s, csp->alist->proxy_args);
1297    }
1298 #endif /* def ACL_FILES */
1299
1300 #ifdef USE_IMAGE_LIST
1301    if (csp->ilist)
1302    {
1303       s = strsav(s, csp->ilist->proxy_args);
1304    }
1305 #endif /* def USE_IMAGE_LIST */
1306
1307 #ifdef PCRS
1308    if (csp->rlist)
1309    {
1310       s = strsav(s, csp->rlist->proxy_args);
1311    }
1312 #endif /* def PCRS */
1313
1314 #ifdef TRUST_FILES
1315    if (csp->tlist)
1316    {
1317       s = strsav(s, csp->tlist->proxy_args);
1318    }
1319 #endif /* def TRUST_FILES */
1320
1321 #endif /* ndef SPLIT_PROXY_ARGS */
1322
1323    s = strsav(s, proxy_args->trailer);
1324
1325    return(s);
1326
1327 }
1328
1329
1330 #ifdef TRUST_FILES
1331 /*********************************************************************
1332  *
1333  * Function    :  ij_untrusted_url
1334  *
1335  * Description :  This "crunch"es "http:/any.thing/ij-untrusted-url" and
1336  *                returns a web page describing why it was untrusted.
1337  *
1338  * Parameters  :
1339  *          1  :  http = http_request request for crunched URL
1340  *          2  :  csp = Current client state (buffers, headers, etc...)
1341  *
1342  * Returns     :  A string that contains why this was untrusted.
1343  *
1344  *********************************************************************/
1345 char *ij_untrusted_url(struct http_request *http, struct client_state *csp)
1346 {
1347    int n;
1348    char *hostport, *path, *refer, *p, *v[9];
1349    char buf[BUFSIZ];
1350    struct url_spec **tl, *t;
1351
1352
1353    static const char format[] =
1354       "HTTP/1.0 200 OK\r\n"
1355       "Pragma: no-cache\n"
1356       "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
1357       "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1358       "Content-Type: text/html\n\n"
1359       "<html>\n"
1360       "<head>\n"
1361       "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
1362       "</head>\n"
1363       BODY
1364       "<center><h1>"
1365       BANNER
1366       "</h1></center>"
1367       "The " BANNER " Proxy "
1368       "<A href=\"" HOME_PAGE_URL "\">"
1369       "(" HOME_PAGE_URL ") </A>"
1370       "intercepted the request for %s%s\n"
1371       "because the URL is not trusted.\n"
1372       "<br><br>\n";
1373
1374    if ((n = ssplit(http->path, "?+", v, SZ(v), 0, 0)) == 4)
1375    {
1376       hostport = url_decode(v[1]);
1377       path     = url_decode(v[2]);
1378       refer    = url_decode(v[3]);
1379    }
1380    else
1381    {
1382       hostport = strdup("undefined_host");
1383       path     = strdup("/undefined_path");
1384       refer    = strdup("undefined");
1385    }
1386
1387    n  = sizeof(format);
1388    n += strlen(hostport);
1389    n += strlen(path    );
1390
1391    if ((p = (char *)malloc(n)))
1392    {
1393       sprintf(p, format, hostport, path);
1394    }
1395
1396    strsav(p, "The referrer in this request was <strong>");
1397    strsav(p, refer);
1398    strsav(p, "</strong><br>\n");
1399
1400    freez(hostport);
1401    freez(path    );
1402    freez(refer   );
1403
1404    p = strsav(p, "<h3>The following referrers are trusted</h3>\n");
1405
1406    for (tl = trust_list; (t = *tl) ; tl++)
1407    {
1408       sprintf(buf, "%s<br>\n", t->spec);
1409       p = strsav(p, buf);
1410    }
1411
1412    if (trust_info->next)
1413    {
1414       struct list *l;
1415
1416       strcpy(buf,
1417          "<p>"
1418          "You can learn more about what this means "
1419          "and what you may be able to do about it by "
1420          "reading the following documents:<br>\n"
1421          "<ol>\n"
1422       );
1423
1424       p = strsav(p, buf);
1425
1426       for (l = trust_info->next; l ; l = l->next)
1427       {
1428          sprintf(buf,
1429             "<li> <a href=%s>%s</a><br>\n",
1430                l->str, l->str);
1431          p = strsav(p, buf);
1432       }
1433
1434       p = strsav(p, "</ol>\n");
1435    }
1436
1437    p = strsav(p, "</body>\n" "</html>\n");
1438
1439    return(p);
1440
1441 }
1442 #endif /* def TRUST_FILES */
1443
1444
1445 #ifdef STATISTICS
1446 /*********************************************************************
1447  *
1448  * Function    :  add_stats
1449  *
1450  * Description :  Statistics function of JB.  Called by `show_proxy_args'.
1451  *
1452  * Parameters  :
1453  *          1  :  s = string that holds the proxy args description page
1454  *
1455  * Returns     :  A pointer to the descriptive status web page.
1456  *
1457  *********************************************************************/
1458 char *add_stats(char *s)
1459 {
1460    /*
1461     * Output details of the number of requests rejected and
1462     * accepted. This is switchable in the junkbuster config.
1463     * Does nothing if this option is not enabled.
1464     */
1465
1466    float perc_rej;   /* Percentage of http requests rejected */
1467    char out_str[81];
1468    int local_urls_read     = urls_read;
1469    int local_urls_rejected = urls_rejected;
1470
1471    /*
1472     * Need to alter the stats not to include the fetch of this
1473     * page.
1474     *
1475          * Can't do following thread safely! doh!
1476          *
1477     * urls_read--;
1478     * urls_rejected--; * This will be incremented subsequently *
1479          */
1480
1481    s = strsav(s,"<h2>Statistics for this " BANNER ":</h2>\n");
1482
1483    if (local_urls_read == 0)
1484    {
1485
1486       s = strsav(s,"No activity so far!\n");
1487
1488    }
1489    else
1490    {
1491
1492       perc_rej = (float)local_urls_rejected * 100.0F /
1493             (float)local_urls_read;
1494
1495       sprintf(out_str,
1496          "%d requests received, %d filtered "
1497          "(%6.2f %%).",
1498          local_urls_read, 
1499          local_urls_rejected, perc_rej);
1500
1501       s = strsav(s,out_str);
1502    }
1503
1504    return(s);
1505 }
1506 #endif /* def STATISTICS */
1507
1508
1509 /*
1510   Local Variables:
1511   tab-width: 3
1512   end:
1513 */