Adding version information block.
[privoxy.git] / parsers.c
1 const char parsers_rcs[] = "$Id: parsers.c,v 1.17 2001/06/29 21:45:41 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *                Functions declared include:
8  *                   `add_to_iob', `client_cookie_adder', `client_from',
9  *                   `client_referrer', `client_send_cookie', `client_ua',
10  *                   `client_uagent', `client_x_forwarded',
11  *                   `client_x_forwarded_adder', `client_xtra_adder',
12  *                   `content_type', `crumble', `destroy_list', `enlist',
13  *                   `flush_socket', `free_http_request', `get_header',
14  *                   `list_to_text', `parse_http_request', `sed',
15  *                   and `server_set_cookie'.
16  *
17  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
18  *                IJBSWA team.  http://ijbswa.sourceforge.net
19  *
20  *                Based on the Internet Junkbuster originally written
21  *                by and Copyright (C) 1997 Anonymous Coders and 
22  *                Junkbusters Corporation.  http://www.junkbusters.com
23  *
24  *                This program is free software; you can redistribute it 
25  *                and/or modify it under the terms of the GNU General
26  *                Public License as published by the Free Software
27  *                Foundation; either version 2 of the License, or (at
28  *                your option) any later version.
29  *
30  *                This program is distributed in the hope that it will
31  *                be useful, but WITHOUT ANY WARRANTY; without even the
32  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
33  *                PARTICULAR PURPOSE.  See the GNU General Public
34  *                License for more details.
35  *
36  *                The GNU General Public License should be included with
37  *                this file.  If not, you can view it at
38  *                http://www.gnu.org/copyleft/gpl.html
39  *                or write to the Free Software Foundation, Inc., 59
40  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
41  *
42  * Revisions   :
43  *    $Log: parsers.c,v $
44  *    Revision 1.17  2001/06/29 21:45:41  oes
45  *    Indentation, CRLF->LF, Tab-> Space
46  *
47  *    Revision 1.16  2001/06/29 13:32:42  oes
48  *    - Fixed a comment
49  *    - Adapted free_http_request
50  *    - Removed logentry from cancelled commit
51  *
52  *    Revision 1.15  2001/06/03 19:12:38  oes
53  *    deleted const struct interceptors
54  *
55  *    Revision 1.14  2001/06/01 18:49:17  jongfoster
56  *    Replaced "list_share" with "list" - the tiny memory gain was not
57  *    worth the extra complexity.
58  *
59  *    Revision 1.13  2001/05/31 21:30:33  jongfoster
60  *    Removed list code - it's now in list.[ch]
61  *    Renamed "permission" to "action", and changed many features
62  *    to use the actions file rather than the global config.
63  *
64  *    Revision 1.12  2001/05/31 17:33:13  oes
65  *
66  *    CRLF -> LF
67  *
68  *    Revision 1.11  2001/05/29 20:11:19  joergs
69  *    '/* inside comment' warning removed.
70  *
71  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
72  *    Unified blocklist/imagelist/permissionslist.
73  *    File format is still under discussion, but the internal changes
74  *    are (mostly) done.
75  *
76  *    Also modified interceptor behaviour:
77  *    - We now intercept all URLs beginning with one of the following
78  *      prefixes (and *only* these prefixes):
79  *        * http://i.j.b/
80  *        * http://ijbswa.sf.net/config/
81  *        * http://ijbswa.sourceforge.net/config/
82  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
83  *    - Internal changes so that intercepted and fast redirect pages
84  *      are not replaced with an image.
85  *    - Interceptors now have the option to send a binary page direct
86  *      to the client. (i.e. ijb-send-banner uses this)
87  *    - Implemented show-url-info interceptor.  (Which is why I needed
88  *      the above interceptors changes - a typical URL is
89  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
90  *      The previous mechanism would not have intercepted that, and
91  *      if it had been intercepted then it then it would have replaced
92  *      it with an image.)
93  *
94  *    Revision 1.9  2001/05/28 17:26:33  jongfoster
95  *    Fixing segfault if last header was crunched.
96  *    Fixing Windows build (snprintf() is _snprintf() under Win32, but we
97  *    can use the cross-platform sprintf() instead.)
98  *
99  *    Revision 1.8  2001/05/27 22:17:04  oes
100  *
101  *    - re_process_buffer no longer writes the modified buffer
102  *      to the client, which was very ugly. It now returns the
103  *      buffer, which it is then written by chat.
104  *
105  *    - content_length now adjusts the Content-Length: header
106  *      for modified documents rather than crunch()ing it.
107  *      (Length info in csp->content_length, which is 0 for
108  *      unmodified documents)
109  *
110  *    - For this to work, sed() is called twice when filtering.
111  *
112  *    Revision 1.7  2001/05/27 13:19:06  oes
113  *    Patched Joergs solution for the content-length in.
114  *
115  *    Revision 1.6  2001/05/26 13:39:32  jongfoster
116  *    Only crunches Content-Length header if applying RE filtering.
117  *    Without this fix, Microsoft Windows Update wouldn't work.
118  *
119  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
120  *    Automatic reloading of config file.
121  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
122  *    Most of the global variables have been moved to a new
123  *    struct configuration_spec, accessed through csp->config->globalname
124  *    Most of the globals remaining are used by the Win32 GUI.
125  *
126  *    Revision 1.4  2001/05/22 18:46:04  oes
127  *
128  *    - Enabled filtering banners by size rather than URL
129  *      by adding patterns that replace all standard banner
130  *      sizes with the "Junkbuster" gif to the re_filterfile
131  *
132  *    - Enabled filtering WebBugs by providing a pattern
133  *      which kills all 1x1 images
134  *
135  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
136  *      which is selected by the (nonstandard and therefore
137  *      capital) letter 'U' in the option string.
138  *      It causes the quantifiers to be ungreedy by default.
139  *      Appending a ? turns back to greedy (!).
140  *
141  *    - Added a new interceptor ijb-send-banner, which
142  *      sends back the "Junkbuster" gif. Without imagelist or
143  *      MSIE detection support, or if tinygif = 1, or the
144  *      URL isn't recognized as an imageurl, a lame HTML
145  *      explanation is sent instead.
146  *
147  *    - Added new feature, which permits blocking remote
148  *      script redirects and firing back a local redirect
149  *      to the browser.
150  *      The feature is conditionally compiled, i.e. it
151  *      can be disabled with --disable-fast-redirects,
152  *      plus it must be activated by a "fast-redirects"
153  *      line in the config file, has its own log level
154  *      and of course wants to be displayed by show-proxy-args
155  *      Note: Boy, all the #ifdefs in 1001 locations and
156  *      all the fumbling with configure.in and acconfig.h
157  *      were *way* more work than the feature itself :-(
158  *
159  *    - Because a generic redirect template was needed for
160  *      this, tinygif = 3 now uses the same.
161  *
162  *    - Moved GIFs, and other static HTTP response templates
163  *      to project.h
164  *
165  *    - Some minor fixes
166  *
167  *    - Removed some >400 CRs again (Jon, you really worked
168  *      a lot! ;-)
169  *
170  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
171  *    Version 2.9.4 checkin.
172  *    - Merged popupfile and cookiefile, and added control over PCRS
173  *      filtering, in new "permissionsfile".
174  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
175  *      file error you now get a message box (in the Win32 GUI) rather
176  *      than the program exiting with no explanation.
177  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
178  *      skipping.
179  *    - Removed tabs from "config"
180  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
181  *    - Bumped up version number.
182  *
183  *    Revision 1.2  2001/05/17 23:02:36  oes
184  *     - Made referrer option accept 'L' as a substitute for 'ยง'
185  *
186  *    Revision 1.1.1.1  2001/05/15 13:59:01  oes
187  *    Initial import of version 2.9.3 source tree
188  *
189  *
190  *********************************************************************/
191 \f
192
193 #include "config.h"
194
195 #include <stdio.h>
196 #include <sys/types.h>
197 #include <stdlib.h>
198 #include <ctype.h>
199 #include <string.h>
200
201 #ifndef _WIN32
202 #include <unistd.h>
203 #endif
204
205 #include "project.h"
206 #include "list.h"
207 #include "parsers.h"
208 #include "encode.h"
209 #include "filters.h"
210 #include "loaders.h"
211 #include "showargs.h"
212 #include "jcc.h"
213 #include "ssplit.h"
214 #include "errlog.h"
215 #include "jbsockets.h"
216 #include "miscutil.h"
217 #include "cgi.h"
218
219 const char parsers_h_rcs[] = PARSERS_H_VERSION;
220
221 /* Fix a problem with Solaris.  There should be no effect on other
222  * platforms.
223  * Solaris's isspace() is a macro which uses it's argument directly
224  * as an array index.  Therefore we need to make sure that high-bit
225  * characters generate +ve values, and ideally we also want to make
226  * the argument match the declared parameter type of "int".
227  * 
228  * Why did they write a character function that can't take a simple 
229  * "char" argument?  Doh!
230  */
231 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
232 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
233
234
235 const struct parsers client_patterns[] = {
236    { "referer:",                 8,    client_referrer },
237    { "user-agent:",              11,   client_uagent },
238    { "ua-",                      3,    client_ua },
239    { "from:",                    5,    client_from },
240    { "cookie:",                  7,    client_send_cookie },
241    { "x-forwarded-for:",         16,   client_x_forwarded },
242    { "proxy-connection:",        17,   crumble },
243 #ifdef DENY_GZIP
244    { "Accept-Encoding: gzip",    21,   crumble },
245 #endif /* def DENY_GZIP */
246 #if defined(DETECT_MSIE_IMAGES)
247    { "Accept:",                   7,   client_accept },
248 #endif /* defined(DETECT_MSIE_IMAGES) */
249 #ifdef FORCE_LOAD
250    { "Host:",                     5,   client_host },
251 #endif /* def FORCE_LOAD */
252 /* { "if-modified-since:",       18,   crumble }, */
253    { NULL,                       0,    NULL }
254 };
255
256
257 const struct parsers server_patterns[] = {
258    { "set-cookie:",        11, server_set_cookie },
259    { "connection:",        11, crumble },
260    { "Content-Type:",      13, content_type },
261    { "Content-Length:",    15, content_length },
262    { NULL, 0, NULL }
263 };
264
265
266 void (* const add_client_headers[])(struct client_state *) = {
267    client_cookie_adder,
268    client_x_forwarded_adder,
269    client_xtra_adder,
270    NULL
271 };
272
273
274 void (* const add_server_headers[])(struct client_state *) = {
275    NULL
276 };
277
278
279 /*********************************************************************
280  *
281  * Function    :  flush_socket
282  *
283  * Description :  Write any pending "buffered" content.
284  *
285  * Parameters  :
286  *          1  :  fd = file descriptor of the socket to read
287  *          2  :  csp = Current client state (buffers, headers, etc...)
288  *
289  * Returns     :  On success, the number of bytes written are returned (zero
290  *                indicates nothing was written).  On error, -1 is returned,
291  *                and errno is set appropriately.  If count is zero and the
292  *                file descriptor refers to a regular file, 0 will be
293  *                returned without causing any other effect.  For a special
294  *                file, the results are not portable.
295  *
296  *********************************************************************/
297 int flush_socket(int fd, struct client_state *csp)
298 {
299    struct iob *iob = csp->iob;
300    int n = iob->eod - iob->cur;
301
302    if (n <= 0)
303    {
304       return(0);
305    }
306
307    n = write_socket(fd, iob->cur, n);
308    iob->eod = iob->cur = iob->buf;
309    return(n);
310
311 }
312
313
314 /*********************************************************************
315  *
316  * Function    :  add_to_iob
317  *
318  * Description :  Add content to the buffered page.
319  *
320  * Parameters  :
321  *          1  :  csp = Current client state (buffers, headers, etc...)
322  *          2  :  buf = holds the content to be added to the page
323  *          3  :  n = number of bytes to be added
324  *
325  * Returns     :  Number of bytes in the content buffer.
326  *
327  *********************************************************************/
328 int add_to_iob(struct client_state *csp, char *buf, int n)
329 {
330    struct iob *iob = csp->iob;
331    int have, need;
332    char *p;
333
334    have = iob->eod - iob->cur;
335
336    if (n <= 0)
337    {
338       return(have);
339    }
340
341    need = have + n;
342
343    if ((p = (char *)malloc(need + 1)) == NULL)
344    {
345       log_error(LOG_LEVEL_ERROR, "malloc() iob failed: %E");
346       return(-1);
347    }
348
349    if (have)
350    {
351       /* there is something in the buffer - save it */
352       memcpy(p, iob->cur, have);
353
354       /* replace the buffer with the new space */
355       freez(iob->buf);
356       iob->buf = p;
357
358       /* point to the end of the data */
359       p += have;
360    }
361    else
362    {
363       /* the buffer is empty, free it and reinitialize */
364       freez(iob->buf);
365       iob->buf = p;
366    }
367
368    /* copy the new data into the iob buffer */
369    memcpy(p, buf, n);
370
371    /* point to the end of the data */
372    p += n;
373
374    /* null terminate == cheap insurance */
375    *p = '\0';
376
377    /* set the pointers to the new values */
378    iob->cur = iob->buf;
379    iob->eod = p;
380
381    return(need);
382
383 }
384
385
386 /*********************************************************************
387  *
388  * Function    :  get_header
389  *
390  * Description :  This (odd) routine will parse the csp->iob
391  *
392  * Parameters  :
393  *          1  :  csp = Current client state (buffers, headers, etc...)
394  *
395  * Returns     :  Any one of the following:
396  *
397  * 1) a pointer to a dynamically allocated string that contains a header line
398  * 2) NULL  indicating that the end of the header was reached
399  * 3) ""    indicating that the end of the iob was reached before finding
400  *          a complete header line.
401  *
402  *********************************************************************/
403 char *get_header(struct client_state *csp)
404 {
405    struct iob *iob;
406    char *p, *q, *ret;
407    iob = csp->iob;
408
409    if ((iob->cur == NULL)
410       || ((p = strchr(iob->cur, '\n')) == NULL))
411    {
412       return(""); /* couldn't find a complete header */
413    }
414
415    *p = '\0';
416
417    ret = strdup(iob->cur);
418
419    iob->cur = p+1;
420
421    if ((q = strchr(ret, '\r'))) *q = '\0';
422
423    /* is this a blank linke (i.e. the end of the header) ? */
424    if (*ret == '\0')
425    {
426       freez(ret);
427       return(NULL);
428    }
429
430    return(ret);
431
432 }
433
434
435 /*********************************************************************
436  *
437  * Function    :  sed
438  *
439  * Description :  add, delete or modify lines in the HTTP header streams.
440  *                On entry, it receives a linked list of headers space
441  *                that was allocated dynamically (both the list nodes
442  *                and the header contents).
443  *
444  *                As a side effect it frees the space used by the original
445  *                header lines.
446  *
447  * Parameters  :
448  *          1  :  pats = list of patterns to match against headers
449  *          2  :  more_headers = list of functions to add more
450  *                headers (client or server)
451  *          3  :  csp = Current client state (buffers, headers, etc...)
452  *
453  * Returns     :  Single pointer to a fully formed header.
454  *
455  *********************************************************************/
456 char *sed(const struct parsers pats[], void (* const more_headers[])(struct client_state *), struct client_state *csp)
457 {
458    struct list *p;
459    const struct parsers *v;
460    char *hdr;
461    void (* const *f)();
462
463    for (v = pats; v->str ; v++)
464    {
465       for (p = csp->headers->next; p ; p = p->next)
466       {
467          /* Header crunch()ed in previous run? -> ignore */
468          if (p->str == NULL) continue;
469
470          if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
471
472          if (strncmpic(p->str, v->str, v->len) == 0)
473          {
474             hdr = v->parser(v, p->str, csp);
475             freez(p->str);
476             p->str = hdr;
477          }
478       }
479    }
480
481    /* place any additional headers on the csp->headers list */
482    for (f = more_headers; *f ; f++)
483    {
484       (*f)(csp);
485    }
486
487    /* add the blank line at the end of the header, if necessary */
488    if ( (csp->headers->last == NULL)
489      || (csp->headers->last->str == NULL)
490      || (*csp->headers->last->str != '\0') )
491    {
492       enlist(csp->headers, "");
493    }
494
495    hdr = list_to_text(csp->headers);
496
497    return(hdr);
498
499 }
500
501
502 /*********************************************************************
503  *
504  * Function    :  free_http_request
505  *
506  * Description :  Freez a http_request structure
507  *
508  * Parameters  :
509  *          1  :  http = points to a http_request structure to free
510  *
511  * Returns     :  N/A
512  *
513  *********************************************************************/
514 void free_http_request(struct http_request *http)
515 {
516    freez(http->cmd);
517    freez(http->gpc);
518    freez(http->host);
519    freez(http->hostport);
520    freez(http->path);
521    freez(http->ver);
522    freez(http->host_ip_addr_str);
523
524 }
525
526
527 /*********************************************************************
528  *
529  * Function    :  parse_http_request
530  *
531  * Description :  Parse out the host and port from the URL.  Find the
532  *                hostname & path, port (if ':'), and/or password (if '@')
533  *
534  * Parameters  :
535  *          1  :  req = URL (or is it URI?) to break down
536  *          2  :  http = pointer to the http structure to hold elements
537  *          3  :  csp = Current client state (buffers, headers, etc...)
538  *
539  * Returns     :  N/A
540  *
541  *********************************************************************/
542 void parse_http_request(char *req, struct http_request *http, struct client_state *csp)
543 {
544    char *buf, *v[10], *url, *p;
545    int n;
546
547    memset(http, '\0', sizeof(*http));
548
549    http->cmd = strdup(req);
550
551    buf = strdup(req);
552    n = ssplit(buf, " \r\n", v, SZ(v), 1, 1);
553
554    if (n == 3)
555    {
556       /* this could be a CONNECT request */
557       if (strcmpic(v[0], "connect") == 0)
558       {
559          http->ssl      = 1;
560          http->gpc      = strdup(v[0]);
561          http->hostport = strdup(v[1]);
562          http->ver      = strdup(v[2]);
563       }
564
565 #ifdef WEBDAV
566
567 /* This next line is a little ugly, but it simplifies the if statement below. */
568 /* Basically if using webDAV, we want the OR condition to use these too.      */
569
570 /*
571  * by haroon
572  * These are the headers as defined in RFC2518 to add webDAV support
573  */
574
575 #define OR_WEBDAV || \
576          (0 == strcmpic(v[0], "propfind")) || \
577          (0 == strcmpic(v[0], "proppatch")) || \
578          (0 == strcmpic(v[0], "move")) || \
579          (0 == strcmpic(v[0], "copy")) || \
580          (0 == strcmpic(v[0], "mkcol")) || \
581          (0 == strcmpic(v[0], "lock")) || \
582          (0 == strcmpic(v[0], "unlock"))
583
584 #else /* No webDAV support is enabled.  Provide an empty OR_WEBDAV macro. */
585
586 #define OR_WEBDAV
587
588 #endif
589
590       /* or it could be a GET or a POST (possibly webDAV too) */
591       if ((strcmpic(v[0], "get")  == 0) ||
592           (strcmpic(v[0], "head") == 0) OR_WEBDAV ||
593           (strcmpic(v[0], "post") == 0))
594       {
595          http->ssl      = 0;
596          http->gpc      = strdup(v[0]);
597          url            = v[1];
598          http->ver      = strdup(v[2]);
599
600          if (strncmpic(url, "http://",  7) == 0)
601          {
602             url += 7;
603          }
604          else if (strncmpic(url, "https://", 8) == 0)
605          {
606             url += 8;
607          }
608          else
609          {
610             url = NULL;
611          }
612
613          if (url)
614          {
615             if (p = strchr(url, '/'))
616             {
617                http->path = strdup(p);
618                *p = '\0';
619                http->hostport = strdup(url);
620             }
621             /* 
622              * Repair broken HTTP requests that don't contain a path
623              */
624             else
625             {
626                /* Repair hostport & path */
627                http->path = strdup("/");
628                http->hostport = strdup(url);
629
630                /* Even repair cmd in case we're just forwarding. Boy are we nice ;-)  */
631                freez(http->cmd);
632                http->cmd = strsav(http->cmd, http->gpc);
633                http->cmd = strsav(http->cmd, " / ");
634                http->cmd = strsav(http->cmd, http->ver);
635             }
636          }
637       }
638    }
639
640    freez(buf);
641
642
643    if (http->hostport == NULL)
644    {
645       free_http_request(http);
646       return;
647    }
648
649    buf = strdup(http->hostport);
650
651
652    /* check if url contains password */
653    n = ssplit(buf, "@", v, SZ(v), 1, 1);
654    if (n == 2)
655    {
656       char * newbuf = NULL;
657       newbuf = strdup(v[1]);
658       freez(buf);
659       buf = newbuf;
660    }
661
662    n = ssplit(buf, ":", v, SZ(v), 1, 1);
663
664    if (n == 1)
665    {
666       http->host = strdup(v[0]);
667       http->port = 80;
668    }
669
670    if (n == 2)
671    {
672       http->host = strdup(v[0]);
673       http->port = atoi(v[1]);
674    }
675
676    freez(buf);
677
678    if (http->host == NULL)
679    {
680       free_http_request(http);
681    }
682
683    if (http->path == NULL)
684    {
685       http->path = strdup("");
686    }
687
688 }
689
690
691 /* here begins the family of parser functions that reformat header lines */
692
693
694 /*********************************************************************
695  *
696  * Function    :  crumble
697  *
698  * Description :  This is called if a header matches a pattern to "crunch"
699  *
700  * Parameters  :
701  *          1  :  v = Pointer to parsers structure, which basically holds
702  *                headers (client or server) that we want to "crunch"
703  *          2  :  s = header (from sed) to "crunch"
704  *          3  :  csp = Current client state (buffers, headers, etc...)
705  *
706  * Returns     :  Always NULL.
707  *
708  *********************************************************************/
709 char *crumble(const struct parsers *v, char *s, struct client_state *csp)
710 {
711    log_error(LOG_LEVEL_HEADER, "crunch!");
712    return(NULL);
713
714 }
715
716
717 /*********************************************************************
718  *
719  * Function    :  content_type
720  *
721  * Description :  Is this a text/.* or javascript MIME Type?
722  *
723  * Parameters  :
724  *          1  :  v = ignored
725  *          2  :  s = header string we are "considering"
726  *          3  :  csp = Current client state (buffers, headers, etc...)
727  *
728  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
729  *
730  *********************************************************************/
731 char *content_type(const struct parsers *v, char *s, struct client_state *csp)
732 {
733    if (strstr(s, " text/") || strstr(s, "application/x-javascript"))
734       csp->content_type = CT_TEXT;
735    else if (strstr(s, " image/gif"))
736       csp->content_type = CT_GIF;
737    else
738       csp->content_type = 0;
739
740    return(strdup(s));
741
742 }
743
744
745 /*********************************************************************
746  *
747  * Function    :  content_length
748  *
749  * Description :  Adjust Content-Length header if we modified
750  *                the body.
751  *
752  * Parameters  :
753  *          1  :  v = ignored
754  *          2  :  s = header string we are "considering"
755  *          3  :  csp = Current client state (buffers, headers, etc...)
756  *
757  * Returns     :  A duplicate string pointer to this header (ie. pass thru)
758  *
759  *********************************************************************/
760 char *content_length(const struct parsers *v, char *s, struct client_state *csp)
761 {
762    if (csp->content_length != 0) /* Content has been modified */
763    {
764       s = (char *) zalloc(100);
765       sprintf(s, "Content-Length: %d", csp->content_length);
766
767         log_error(LOG_LEVEL_HEADER, "Adjust Content-Length to %d", csp->content_length);
768       return(s);
769    }
770    else
771    {
772       return(strdup(s));
773    }
774
775 }
776
777
778 /*********************************************************************
779  *
780  * Function    :  client_referrer
781  *
782  * Description :  Handle the "referer" config setting properly.
783  *                Called from `sed'.
784  *
785  * Parameters  :
786  *          1  :  v = ignored
787  *          2  :  s = header (from sed) to "crunch"
788  *          3  :  csp = Current client state (buffers, headers, etc...)
789  *
790  * Returns     :  NULL if crunched, or a malloc'ed string with the original
791  *                or modified header
792  *
793  *********************************************************************/
794 char *client_referrer(const struct parsers *v, char *s, struct client_state *csp)
795 {
796    const char * newval;
797 #ifdef FORCE_LOAD
798    /* Since the referrer can include the prefix even
799     * even if the request itself is non-forced, we must
800     * clean it unconditionally 
801     */
802    strclean(s, FORCE_PREFIX);
803 #endif /* def FORCE_LOAD */
804
805 #ifdef TRUST_FILES
806    csp->referrer = strdup(s);
807 #endif /* def TRUST_FILES */
808
809    /*
810     * Are we sending referer?
811     */
812    if ((csp->action->flags & ACTION_HIDE_REFERER) == 0)
813    {
814       return(strdup(s));
815    }
816
817    newval = csp->action->string[ACTION_STRING_REFERER];
818
819    /*
820     * Are we blocking referer?
821     */
822    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
823    {
824       log_error(LOG_LEVEL_HEADER, "crunch!");
825       return(NULL);
826    }
827
828    /*
829     * Are we forging referer?
830     */
831    if (0 == strcmpic(newval, "forge"))
832    {
833       /*
834        * Forge a referer as http://[hostname:port of REQUEST]/
835        * to fool stupid checks for in-site links
836        */
837       log_error(LOG_LEVEL_HEADER, "crunch+forge!");
838       s = strsav(NULL, "Referer: ");
839       s = strsav(s, "http://");
840       s = strsav(s, csp->http->hostport);
841       s = strsav(s, "/");
842       return(s);
843    }
844
845    /*
846     * Have we got a fixed referer?
847     */
848    if (0 == strncmpic(newval, "http://", 7))
849    {
850       /*
851        * We have a specific (fixed) referer we want to send.
852        */
853
854       log_error(LOG_LEVEL_HEADER, "modified");
855
856       s = strsav( NULL, "Referer: " );
857       s = strsav( s, newval );
858       return(s);
859    }
860
861    /* Should never get here! */
862    log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval);
863
864    /*
865     * Forge is probably the best default.
866     *
867     * Forge a referer as http://[hostname:port of REQUEST]/
868     * to fool stupid checks for in-site links
869     */
870    log_error(LOG_LEVEL_HEADER, "crunch+forge!");
871    s = strsav(NULL, "Referer: ");
872    s = strsav(s, "http://");
873    s = strsav(s, csp->http->hostport);
874    s = strsav(s, "/");
875    return(s);
876 }
877
878
879 /*********************************************************************
880  *
881  * Function    :  client_uagent
882  *
883  * Description :  Handle the "user-agent" config setting properly.
884  *                Called from `sed'.
885  *
886  * Parameters  :
887  *          1  :  v = ignored
888  *          2  :  s = header (from sed) to "crunch"
889  *          3  :  csp = Current client state (buffers, headers, etc...)
890  *
891  * Returns     :  A malloc'ed pointer to the default agent, or
892  *                a malloc'ed string pointer to this header (ie. pass thru).
893  *
894  *********************************************************************/
895 char *client_uagent(const struct parsers *v, char *s, struct client_state *csp)
896 {
897    const char * newval;
898
899 #ifdef DETECT_MSIE_IMAGES
900    if (strstr (s, "MSIE "))
901    {
902       /* This is Microsoft Internet Explorer.
903        * Enable auto-detect.
904        */
905       csp->accept_types |= ACCEPT_TYPE_IS_MSIE;
906    }
907 #endif /* def DETECT_MSIE_IMAGES */
908
909    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
910    {
911       return(strdup(s));
912    }
913
914    newval = csp->action->string[ACTION_STRING_USER_AGENT];
915    if (newval == NULL)
916    {
917       return(strdup(s));
918    }
919
920    log_error(LOG_LEVEL_HEADER, "modified");
921
922    s = strsav( NULL, "User-Agent: " );
923    s = strsav( s, newval );
924    return(s);
925
926 }
927
928
929 /*********************************************************************
930  *
931  * Function    :  client_ua
932  *
933  * Description :  Handle "ua-" headers properly.  Called from `sed'.
934  *
935  * Parameters  :
936  *          1  :  v = ignored
937  *          2  :  s = header (from sed) to "crunch"
938  *          3  :  csp = Current client state (buffers, headers, etc...)
939  *
940  * Returns     :  NULL if crunched, or a malloc'ed string to original header
941  *
942  *********************************************************************/
943 char *client_ua(const struct parsers *v, char *s, struct client_state *csp)
944 {
945    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0)
946    {
947       return(strdup(s));
948    }
949    else
950    {
951       log_error(LOG_LEVEL_HEADER, "crunch!");
952       return(NULL);
953    }
954 }
955
956
957 /*********************************************************************
958  *
959  * Function    :  client_from
960  *
961  * Description :  Handle the "from" config setting properly.
962  *                Called from `sed'.
963  *
964  * Parameters  :
965  *          1  :  v = ignored
966  *          2  :  s = header (from sed) to "crunch"
967  *          3  :  csp = Current client state (buffers, headers, etc...)
968  *
969  * Returns     :  NULL if crunched, or a malloc'ed string to
970  *                modified/original header.
971  *
972  *********************************************************************/
973 char *client_from(const struct parsers *v, char *s, struct client_state *csp)
974 {
975    const char * newval;
976
977    if ((csp->action->flags & ACTION_HIDE_FROM) == 0)
978    {
979       return(strdup(s));
980    }
981
982    newval = csp->action->string[ACTION_STRING_FROM];
983
984    /*
985     * Are we blocking referer?
986     */
987    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
988    {
989       log_error(LOG_LEVEL_HEADER, "crunch!");
990       return(NULL);
991    }
992
993    log_error(LOG_LEVEL_HEADER, " modified");
994
995    s = strsav( NULL, "From: " );
996    s = strsav( s, newval );
997    return(s);
998
999 }
1000
1001
1002 /*********************************************************************
1003  *
1004  * Function    :  client_send_cookie
1005  *
1006  * Description :  Handle the "cookie" header properly.  Called from `sed'.
1007  *                If cookie is accepted, add it to the cookie_list,
1008  *                else we crunch it.  Mmmmmmmmmmm ... cookie ......
1009  *
1010  * Parameters  :
1011  *          1  :  v = pattern of cookie `sed' found matching
1012  *          2  :  s = header (from sed) to "crunch"
1013  *          3  :  csp = Current client state (buffers, headers, etc...)
1014  *
1015  * Returns     :  Always NULL.
1016  *
1017  *********************************************************************/
1018 char *client_send_cookie(const struct parsers *v, char *s, struct client_state *csp)
1019 {
1020    if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0)
1021    {
1022       enlist(csp->cookie_list, s + v->len + 1);
1023    }
1024    else
1025    {
1026       log_error(LOG_LEVEL_HEADER, " crunch!");
1027    }
1028
1029    /*
1030     * Always return NULL here.  The cookie header
1031     * will be sent at the end of the header.
1032     */
1033    return(NULL);
1034
1035 }
1036
1037
1038 /*********************************************************************
1039  *
1040  * Function    :  client_x_forwarded
1041  *
1042  * Description :  Handle the "x-forwarded-for" config setting properly,
1043  *                also used in the add_client_headers list.  Called from `sed'.
1044  *
1045  * Parameters  :
1046  *          1  :  v = ignored
1047  *          2  :  s = header (from sed) to "crunch"
1048  *          3  :  csp = Current client state (buffers, headers, etc...)
1049  *
1050  * Returns     :  Always NULL.
1051  *
1052  *********************************************************************/
1053 char *client_x_forwarded(const struct parsers *v, char *s, struct client_state *csp)
1054 {
1055    if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0)
1056    {
1057       /* Save it so we can re-add it later */
1058       csp->x_forwarded = strdup(s);
1059    }
1060
1061    /*
1062     * Always return NULL, since this information
1063     * will be sent at the end of the header.
1064     */
1065
1066    return(NULL);
1067
1068 }
1069
1070 #if defined(DETECT_MSIE_IMAGES)
1071 /*********************************************************************
1072  *
1073  * Function    :  client_accept
1074  *
1075  * Description :  Detect whether the client wants HTML or an image.
1076  *                Clients do not always make this information available
1077  *                in a sane way.  Always passes the header through
1078  *                the proxy unchanged.
1079  *
1080  * Parameters  :
1081  *          1  :  v = Ignored.
1082  *          2  :  s = Header string.  Null terminated.
1083  *          3  :  csp = Current client state (buffers, headers, etc...)
1084  *
1085  * Returns     :  Duplicate of argument s.
1086  *
1087  *********************************************************************/
1088 char *client_accept(const struct parsers *v, char *s, struct client_state *csp)
1089 {
1090 #ifdef DETECT_MSIE_IMAGES
1091    if (strstr (s, "image/gif"))
1092    {
1093       /* Client will accept HTML.  If this seems counterintuitive,
1094        * blame Microsoft. 
1095        */
1096       csp->accept_types |= ACCEPT_TYPE_MSIE_HTML;
1097    }
1098    else
1099    {
1100       csp->accept_types |= ACCEPT_TYPE_MSIE_IMAGE;
1101    }
1102 #endif /* def DETECT_MSIE_IMAGES */
1103
1104    return(strdup(s));
1105
1106 }
1107 #endif /* defined(DETECT_MSIE_IMAGES) */
1108
1109
1110
1111 /* the following functions add headers directly to the header list */
1112
1113
1114 /*********************************************************************
1115  *
1116  * Function    :  client_cookie_adder
1117  *
1118  * Description :  Used in the add_client_headers list.  Called from `sed'.
1119  *
1120  * Parameters  :
1121  *          1  :  csp = Current client state (buffers, headers, etc...)
1122  *
1123  * Returns     :  N/A
1124  *
1125  *********************************************************************/
1126 void client_cookie_adder(struct client_state *csp)
1127 {
1128    struct list *lst;
1129    char *tmp = NULL;
1130    char *e;
1131
1132    for (lst = csp->cookie_list->next; lst ; lst = lst->next)
1133    {
1134       if (tmp)
1135       {
1136          tmp = strsav(tmp, "; ");
1137       }
1138       tmp = strsav(tmp, lst->str);
1139    }
1140
1141    for (lst = csp->action->multi[ACTION_MULTI_WAFER]->next;  lst ; lst = lst->next)
1142    {
1143       if (tmp)
1144       {
1145          tmp = strsav(tmp, "; ");
1146       }
1147
1148       if ((e = cookie_encode(lst->str)))
1149       {
1150          tmp = strsav(tmp, e);
1151          freez(e);
1152       }
1153    }
1154
1155    if (tmp)
1156    {
1157       char *ret;
1158
1159       ret = strdup("Cookie: ");
1160       ret = strsav(ret, tmp);
1161       log_error(LOG_LEVEL_HEADER, "addh: %s", ret);
1162       enlist(csp->headers, ret);
1163       freez(tmp);
1164       freez(ret);
1165    }
1166
1167 }
1168
1169
1170 /*********************************************************************
1171  *
1172  * Function    :  client_xtra_adder
1173  *
1174  * Description :  Used in the add_client_headers list.  Called from `sed'.
1175  *
1176  * Parameters  :
1177  *          1  :  csp = Current client state (buffers, headers, etc...)
1178  *
1179  * Returns     :  N/A
1180  *
1181  *********************************************************************/
1182 void client_xtra_adder(struct client_state *csp)
1183 {
1184    struct list *lst = csp->action->multi[ACTION_MULTI_ADD_HEADER];
1185
1186    for (lst = lst->next; lst ; lst = lst->next)
1187    {
1188       log_error(LOG_LEVEL_HEADER, "addh: %s", lst->str);
1189       enlist(csp->headers, lst->str);
1190    }
1191
1192 }
1193
1194
1195 /*********************************************************************
1196  *
1197  * Function    :  client_x_forwarded_adder
1198  *
1199  * Description :  Used in the add_client_headers list.  Called from `sed'.
1200  *
1201  * Parameters  :
1202  *          1  :  csp = Current client state (buffers, headers, etc...)
1203  *
1204  * Returns     :  N/A
1205  *
1206  *********************************************************************/
1207 void client_x_forwarded_adder(struct client_state *csp)
1208 {
1209    char *p = NULL;
1210
1211    if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0)
1212    {
1213       return;
1214    }
1215
1216    if (csp->x_forwarded)
1217    {
1218       p = strsav(p, csp->x_forwarded);
1219       p = strsav(p, ", ");
1220       p = strsav(p, csp->ip_addr_str);
1221    }
1222    else
1223    {
1224       p = strsav(p, "X-Forwarded-For: ");
1225       p = strsav(p, csp->ip_addr_str);
1226    }
1227
1228    log_error(LOG_LEVEL_HEADER, "addh: %s", p);
1229    enlist(csp->headers, p);
1230
1231 }
1232
1233
1234 /*********************************************************************
1235  *
1236  * Function    :  server_set_cookie
1237  *
1238  * Description :  Handle the server "cookie" header properly.
1239  *                Log cookie to the jar file.  Then "crunch" it,
1240  *                or accept it.  Called from `sed'.
1241  *
1242  * Parameters  :
1243  *          1  :  v = parser pattern that matched this header
1244  *          2  :  s = header that matched this pattern
1245  *          3  :  csp = Current client state (buffers, headers, etc...)
1246  *
1247  * Returns     :  `crumble' or a newly malloc'ed string.
1248  *
1249  *********************************************************************/
1250 char *server_set_cookie(const struct parsers *v, char *s, struct client_state *csp)
1251 {
1252 #ifdef JAR_FILES
1253    if (csp->config->jar)
1254    {
1255       fprintf(csp->config->jar, "%s\t%s\n", csp->http->host, (s + v->len + 1));
1256    }
1257 #endif /* def JAR_FILES */
1258
1259    if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0)
1260    {
1261       return(crumble(v, s, csp));
1262    }
1263
1264    return(strdup(s));
1265
1266 }
1267
1268
1269 #ifdef FORCE_LOAD
1270 /*********************************************************************
1271  *
1272  * Function    :  client_host
1273  *
1274  * Description :  Clean the FORCE_PREFIX out of the 'host' http
1275  *                header, if we use force
1276  *
1277  * Parameters  :
1278  *          1  :  v = ignored
1279  *          2  :  s = header (from sed) to clean
1280  *          3  :  csp = Current client state (buffers, headers, etc...)
1281  *
1282  * Returns     :  A malloc'ed pointer to the cleaned host header 
1283  *
1284  *********************************************************************/
1285 char *client_host(const struct parsers *v, char *s, struct client_state *csp)
1286 {
1287    char *cleanhost = strdup(s);
1288  
1289    if(csp->force)
1290       strclean(cleanhost, FORCE_PREFIX);
1291  
1292    return(cleanhost);
1293 }
1294 #endif /* def FORCE_LOAD */
1295  
1296  
1297 #ifdef FORCE_LOAD 
1298 /*********************************************************************
1299  *
1300  * Function    :  strclean
1301  *
1302  * Description :  In-Situ-Eliminate all occurances of substring in 
1303  *                string
1304  *
1305  * Parameters  :
1306  *          1  :  string = string to clean
1307  *          2  :  substring = substring to eliminate
1308  *
1309  * Returns     :  Number of eliminations
1310  *
1311  *********************************************************************/
1312 int strclean(const char *string, const char *substring)
1313 {
1314    int hits = 0, len = strlen(substring);
1315    char *pos, *p;
1316
1317    while((pos = strstr(string, substring)))
1318    {
1319       p = pos + len;
1320       do
1321       {
1322          *(p - len) = *p; 
1323       }
1324       while (*p++ != '\0');
1325
1326       hits++;
1327    }
1328
1329    return(hits);
1330 }
1331 #endif /* def FORCE_LOAD */
1332
1333
1334 /*
1335   Local Variables:
1336   tab-width: 3
1337   end:
1338 */