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