Removing a single unused #define which referenced BUFSIZ
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.16 2001/06/07 23:10:26 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_actions', `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.16  2001/06/07 23:10:26  jongfoster
42  *    Allowing unanchored domain patterns to back off and retry
43  *    if they partially match.  Optimized right-anchored patterns.
44  *    Moving ACL and forward files into config file.
45  *    Replacing struct gateway with struct forward_spec
46  *
47  *    Revision 1.15  2001/06/03 19:12:00  oes
48  *    extracted-CGI relevant stuff
49  *
50  *    Revision 1.15  2001/06/03 11:03:48  oes
51  *    Makefile/in
52  *
53  *    introduced cgi.c
54  *
55  *    actions.c:
56  *
57  *    adapted to new enlist_unique arg format
58  *
59  *    conf loadcfg.c
60  *
61  *    introduced confdir option
62  *
63  *    filters.c filtrers.h
64  *
65  *     extracted-CGI relevant stuff
66  *
67  *    jbsockets.c
68  *
69  *     filled comment
70  *
71  *    jcc.c
72  *
73  *     support for new cgi mechansim
74  *
75  *    list.c list.h
76  *
77  *    functions for new list type: "map"
78  *    extended enlist_unique
79  *
80  *    miscutil.c .h
81  *    introduced bindup()
82  *
83  *    parsers.c parsers.h
84  *
85  *    deleted const struct interceptors
86  *
87  *    pcrs.c
88  *    added FIXME
89  *
90  *    project.h
91  *
92  *    added struct map
93  *    added struct http_response
94  *    changes struct interceptors to struct cgi_dispatcher
95  *    moved HTML stuff to cgi.h
96  *
97  *    re_filterfile:
98  *
99  *    changed
100  *
101  *    showargs.c
102  *    NO TIME LEFT
103  *
104  *    Revision 1.14  2001/06/01 10:30:55  oes
105  *    Added optional left-anchoring to domaincmp
106  *
107  *    Revision 1.13  2001/05/31 21:21:30  jongfoster
108  *    Permissionsfile / actions file changes:
109  *    - Changed "permission" to "action" throughout
110  *    - changes to file format to allow string parameters
111  *    - Moved helper functions to actions.c
112  *
113  *    Revision 1.12  2001/05/31 17:35:20  oes
114  *
115  *     - Enhanced domain part globbing with infix and prefix asterisk
116  *       matching and optional unanchored operation
117  *
118  *    Revision 1.11  2001/05/29 11:53:23  oes
119  *    "See why" link added to "blocked" page
120  *
121  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
122  *    Unified blocklist/imagelist/permissionslist.
123  *    File format is still under discussion, but the internal changes
124  *    are (mostly) done.
125  *
126  *    Also modified interceptor behaviour:
127  *    - We now intercept all URLs beginning with one of the following
128  *      prefixes (and *only* these prefixes):
129  *        * http://i.j.b/
130  *        * http://ijbswa.sf.net/config/
131  *        * http://ijbswa.sourceforge.net/config/
132  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
133  *    - Internal changes so that intercepted and fast redirect pages
134  *      are not replaced with an image.
135  *    - Interceptors now have the option to send a binary page direct
136  *      to the client. (i.e. ijb-send-banner uses this)
137  *    - Implemented show-url-info interceptor.  (Which is why I needed
138  *      the above interceptors changes - a typical URL is
139  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
140  *      The previous mechanism would not have intercepted that, and
141  *      if it had been intercepted then it then it would have replaced
142  *      it with an image.)
143  *
144  *    Revision 1.9  2001/05/27 22:17:04  oes
145  *
146  *    - re_process_buffer no longer writes the modified buffer
147  *      to the client, which was very ugly. It now returns the
148  *      buffer, which it is then written by chat.
149  *
150  *    - content_length now adjusts the Content-Length: header
151  *      for modified documents rather than crunch()ing it.
152  *      (Length info in csp->content_length, which is 0 for
153  *      unmodified documents)
154  *
155  *    - For this to work, sed() is called twice when filtering.
156  *
157  *    Revision 1.8  2001/05/26 17:13:28  jongfoster
158  *    Filled in a function comment.
159  *
160  *    Revision 1.7  2001/05/26 15:26:15  jongfoster
161  *    ACL feature now provides more security by immediately dropping
162  *    connections from untrusted hosts.
163  *
164  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
165  *    Automatic reloading of config file.
166  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
167  *    Most of the global variables have been moved to a new
168  *    struct configuration_spec, accessed through csp->config->globalname
169  *    Most of the globals remaining are used by the Win32 GUI.
170  *
171  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
172  *    Hard tabs->Spaces
173  *
174  *    Revision 1.4  2001/05/22 18:46:04  oes
175  *
176  *    - Enabled filtering banners by size rather than URL
177  *      by adding patterns that replace all standard banner
178  *      sizes with the "Junkbuster" gif to the re_filterfile
179  *
180  *    - Enabled filtering WebBugs by providing a pattern
181  *      which kills all 1x1 images
182  *
183  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
184  *      which is selected by the (nonstandard and therefore
185  *      capital) letter 'U' in the option string.
186  *      It causes the quantifiers to be ungreedy by default.
187  *      Appending a ? turns back to greedy (!).
188  *
189  *    - Added a new interceptor ijb-send-banner, which
190  *      sends back the "Junkbuster" gif. Without imagelist or
191  *      MSIE detection support, or if tinygif = 1, or the
192  *      URL isn't recognized as an imageurl, a lame HTML
193  *      explanation is sent instead.
194  *
195  *    - Added new feature, which permits blocking remote
196  *      script redirects and firing back a local redirect
197  *      to the browser.
198  *      The feature is conditionally compiled, i.e. it
199  *      can be disabled with --disable-fast-redirects,
200  *      plus it must be activated by a "fast-redirects"
201  *      line in the config file, has its own log level
202  *      and of course wants to be displayed by show-proxy-args
203  *      Note: Boy, all the #ifdefs in 1001 locations and
204  *      all the fumbling with configure.in and acconfig.h
205  *      were *way* more work than the feature itself :-(
206  *
207  *    - Because a generic redirect template was needed for
208  *      this, tinygif = 3 now uses the same.
209  *
210  *    - Moved GIFs, and other static HTTP response templates
211  *      to project.h
212  *
213  *    - Some minor fixes
214  *
215  *    - Removed some >400 CRs again (Jon, you really worked
216  *      a lot! ;-)
217  *
218  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
219  *    Removing last hardcoded JunkBusters.com URLs.
220  *
221  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
222  *    Version 2.9.4 checkin.
223  *    - Merged popupfile and cookiefile, and added control over PCRS
224  *      filtering, in new "permissionsfile".
225  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
226  *      file error you now get a message box (in the Win32 GUI) rather
227  *      than the program exiting with no explanation.
228  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
229  *      skipping.
230  *    - Removed tabs from "config"
231  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
232  *    - Bumped up version number.
233  *
234  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
235  *    Initial import of version 2.9.3 source tree
236  *
237  *
238  *********************************************************************/
239 \f
240
241 #include "config.h"
242
243 #include <stdio.h>
244 #include <sys/types.h>
245 #include <stdlib.h>
246 #include <ctype.h>
247 #include <string.h>
248
249 #ifndef _WIN32
250 #include <unistd.h>
251 #include <netinet/in.h>
252 #else
253 #include <winsock2.h>
254 #endif
255
256 #include "project.h"
257 #include "filters.h"
258 #include "encode.h"
259 #include "jcc.h"
260 #include "showargs.h"
261 #include "parsers.h"
262 #include "ssplit.h"
263 #include "gateway.h"
264 #include "jbsockets.h"
265 #include "errlog.h"
266 #include "jbsockets.h"
267 #include "miscutil.h"
268 #include "actions.h"
269 #include "cgi.h"
270
271 #ifdef _WIN32
272 #include "win32.h"
273 #endif
274
275 const char filters_h_rcs[] = FILTERS_H_VERSION;
276
277 /* Fix a problem with Solaris.  There should be no effect on other
278  * platforms.
279  * Solaris's isspace() is a macro which uses it's argument directly
280  * as an array index.  Therefore we need to make sure that high-bit
281  * characters generate +ve values, and ideally we also want to make
282  * the argument match the declared parameter type of "int".
283  */
284 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
285
286
287 #ifdef ACL_FILES
288 /*********************************************************************
289  *
290  * Function    :  block_acl
291  *
292  * Description :  Block this request?
293  *                Decide yes or no based on ACL file.
294  *
295  * Parameters  :
296  *          1  :  dst = The proxy or gateway address this is going to.
297  *                      Or NULL to check all possible targets.
298  *          2  :  csp = Current client state (buffers, headers, etc...)
299  *                      Also includes the client IP address.
300  *
301  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
302  *
303  *********************************************************************/
304 int block_acl(struct access_control_addr *dst,
305               struct client_state *csp)
306 {
307    struct access_control_list *acl = csp->config->acl;
308
309    /* if not using an access control list, then permit the connection */
310    if (acl == NULL)
311    {
312       return(0);
313    }
314
315    /* search the list */
316    while (acl != NULL)
317    {
318       if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
319       {
320          if (dst == NULL)
321          {
322             /* Just want to check if they have any access */
323             if (acl->action == ACL_PERMIT)
324             {
325                return(0);
326             }
327          }
328          else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
329            && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
330          {
331             if (acl->action == ACL_PERMIT)
332             {
333                return(0);
334             }
335             else
336             {
337                return(1);
338             }
339          }
340       }
341       acl = acl->next;
342    }
343
344    return(1);
345
346 }
347
348
349 /*********************************************************************
350  *
351  * Function    :  acl_addr
352  *
353  * Description :  Called from `load_aclfile' to parse an ACL address.
354  *
355  * Parameters  :
356  *          1  :  aspec = String specifying ACL address.
357  *          2  :  aca = struct access_control_addr to fill in.
358  *
359  * Returns     :  0 => Ok, everything else is an error.
360  *
361  *********************************************************************/
362 int acl_addr(char *aspec, struct access_control_addr *aca)
363 {
364    int i, masklength, port;
365    char *p;
366
367    masklength = 32;
368    port       =  0;
369
370    if ((p = strchr(aspec, '/')))
371    {
372       *p++ = '\0';
373
374       if (ijb_isdigit(*p) == 0)
375       {
376          return(-1);
377       }
378       masklength = atoi(p);
379    }
380
381    if ((masklength < 0) || (masklength > 32))
382    {
383       return(-1);
384    }
385
386    if ((p = strchr(aspec, ':')))
387    {
388       *p++ = '\0';
389
390       if (ijb_isdigit(*p) == 0)
391       {
392          return(-1);
393       }
394       port = atoi(p);
395    }
396
397    aca->port = port;
398
399    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
400
401    if (aca->addr == -1)
402    {
403       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
404       return(-1);
405    }
406
407    /* build the netmask */
408    aca->mask = 0;
409    for (i=1; i <= masklength ; i++)
410    {
411       aca->mask |= (1 << (32 - i));
412    }
413
414    /* now mask off the host portion of the ip address
415     * (i.e. save on the network portion of the address).
416     */
417    aca->addr = aca->addr & aca->mask;
418
419    return(0);
420
421 }
422 #endif /* def ACL_FILES */
423
424
425 /*********************************************************************
426  *
427  * Function    :  block_url
428  *
429  * Description :  Called from `chat'.  Check to see if we need to block this.
430  *
431  * Parameters  :
432  *          1  :  http = http_request request to "check" for blocked
433  *          2  :  csp = Current client state (buffers, headers, etc...)
434  *
435  * Returns     :  NULL => unblocked, else string to HTML block description.
436  *
437  *********************************************************************/
438 char *block_url(struct http_request *http, struct client_state *csp)
439 {
440    char *p;
441    int n;
442    int factor = 2;
443
444    if ((csp->action->flags & ACTION_BLOCK) == 0)
445    {
446       return(NULL);
447    }
448    else
449    {
450 #ifdef FORCE_LOAD
451       factor++;
452 #endif /* def FORCE_LOAD */
453
454       n  = strlen(CBLOCK);
455       n += factor * strlen(http->hostport);
456       n += factor * strlen(http->path);
457
458       p = (char *)malloc(n);
459
460 #ifdef FORCE_LOAD
461       sprintf(p, CBLOCK, http->hostport, http->path, http->hostport, http->path,
462               http->hostport, http->path);
463 #else
464       sprintf(p, CBLOCK, http->hostport, http->path, http->hostport, http->path);
465 #endif /* def FORCE_LOAD */
466
467       return(p);
468    }
469 }
470
471
472 #ifdef IMAGE_BLOCKING
473 /*********************************************************************
474  *
475  * Function    :  block_imageurl
476  *
477  * Description :  Given a URL which is blocked, decide whether to 
478  *                send the "blocked" image or HTML.
479  *
480  * Parameters  :
481  *          1  :  http = URL to check.
482  *          2  :  csp = Current client state (buffers, headers, etc...)
483  *
484  * Returns     :  True (nonzero) if URL is in image list, false (0)
485  *                otherwise
486  *
487  *********************************************************************/
488 int block_imageurl(struct http_request *http, struct client_state *csp)
489 {
490 #ifdef DETECT_MSIE_IMAGES
491    if ((csp->accept_types 
492        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
493        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
494    {
495       return 1;
496    }
497    else if ((csp->accept_types 
498        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
499        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
500    {
501       return 0;
502    }
503 #endif
504
505    return ((csp->action->flags & ACTION_IMAGE) != 0);
506 }
507 #endif /* def IMAGE_BLOCKING */
508
509
510 #ifdef TRUST_FILES
511 /*********************************************************************
512  *
513  * Function    :  trust_url
514  *
515  * Description :  Should we "trust" this URL?  See "trustfile" line in config.
516  *
517  * Parameters  :
518  *          1  :  http = http_request request for requested URL
519  *          2  :  csp = Current client state (buffers, headers, etc...)
520  *
521  * Returns     :  NULL => trusted, else string to HTML "untrusted" description.
522  *
523  *********************************************************************/
524 char *trust_url(struct http_request *http, struct client_state *csp)
525 {
526    struct file_list *fl;
527    struct block_spec *b;
528    struct url_spec url[1], **tl, *t;
529    char *p, *h;
530    char *hostport, *path, *refer;
531    struct http_request rhttp[1];
532    int n;
533
534    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
535    {
536       return(NULL);
537    }
538
539    *url = dsplit(http->host);
540
541    /* if splitting the domain fails, punt */
542    if (url->dbuf == NULL) return(NULL);
543
544    memset(rhttp, '\0', sizeof(*rhttp));
545
546    for (b = b->next; b ; b = b->next)
547    {
548       if ((b->url->port == 0) || (b->url->port == http->port))
549       {
550          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
551          {
552             if ((b->url->path == NULL) ||
553 #ifdef REGEX
554                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
555 #else
556                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
557 #endif
558             )
559             {
560                freez(url->dbuf);
561                freez(url->dvec);
562
563                if (b->reject == 0) return(NULL);
564
565                hostport = url_encode(http->hostport);
566                path     = url_encode(http->path);
567
568                if (csp->referrer)
569                {
570                   refer = url_encode(csp->referrer);
571                }
572                else
573                {
574                   refer = url_encode("undefined");
575                }
576
577                n  = strlen(CTRUST);
578                n += strlen(hostport);
579                n += strlen(path);
580                n += strlen(refer);
581
582                p = (char *)malloc(n);
583
584                sprintf(p, CTRUST, hostport, path, refer);
585
586                freez(hostport);
587                freez(path);
588                freez(refer);
589
590                return(p);
591             }
592          }
593       }
594    }
595
596    freez(url->dbuf);
597    freez(url->dvec);
598
599    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
600    {
601       /* no referrer was supplied */
602       goto trust_url_not_trusted;
603    }
604
605    /* forge a URL from the referrer so we can use
606     * convert_url() to parse it into its components.
607     */
608
609    p = NULL;
610    p = strsav(p, "GET ");
611    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
612    p = strsav(p, " HTTP/1.0");
613
614    parse_http_request(p, rhttp, csp);
615
616    if (rhttp->cmd == NULL)
617    {
618       freez(p);
619       goto trust_url_not_trusted;
620    }
621
622    freez(p);
623
624    *url = dsplit(rhttp->host);
625
626    /* if splitting the domain fails, punt */
627    if (url->dbuf == NULL) goto trust_url_not_trusted;
628
629    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
630    {
631       if ((t->port == 0) || (t->port == rhttp->port))
632       {
633          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
634          {
635             if ((t->path == NULL) ||
636 #ifdef REGEX
637                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
638 #else
639                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
640 #endif
641             )
642             {
643                /* if the URL's referrer is from a trusted referrer, then
644                 * add the target spec to the trustfile as an unblocked
645                 * domain and return NULL (which means it's OK).
646                 */
647
648                FILE *fp;
649
650                freez(url->dbuf);
651                freez(url->dvec);
652
653                if ((fp = fopen(csp->config->trustfile, "a")))
654                {
655                   h = NULL;
656
657                   h = strsav(h, "~");
658                   h = strsav(h, http->hostport);
659
660                   p = http->path;
661                   if ((*p++ == '/')
662                   && (*p++ == '~'))
663                   {
664                   /* since this path points into a user's home space
665                    * be sure to include this spec in the trustfile.
666                    */
667                      if ((p = strchr(p, '/')))
668                      {
669                         *p = '\0';
670                         h = strsav(h, http->path);
671                         h = strsav(h, "/");
672                      }
673                   }
674
675                   free_http_request(rhttp);
676
677                   fprintf(fp, "%s\n", h);
678                   freez(h);
679                   fclose(fp);
680                }
681                return(NULL);
682             }
683          }
684       }
685    }
686
687 trust_url_not_trusted:
688    free_http_request(rhttp);
689
690    hostport = url_encode(http->hostport);
691    path     = url_encode(http->path);
692
693    if (csp->referrer)
694    {
695       refer = url_encode(csp->referrer);
696    }
697    else
698    {
699       refer = url_encode("undefined");
700    }
701
702    n  = strlen(CTRUST);
703    n += strlen(hostport);
704    n += strlen(path);
705    n += strlen(refer);
706
707    p = (char *)malloc(n);
708    sprintf(p, CTRUST, hostport, path, refer);
709
710    freez(hostport);
711    freez(path);
712    freez(refer);
713
714    return(p);
715
716 }
717 #endif /* def TRUST_FILES */
718
719
720 #ifdef PCRS
721 /*********************************************************************
722  *
723  * Function    :  re_process_buffer
724  *
725  * Description :  Apply all jobs from the joblist (aka. Perl regexp's) to
726  *                the text buffer that's been accumulated in csp->iob->buf
727  *                and set csp->content_length to the modified size.
728  *
729  * Parameters  :
730  *          1  :  csp = Current client state (buffers, headers, etc...)
731  *
732  * Returns     :  a pointer to the (newly allocated) modified buffer.
733  *                
734  *
735  *********************************************************************/
736 char *re_process_buffer(struct client_state *csp)
737 {
738    int hits=0;
739    int size = csp->iob->eod - csp->iob->cur;
740    char *old=csp->iob->cur, *new = NULL;
741    pcrs_job *job, *joblist;
742
743    struct file_list *fl;
744    struct re_filterfile_spec *b;
745
746    /* Sanity first ;-) */
747    if (size <= 0)
748    {
749       return(strdup(""));
750    }
751
752    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
753    {
754       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
755       return(strdup(""));
756    }
757
758    joblist = b->joblist;
759
760
761    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
762               csp->http->hostport, csp->http->path, size);
763
764    /* Apply all jobs from the joblist */
765    for (job = joblist; NULL != job; job = job->next)
766    {
767       hits += pcrs_exec_substitution(job, old, size, &new, &size);
768       if (old != csp->iob->cur) free(old);
769       old=new;
770    }
771
772    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
773
774    csp->content_length = size;
775
776    /* fwiw, reset the iob */
777    IOB_RESET(csp);
778    return(new);
779
780 }
781 #endif /* def PCRS */
782
783
784 /*********************************************************************
785  *
786  * Function    :  url_actions
787  *
788  * Description :  Gets the actions for this URL.
789  *
790  * Parameters  :
791  *          1  :  http = http_request request for blocked URLs
792  *          2  :  csp = Current client state (buffers, headers, etc...)
793  *
794  * Returns     :  N/A
795  *
796  *********************************************************************/
797 void url_actions(struct http_request *http, 
798                  struct client_state *csp)
799 {
800    struct file_list *fl;
801    struct url_actions *b;
802
803    init_current_action(csp->action);
804
805    if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
806    {
807       return;
808    }
809
810    apply_url_actions(csp->action, http, b);
811 }
812
813
814 /*********************************************************************
815  *
816  * Function    :  apply_url_actions
817  *
818  * Description :  Applies a list of URL actions.
819  *
820  * Parameters  :
821  *          1  :  action = Destination.
822  *          2  :  http = Current URL
823  *          3  :  b = list of URL actions to apply
824  *
825  * Returns     :  N/A
826  *
827  *********************************************************************/
828 void apply_url_actions(struct current_action_spec *action, 
829                        struct http_request *http, 
830                        struct url_actions *b)
831 {
832    struct url_spec url[1];
833
834    if (b == NULL)
835    {
836       /* Should never happen */
837       return;
838    }
839
840    *url = dsplit(http->host);
841
842    /* if splitting the domain fails, punt */
843    if (url->dbuf == NULL)
844    {
845       return;
846    }
847
848    for (b = b->next; NULL != b; b = b->next)
849    {
850       if ((b->url->port == 0) || (b->url->port == http->port))
851       {
852          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
853          {
854             if ((b->url->path == NULL) ||
855 #ifdef REGEX
856                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
857 #else
858                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
859 #endif
860             )
861             {
862                merge_current_action(action, b->action);
863             }
864          }
865       }
866    }
867
868    freez(url->dbuf);
869    freez(url->dvec);
870 }
871
872
873 /*********************************************************************
874  *
875  * Function    :  forward_url
876  *
877  * Description :  Should we forward this to another proxy?
878  *
879  * Parameters  :
880  *          1  :  http = http_request request for current URL
881  *          2  :  csp = Current client state (buffers, headers, etc...)
882  *
883  * Returns     :  Pointer to forwarding information.
884  *
885  *********************************************************************/
886 const struct forward_spec * forward_url(struct http_request *http,
887                                         struct client_state *csp)
888 {
889    static const struct forward_spec fwd_default[1] = { 0 }; /* All zeroes */
890    struct forward_spec *fwd = csp->config->forward;
891    struct url_spec url[1];
892
893    if (fwd == NULL)
894    {
895       return(fwd_default);
896    }
897
898    *url = dsplit(http->host);
899
900    /* if splitting the domain fails, punt */
901    if (url->dbuf == NULL)
902    {
903       return(fwd_default);
904    }
905
906    while (fwd != NULL)
907    {
908       if ((fwd->url->port == 0) || (fwd->url->port == http->port))
909       {
910          if ((fwd->url->domain[0] == '\0') || (domaincmp(fwd->url, url) == 0))
911          {
912             if ((fwd->url->path == NULL) ||
913 #ifdef REGEX
914                (regexec(fwd->url->preg, http->path, 0, NULL, 0) == 0)
915 #else
916                (strncmp(fwd->url->path, http->path, fwd->url->pathlen) == 0)
917 #endif
918             )
919             {
920                freez(url->dbuf);
921                freez(url->dvec);
922                return(fwd);
923             }
924          }
925       }
926       fwd = fwd->next;
927    }
928
929    freez(url->dbuf);
930    freez(url->dvec);
931    return(fwd_default);
932
933 }
934
935
936 /*********************************************************************
937  *
938  * Function    :  dsplit
939  *
940  * Description :  Takes a domain and returns a pointer to a url_spec
941  *                structure populated with dbuf, dcnt and dvec.  The
942  *                other fields in the structure that is returned are zero.
943  *
944  * Parameters  :
945  *          1  :  domain = a URL address
946  *
947  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
948  *
949  *********************************************************************/
950 struct url_spec dsplit(char *domain)
951 {
952    struct url_spec ret[1];
953    char *v[BUFFER_SIZE];
954    int size;
955    char *p;
956
957    memset(ret, '\0', sizeof(*ret));
958
959    if (domain[strlen(domain) - 1] == '.')
960    {
961           ret->unanchored |= ANCHOR_RIGHT;
962         }
963         if (domain[0] == '.')
964    {
965           ret->unanchored |= ANCHOR_LEFT;
966         }
967
968    ret->dbuf = strdup(domain);
969
970    /* map to lower case */
971    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
972
973    /* split the domain name into components */
974    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
975
976    if (ret->dcnt <= 0)
977    {
978       memset(ret, '\0', sizeof(ret));
979       return(*ret);
980    }
981
982    /* save a copy of the pointers in dvec */
983    size = ret->dcnt * sizeof(*ret->dvec);
984
985    if ((ret->dvec = (char **)malloc(size)))
986    {
987       memcpy(ret->dvec, v, size);
988    }
989
990
991    return(*ret);
992
993 }
994
995
996 /*********************************************************************
997  *
998  * Function    :  simple_domaincmp
999  *
1000  * Description :  Domain-wise Compare fqdn's.  The comparison is 
1001  *                both left- and right-anchored.  The individual
1002  *                domain names are compared with trivialmatch().
1003  *                This is only used by domaincmp.
1004  *
1005  * Parameters  :
1006  *          1  :  pv = array of patterns to compare
1007  *          2  :  fv = array of domain components to compare
1008  *          3  :  len = length of the arrays (both arrays are the
1009  *                      same length - if they weren't, it couldn't
1010  *                      possibly be a match).
1011  *
1012  * Returns     :  0 => domains are equivalent, else no match.
1013  *
1014  *********************************************************************/
1015 static int simple_domaincmp(char **pv, char **fv, int len)
1016 {
1017    int n;
1018
1019    for (n = 0; n < len; n++)
1020    {
1021       if (simplematch(pv[n], fv[n]))
1022       {
1023          return 1;
1024       }
1025    }
1026
1027    return 0;
1028 }
1029
1030
1031 /*********************************************************************
1032  *
1033  * Function    :  domaincmp
1034  *
1035  * Description :  Domain-wise Compare fqdn's. Governed by the bimap in
1036  *                pattern->unachored, the comparison is un-, left-,
1037  *                right-anchored, or both.
1038  *                The individual domain names are compared with
1039  *                trivialmatch().
1040  *
1041  * Parameters  :
1042  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
1043  *          2  :  fqdn = domain name against which the patterns are compared.
1044  *
1045  * Returns     :  0 => domains are equivalent, else no match.
1046  *
1047  *********************************************************************/
1048 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
1049 {
1050    char **pv, **fv;  /* vectors  */
1051    int    plen, flen;
1052    int unanchored = pattern->unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
1053
1054    plen = pattern->dcnt;
1055    flen = fqdn->dcnt;
1056
1057    if (flen < plen)
1058    {
1059       /* fqdn is too short to match this pattern */
1060       return 1;
1061    }
1062
1063    pv   = pattern->dvec;
1064    fv   = fqdn->dvec;
1065
1066    if (unanchored == ANCHOR_LEFT)
1067    {
1068       /*
1069        * Right anchored.
1070        *
1071        * Convert this into a fully anchored pattern with
1072        * the fqdn and pattern the same length
1073        */
1074       fv += (flen - plen); /* flen - plen >= 0 due to check above */
1075       return simple_domaincmp(pv, fv, plen);
1076    }
1077    else if (unanchored == 0)
1078    {
1079       /* Fully anchored, check length */
1080       if (flen != plen)
1081       {
1082          return 1;
1083       }
1084       return simple_domaincmp(pv, fv, plen);
1085    }
1086    else if (unanchored == ANCHOR_RIGHT)
1087    {
1088       /* Left anchored, ignore all extra in fqdn */
1089       return simple_domaincmp(pv, fv, plen);
1090    }
1091    else
1092    {
1093       /* Unanchored */
1094       int n;
1095       int maxn = flen - plen;
1096       for (n = 0; n <= maxn; n++)
1097       {
1098          if (!simple_domaincmp(pv, fv, plen))
1099          {
1100             return 0;
1101          }
1102          /*
1103           * Doesn't match from start of fqdn
1104           * Try skipping first part of fqdn
1105           */
1106          fv++;
1107       }
1108       return 1;
1109    }
1110 }
1111
1112
1113
1114 /*
1115   Local Variables:
1116   tab-width: 3
1117   end:
1118 */