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