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