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