bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / proxy / proxy_util.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* Utility routines for Apache proxy */
18 #include "mod_proxy.h"
19
20
21 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
22 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
23 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
24 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
25
26 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, 
27                                    (request_rec *r, request_rec *pr), (r, pr),
28                                    OK, DECLINED)
29
30 /* already called in the knowledge that the characters are hex digits */
31 PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
32 {
33     int i, ch;
34
35 #if !APR_CHARSET_EBCDIC
36     ch = x[0];
37     if (apr_isdigit(ch))
38         i = ch - '0';
39     else if (apr_isupper(ch))
40         i = ch - ('A' - 10);
41     else
42         i = ch - ('a' - 10);
43     i <<= 4;
44
45     ch = x[1];
46     if (apr_isdigit(ch))
47         i += ch - '0';
48     else if (apr_isupper(ch))
49         i += ch - ('A' - 10);
50     else
51         i += ch - ('a' - 10);
52     return i;
53 #else /*APR_CHARSET_EBCDIC*/
54     /* we assume that the hex value refers to an ASCII character
55      * so convert to EBCDIC so that it makes sense locally;
56      *
57      * example:
58      *
59      * client specifies %20 in URL to refer to a space char;
60      * at this point we're called with EBCDIC "20"; after turning
61      * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
62      * represents an ASCII char and convert 0x20 to EBCDIC, yielding
63      * 0x40
64      */
65     char buf[1];
66
67     if (1 == sscanf(x, "%2x", &i)) {
68         buf[0] = i & 0xFF;
69         ap_xlate_proto_from_ascii(buf, 1);
70         return buf[0];
71     }
72     else {
73         return 0;
74     }
75 #endif /*APR_CHARSET_EBCDIC*/
76 }
77
78 PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
79 {
80 #if !APR_CHARSET_EBCDIC
81     int i;
82
83     x[0] = '%';
84     i = (ch & 0xF0) >> 4;
85     if (i >= 10)
86         x[1] = ('A' - 10) + i;
87     else
88         x[1] = '0' + i;
89
90     i = ch & 0x0F;
91     if (i >= 10)
92         x[2] = ('A' - 10) + i;
93     else
94         x[2] = '0' + i;
95 #else /*APR_CHARSET_EBCDIC*/
96     static const char ntoa[] = { "0123456789ABCDEF" };
97     char buf[1];
98
99     ch &= 0xFF;
100
101     buf[0] = ch;
102     ap_xlate_proto_to_ascii(buf, 1);
103
104     x[0] = '%';
105     x[1] = ntoa[(buf[0] >> 4) & 0x0F];
106     x[2] = ntoa[buf[0] & 0x0F];
107     x[3] = '\0';
108 #endif /*APR_CHARSET_EBCDIC*/
109 }
110
111 /*
112  * canonicalise a URL-encoded string
113  */
114
115 /*
116  * Convert a URL-encoded string to canonical form.
117  * It decodes characters which need not be encoded,
118  * and encodes those which must be encoded, and does not touch
119  * those which must not be touched.
120  */
121 PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
122         int isenc)
123 {
124     int i, j, ch;
125     char *y;
126     char *allowed;      /* characters which should not be encoded */
127     char *reserved;     /* characters which much not be en/de-coded */
128
129 /* N.B. in addition to :@&=, this allows ';' in an http path
130  * and '?' in an ftp path -- this may be revised
131  * 
132  * Also, it makes a '+' character in a search string reserved, as
133  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
134  * it only permits ; / ? : @ = & as reserved chars.)
135  */
136     if (t == enc_path)
137         allowed = "$-_.+!*'(),;:@&=";
138     else if (t == enc_search)
139         allowed = "$-_.!*'(),;:@&=";
140     else if (t == enc_user)
141         allowed = "$-_.+!*'(),;@&=";
142     else if (t == enc_fpath)
143         allowed = "$-_.+!*'(),?:@&=";
144     else                        /* if (t == enc_parm) */
145         allowed = "$-_.+!*'(),?/:@&=";
146
147     if (t == enc_path)
148         reserved = "/";
149     else if (t == enc_search)
150         reserved = "+";
151     else
152         reserved = "";
153
154     y = apr_palloc(p, 3 * len + 1);
155
156     for (i = 0, j = 0; i < len; i++, j++) {
157 /* always handle '/' first */
158         ch = x[i];
159         if (strchr(reserved, ch)) {
160             y[j] = ch;
161             continue;
162         }
163 /* decode it if not already done */
164         if (isenc && (isenc != PROXYREQ_REVERSE) && (ch == '%')) {
165             if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
166                 return NULL;
167             ch = ap_proxy_hex2c(&x[i + 1]);
168             i += 2;
169             if (ch != 0 && strchr(reserved, ch)) {      /* keep it encoded */
170                 ap_proxy_c2hex(ch, &y[j]);
171                 j += 2;
172                 continue;
173             }
174         }
175 /* recode it, if necessary */
176         if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
177             ap_proxy_c2hex(ch, &y[j]);
178             j += 2;
179         }
180         else
181             y[j] = ch;
182     }
183     y[j] = '\0';
184     return y;
185 }
186
187 /*
188  * Parses network-location.
189  *    urlp           on input the URL; on output the path, after the leading /
190  *    user           NULL if no user/password permitted
191  *    password       holder for password
192  *    host           holder for host
193  *    port           port number; only set if one is supplied.
194  *
195  * Returns an error string.
196  */
197 PROXY_DECLARE(char *)
198      ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
199                         char **passwordp, char **hostp, apr_port_t *port)
200 {
201     char *addr, *scope_id, *strp, *host, *url = *urlp;
202     char *user = NULL, *password = NULL;
203     apr_port_t tmp_port;
204     apr_status_t rv;
205
206     if (url[0] != '/' || url[1] != '/')
207         return "Malformed URL";
208     host = url + 2;
209     url = strchr(host, '/');
210     if (url == NULL)
211         url = "";
212     else
213         *(url++) = '\0';        /* skip seperating '/' */
214
215     /* find _last_ '@' since it might occur in user/password part */
216     strp = strrchr(host, '@');
217
218     if (strp != NULL) {
219         *strp = '\0';
220         user = host;
221         host = strp + 1;
222
223 /* find password */
224         strp = strchr(user, ':');
225         if (strp != NULL) {
226             *strp = '\0';
227             password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1);
228             if (password == NULL)
229                 return "Bad %-escape in URL (password)";
230         }
231
232         user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1);
233         if (user == NULL)
234             return "Bad %-escape in URL (username)";
235     }
236     if (userp != NULL) {
237         *userp = user;
238     }
239     if (passwordp != NULL) {
240         *passwordp = password;
241     }
242
243     /* Parse the host string to separate host portion from optional port.
244      * Perform range checking on port.
245      */
246     rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
247     if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
248         return "Invalid host/port";
249     }
250     if (tmp_port != 0) { /* only update caller's port if port was specified */
251         *port = tmp_port;
252     }
253
254     ap_str_tolower(addr); /* DNS names are case-insensitive */
255
256     *urlp = url;
257     *hostp = addr;
258
259     return NULL;
260 }
261
262 /*
263  * If the date is a valid RFC 850 date or asctime() date, then it
264  * is converted to the RFC 1123 format.
265  */
266 PROXY_DECLARE(const char *)
267      ap_proxy_date_canon(apr_pool_t *p, const char *date)
268 {
269     apr_status_t rv;
270     char* ndate;
271
272     apr_time_t time = apr_date_parse_http(date);
273     if (!time) {
274         return date;
275     }
276
277     ndate = apr_palloc(p, APR_RFC822_DATE_LEN);
278     rv = apr_rfc822_date(ndate, time);
279     if (rv != APR_SUCCESS) {
280         return date;
281     }
282
283     return ndate;
284 }
285
286 PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
287 {
288     request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
289
290     rp->pool            = c->pool;
291     rp->status          = HTTP_OK;
292
293     rp->headers_in      = apr_table_make(c->pool, 50);
294     rp->subprocess_env  = apr_table_make(c->pool, 50);
295     rp->headers_out     = apr_table_make(c->pool, 12);
296     rp->err_headers_out = apr_table_make(c->pool, 5);
297     rp->notes           = apr_table_make(c->pool, 5);
298
299     rp->server = r->server;
300     rp->proxyreq = r->proxyreq;
301     rp->request_time = r->request_time;
302     rp->connection      = c;
303     rp->output_filters  = c->output_filters;
304     rp->input_filters   = c->input_filters;
305     rp->proto_output_filters  = c->output_filters;
306     rp->proto_input_filters   = c->input_filters;
307
308     rp->request_config  = ap_create_request_config(c->pool);
309     proxy_run_create_req(r, rp);
310
311     return rp;
312 }
313
314 /*
315  * Reads headers from a buffer and returns an array of headers.
316  * Returns NULL on file error
317  * This routine tries to deal with too long lines and continuation lines.
318  *
319  * Note: Currently the headers are passed through unmerged. This has to be
320  * done so that headers which react badly to merging (such as Set-Cookie
321  * headers, which contain commas within the date field) do not get stuffed
322  * up.
323  */
324 PROXY_DECLARE(apr_table_t *)ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
325 {
326     apr_table_t *headers_out;
327     int len;
328     char *value, *end;
329     char field[MAX_STRING_LEN];
330     int saw_headers = 0;
331     void *sconf = r->server->module_config;
332     proxy_server_conf *psc;
333
334     psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
335
336     headers_out = apr_table_make(r->pool, 20);
337
338     /*
339      * Read header lines until we get the empty separator line, a read error,
340      * the connection closes (EOF), or we timeout.
341      */
342     while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
343
344         if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
345
346             /* We may encounter invalid headers, usually from buggy
347              * MS IIS servers, so we need to determine just how to handle
348              * them. We can either ignore them, assume that they mark the
349              * start-of-body (eg: a missing CRLF) or (the default) mark
350              * the headers as totally bogus and return a 500. The sole
351              * exception is an extra "HTTP/1.0 200, OK" line sprinkled
352              * in between the usual MIME headers, which is a favorite
353              * IIS bug.
354              */
355              /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
356
357             if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
358                 if (psc->badopt == bad_error) {
359                     /* Nope, it wasn't even an extra HTTP header. Give up. */
360                     return NULL;
361                 }
362                 else if (psc->badopt == bad_body) {
363                     /* if we've already started loading headers_out, then
364                      * return what we've accumulated so far, in the hopes
365                      * that they are useful. Otherwise, we completely bail.
366                      */
367                     /* FIXME: We've already scarfed the supposed 1st line of
368                      * the body, so the actual content may end up being bogus
369                      * as well. If the content is HTML, we may be lucky.
370                      */
371                     if (saw_headers) {
372                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
373                          "proxy: Starting body due to bogus non-header in headers "
374                          "returned by %s (%s)", r->uri, r->method);
375                         return headers_out;
376                     } else {
377                          ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
378                          "proxy: No HTTP headers "
379                          "returned by %s (%s)", r->uri, r->method);
380                         return NULL;
381                     }
382                 }
383             }
384             /* this is the psc->badopt == bad_ignore case */
385             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
386                          "proxy: Ignoring bogus HTTP header "
387                          "returned by %s (%s)", r->uri, r->method);
388             continue;
389         }
390
391         *value = '\0';
392         ++value;
393         /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
394          * wrong... and so are many others probably.
395          */
396         while (apr_isspace(*value))
397             ++value;            /* Skip to start of value   */
398
399         /* should strip trailing whitespace as well */
400         for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end)
401             *end = '\0';
402
403         /* make sure we add so as not to destroy duplicated headers */
404         apr_table_add(headers_out, buffer, value);
405         saw_headers = 1;
406
407         /* the header was too long; at the least we should skip extra data */
408         if (len >= size - 1) { 
409             while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
410                     >= MAX_STRING_LEN - 1) {
411                 /* soak up the extra data */
412             }
413             if (len == 0) /* time to exit the larger loop as well */
414                 break;
415         }
416     }
417     return headers_out;
418 }
419
420
421 /*
422  * list is a comma-separated list of case-insensitive tokens, with
423  * optional whitespace around the tokens.
424  * The return returns 1 if the token val is found in the list, or 0
425  * otherwise.
426  */
427 PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
428 {
429     int len, i;
430     const char *p;
431
432     len = strlen(val);
433
434     while (list != NULL) {
435         p = ap_strchr_c(list, ',');
436         if (p != NULL) {
437             i = p - list;
438             do
439                 p++;
440             while (apr_isspace(*p));
441         }
442         else
443             i = strlen(list);
444
445         while (i > 0 && apr_isspace(list[i - 1]))
446             i--;
447         if (i == len && strncasecmp(list, val, len) == 0)
448             return 1;
449         list = p;
450     }
451     return 0;
452 }
453
454 /*
455  * list is a comma-separated list of case-insensitive tokens, with
456  * optional whitespace around the tokens.
457  * if val appears on the list of tokens, it is removed from the list,
458  * and the new list is returned.
459  */
460 PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
461 {
462     int len, i;
463     const char *p;
464     char *new = NULL;
465
466     len = strlen(val);
467
468     while (list != NULL) {
469         p = ap_strchr_c(list, ',');
470         if (p != NULL) {
471             i = p - list;
472             do
473                 p++;
474             while (apr_isspace(*p));
475         }
476         else
477             i = strlen(list);
478
479         while (i > 0 && apr_isspace(list[i - 1]))
480             i--;
481         if (i == len && strncasecmp(list, val, len) == 0) {
482             /* do nothing */
483         }
484         else {
485             if (new)
486                 new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
487             else
488                 new = apr_pstrndup(pool, list, i);
489         }
490         list = p;
491     }
492     return new;
493 }
494
495 /*
496  * Converts 8 hex digits to a time integer
497  */
498 PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
499 {
500     int i, ch;
501     unsigned int j;
502
503     for (i = 0, j = 0; i < 8; i++) {
504         ch = x[i];
505         j <<= 4;
506         if (apr_isdigit(ch))
507             j |= ch - '0';
508         else if (apr_isupper(ch))
509             j |= ch - ('A' - 10);
510         else
511             j |= ch - ('a' - 10);
512     }
513     if (j == 0xffffffff)
514         return -1;              /* so that it works with 8-byte ints */
515     else
516         return j;
517 }
518
519 /*
520  * Converts a time integer to 8 hex digits
521  */
522 PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
523 {
524     int i, ch;
525     unsigned int j = t;
526
527     for (i = 7; i >= 0; i--) {
528         ch = j & 0xF;
529         j >>= 4;
530         if (ch >= 10)
531             y[i] = ch + ('A' - 10);
532         else
533             y[i] = ch + '0';
534     }
535     y[8] = '\0';
536 }
537
538 PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
539 {
540     apr_table_setn(r->notes, "error-notes",
541         apr_pstrcat(r->pool, 
542                 "The proxy server could not handle the request "
543                 "<em><a href=\"", ap_escape_uri(r->pool, r->uri),
544                 "\">", ap_escape_html(r->pool, r->method),
545                 "&nbsp;", 
546                 ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
547                 "Reason: <strong>",
548                 ap_escape_html(r->pool, message), 
549                 "</strong></p>", NULL));
550
551     /* Allow "error-notes" string to be printed by ap_send_error_response() */
552     apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
553
554     r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
555     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
556                          "proxy: %s returned by %s", message, r->uri);
557     return statuscode;
558 }
559
560 static const char *
561      proxy_get_host_of_request(request_rec *r)
562 {
563     char *url, *user = NULL, *password = NULL, *err, *host;
564     apr_port_t port;
565
566     if (r->hostname != NULL)
567         return r->hostname;
568
569     /* Set url to the first char after "scheme://" */
570     if ((url = strchr(r->uri, ':')) == NULL
571         || url[1] != '/' || url[2] != '/')
572         return NULL;
573
574     url = apr_pstrdup(r->pool, &url[1]);        /* make it point to "//", which is what proxy_canon_netloc expects */
575
576     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
577
578     if (err != NULL)
579         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
580                      "%s", err);
581
582     r->hostname = host;
583
584     return host;                /* ought to return the port, too */
585 }
586
587 /* Return TRUE if addr represents an IP address (or an IP network address) */
588 PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
589 {
590     const char *addr = This->name;
591     long ip_addr[4];
592     int i, quads;
593     long bits;
594
595     /* if the address is given with an explicit netmask, use that */
596     /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */
597     /* "partial" addresses (with less than 4 quads) correctly, i.e.  */
598     /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
599     /* I therefore have to parse the IP address manually: */
600     /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
601     /* addr and mask were set by proxy_readmask() */
602     /*return 1; */
603
604     /* Parse IP addr manually, optionally allowing */
605     /* abbreviated net addresses like 192.168. */
606
607     /* Iterate over up to 4 (dotted) quads. */
608     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
609         char *tmp;
610
611         if (*addr == '/' && quads > 0)  /* netmask starts here. */
612             break;
613
614         if (!apr_isdigit(*addr))
615             return 0;           /* no digit at start of quad */
616
617         ip_addr[quads] = strtol(addr, &tmp, 0);
618
619         if (tmp == addr)        /* expected a digit, found something else */
620             return 0;
621
622         if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
623             /* invalid octet */
624             return 0;
625         }
626
627         addr = tmp;
628
629         if (*addr == '.' && quads != 3)
630             ++addr;             /* after the 4th quad, a dot would be illegal */
631     }
632
633     for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
634         This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
635
636     if (addr[0] == '/' && apr_isdigit(addr[1])) {       /* net mask follows: */
637         char *tmp;
638
639         ++addr;
640
641         bits = strtol(addr, &tmp, 0);
642
643         if (tmp == addr)        /* expected a digit, found something else */
644             return 0;
645
646         addr = tmp;
647
648         if (bits < 0 || bits > 32)      /* netmask must be between 0 and 32 */
649             return 0;
650
651     }
652     else {
653         /* Determine (i.e., "guess") netmask by counting the */
654         /* number of trailing .0's; reduce #quads appropriately */
655         /* (so that 192.168.0.0 is equivalent to 192.168.)        */
656         while (quads > 0 && ip_addr[quads - 1] == 0)
657             --quads;
658
659         /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
660         if (quads < 1)
661             return 0;
662
663         /* every zero-byte counts as 8 zero-bits */
664         bits = 8 * quads;
665
666         if (bits != 32)         /* no warning for fully qualified IP address */
667             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
668               "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
669                  inet_ntoa(This->addr), bits);
670     }
671
672     This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
673
674     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
675         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
676             "Warning: NetMask and IP-Addr disagree in %s/%ld",
677                 inet_ntoa(This->addr), bits);
678         This->addr.s_addr &= This->mask.s_addr;
679         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
680             "         Set to %s/%ld",
681                 inet_ntoa(This->addr), bits);
682     }
683
684     if (*addr == '\0') {
685         This->matcher = proxy_match_ipaddr;
686         return 1;
687     }
688     else
689         return (*addr == '\0'); /* okay iff we've parsed the whole string */
690 }
691
692 /* Return TRUE if addr represents an IP address (or an IP network address) */
693 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
694 {
695     int i, ip_addr[4];
696     struct in_addr addr, *ip;
697     const char *host = proxy_get_host_of_request(r);
698
699     if (host == NULL)   /* oops! */
700        return 0;
701
702     memset(&addr, '\0', sizeof addr);
703     memset(ip_addr, '\0', sizeof ip_addr);
704
705     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
706         for (addr.s_addr = 0, i = 0; i < 4; ++i)
707             addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
708
709         if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
710 #if DEBUGGING
711         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
712                          "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
713         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
714                          "%s/", inet_ntoa(This->addr));
715         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
716                          "%s", inet_ntoa(This->mask));
717 #endif
718             return 1;
719         }
720 #if DEBUGGING
721         else {
722         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
723                          "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
724         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
725                          "%s/", inet_ntoa(This->addr));
726         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
727                          "%s", inet_ntoa(This->mask));
728         }
729 #endif
730     }
731     else {
732         struct apr_sockaddr_t *reqaddr;
733
734         if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
735             != APR_SUCCESS) {
736 #if DEBUGGING
737             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
738                          "2)IP-NoMatch: hostname=%s msg=Host not found", 
739                          host);
740 #endif
741             return 0;
742         }
743
744         /* Try to deal with multiple IP addr's for a host */
745         /* FIXME: This needs to be able to deal with IPv6 */
746         while (reqaddr) {
747             ip = (struct in_addr *) reqaddr->ipaddr_ptr;
748             if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
749 #if DEBUGGING
750                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
751                              "3)IP-Match: %s[%s] <-> ", host, 
752                              inet_ntoa(*ip));
753                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
754                              "%s/", inet_ntoa(This->addr));
755                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
756                              "%s", inet_ntoa(This->mask));
757 #endif
758                 return 1;
759             }
760 #if DEBUGGING
761             else {
762                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
763                              "3)IP-NoMatch: %s[%s] <-> ", host, 
764                              inet_ntoa(*ip));
765                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
766                              "%s/", inet_ntoa(This->addr));
767                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
768                              "%s", inet_ntoa(This->mask));
769             }
770 #endif
771             reqaddr = reqaddr->next;
772         }
773     }
774
775     return 0;
776 }
777
778 /* Return TRUE if addr represents a domain name */
779 PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
780 {
781     char *addr = This->name;
782     int i;
783
784     /* Domain name must start with a '.' */
785     if (addr[0] != '.')
786         return 0;
787
788     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
789     for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
790         continue;
791
792 #if 0
793     if (addr[i] == ':') {
794     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
795                      "@@@@ handle optional port in proxy_is_domainname()");
796         /* @@@@ handle optional port */
797     }
798 #endif
799
800     if (addr[i] != '\0')
801         return 0;
802
803     /* Strip trailing dots */
804     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
805         addr[i] = '\0';
806
807     This->matcher = proxy_match_domainname;
808     return 1;
809 }
810
811 /* Return TRUE if host "host" is in domain "domain" */
812 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
813 {
814     const char *host = proxy_get_host_of_request(r);
815     int d_len = strlen(This->name), h_len;
816
817     if (host == NULL)           /* some error was logged already */
818         return 0;
819
820     h_len = strlen(host);
821
822     /* @@@ do this within the setup? */
823     /* Ignore trailing dots in domain comparison: */
824     while (d_len > 0 && This->name[d_len - 1] == '.')
825         --d_len;
826     while (h_len > 0 && host[h_len - 1] == '.')
827         --h_len;
828     return h_len > d_len
829         && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
830 }
831
832 /* Return TRUE if host represents a host name */
833 PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
834 {
835     struct apr_sockaddr_t *addr;
836     char *host = This->name;
837     int i;
838
839     /* Host names must not start with a '.' */
840     if (host[0] == '.')
841         return 0;
842
843     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
844     for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
845
846     if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)
847         return 0;
848     
849     This->hostaddr = addr;
850
851     /* Strip trailing dots */
852     for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)
853         host[i] = '\0';
854
855     This->matcher = proxy_match_hostname;
856     return 1;
857 }
858
859 /* Return TRUE if host "host" is equal to host2 "host2" */
860 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
861 {
862     char *host = This->name;
863     const char *host2 = proxy_get_host_of_request(r);
864     int h2_len;
865     int h1_len;
866
867     if (host == NULL || host2 == NULL)
868         return 0; /* oops! */
869
870     h2_len = strlen(host2);
871     h1_len = strlen(host);
872
873 #if 0
874     struct apr_sockaddr_t *addr = *This->hostaddr;
875
876     /* Try to deal with multiple IP addr's for a host */
877     while (addr) {
878         if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
879             return 1;
880         addr = addr->next;
881     }
882 #endif
883
884     /* Ignore trailing dots in host2 comparison: */
885     while (h2_len > 0 && host2[h2_len - 1] == '.')
886         --h2_len;
887     while (h1_len > 0 && host[h1_len - 1] == '.')
888         --h1_len;
889     return h1_len == h2_len
890         && strncasecmp(host, host2, h1_len) == 0;
891 }
892
893 /* Return TRUE if addr is to be matched as a word */
894 PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
895 {
896     This->matcher = proxy_match_word;
897     return 1;
898 }
899
900 /* Return TRUE if string "str2" occurs literally in "str1" */
901 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
902 {
903     const char *host = proxy_get_host_of_request(r);
904     return host != NULL && ap_strstr_c(host, This->name) != NULL;
905 }
906
907 /* checks whether a host in uri_addr matches proxyblock */
908 PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, 
909                              apr_sockaddr_t *uri_addr)
910 {
911     int j;
912     apr_sockaddr_t * src_uri_addr = uri_addr;
913     /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
914     for (j = 0; j < conf->noproxies->nelts; j++) {
915         struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
916         struct apr_sockaddr_t *conf_addr = npent[j].addr;
917         uri_addr = src_uri_addr;
918         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
919                      "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
920         if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
921             || npent[j].name[0] == '*') {
922             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
923                          "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
924             return HTTP_FORBIDDEN;
925         }
926         while (conf_addr) {
927             while (uri_addr) {
928                 char *conf_ip;
929                 char *uri_ip;
930                 apr_sockaddr_ip_get(&conf_ip, conf_addr);
931                 apr_sockaddr_ip_get(&uri_ip, uri_addr);
932                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
933                              "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
934                 if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
935                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
936                                  "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
937                     return HTTP_FORBIDDEN;
938                 }
939                 uri_addr = uri_addr->next;
940             }
941             conf_addr = conf_addr->next;
942         }
943     }
944     return OK;
945 }
946
947 /* set up the minimal filter set */
948 PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
949 {
950     ap_add_input_filter("HTTP_IN", NULL, r, c);
951     return OK;
952 }
953
954 /* converts a series of buckets into a string 
955  * XXX: BillS says this function performs essentially the same function as 
956  * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() 
957  * instead? I think ap_proxy_string_read() will not work properly on non ASCII
958  * (EBCDIC) machines either.
959  */
960 PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
961                                                  char *buff, apr_size_t bufflen, int *eos)
962 {
963     apr_bucket *e;
964     apr_status_t rv;
965     char *pos = buff;
966     char *response;
967     int found = 0;
968     apr_size_t len;
969
970     /* start with an empty string */
971     buff[0] = 0;
972     *eos = 0;
973
974     /* loop through each brigade */
975     while (!found) {
976         /* get brigade from network one line at a time */
977         if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, 
978                                                 AP_MODE_GETLINE,
979                                                 APR_BLOCK_READ,
980                                                 0))) {
981             return rv;
982         }
983         /* loop through each bucket */
984         while (!found) {
985             if (*eos || APR_BRIGADE_EMPTY(bb)) {
986                 /* The connection aborted or timed out */
987                 return APR_ECONNABORTED;
988             }
989             e = APR_BRIGADE_FIRST(bb);
990             if (APR_BUCKET_IS_EOS(e)) {
991                 *eos = 1;
992             }
993             else {
994                 if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {
995                     return rv;
996                 }
997                 /* is string LF terminated? 
998                  * XXX: This check can be made more efficient by simply checking 
999                  * if the last character in the 'response' buffer is an ASCII_LF.
1000                  * See ap_rgetline() for an example.
1001                  */
1002                 if (memchr(response, APR_ASCII_LF, len)) {
1003                     found = 1;
1004                 }
1005                 /* concat strings until buff is full - then throw the data away */
1006                 if (len > ((bufflen-1)-(pos-buff))) {
1007                     len = (bufflen-1)-(pos-buff);
1008                 }
1009                 if (len > 0) {
1010                     pos = apr_cpystrn(pos, response, len);
1011                 }
1012             }
1013             APR_BUCKET_REMOVE(e);
1014             apr_bucket_destroy(e);
1015         }
1016     }
1017
1018     return APR_SUCCESS;
1019 }
1020
1021 /* unmerge an element in the table */
1022 PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
1023 {
1024     apr_off_t offset = 0;
1025     apr_off_t count = 0;
1026     char *value = NULL;
1027
1028     /* get the value to unmerge */
1029     const char *initial = apr_table_get(t, key);
1030     if (!initial) {
1031         return;
1032     }
1033     value = apr_pstrdup(p, initial);
1034
1035     /* remove the value from the headers */
1036     apr_table_unset(t, key);
1037
1038     /* find each comma */
1039     while (value[count]) {
1040         if (value[count] == ',') {
1041             value[count] = 0;
1042             apr_table_add(t, key, value + offset);
1043             offset = count + 1;
1044         }
1045         count++;
1046     }
1047     apr_table_add(t, key, value + offset);
1048 }
1049
1050 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1051                                                const char *proxy_function,
1052                                                apr_sockaddr_t *backend_addr,
1053                                                const char *backend_name,
1054                                                proxy_server_conf *conf,
1055                                                server_rec *s,
1056                                                apr_pool_t *p)
1057 {
1058     apr_status_t rv;
1059     int connected = 0;
1060     int loglevel;
1061     
1062     while (backend_addr && !connected) {
1063         if ((rv = apr_socket_create(newsock, backend_addr->family,
1064                                     SOCK_STREAM, p)) != APR_SUCCESS) {
1065             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1066             ap_log_error(APLOG_MARK, loglevel, rv, s,
1067                          "proxy: %s: error creating fam %d socket for target %s",
1068                          proxy_function,
1069                          backend_addr->family,
1070                          backend_name);
1071             /* this could be an IPv6 address from the DNS but the
1072              * local machine won't give us an IPv6 socket; hopefully the
1073              * DNS returned an additional address to try
1074              */
1075             backend_addr = backend_addr->next;
1076             continue;
1077         }
1078
1079 #if !defined(TPF) && !defined(BEOS)
1080         if (conf->recv_buffer_size > 0 &&
1081             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1082                                      conf->recv_buffer_size))) {
1083             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1084                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1085                          "ProxyReceiveBufferSize, using default");
1086         }
1087 #endif
1088
1089         /* Set a timeout on the socket */
1090         if (conf->timeout_set == 1) {
1091             apr_socket_timeout_set(*newsock, conf->timeout);
1092         }
1093         else {
1094              apr_socket_timeout_set(*newsock, s->timeout);
1095         }
1096
1097         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1098                      "proxy: %s: fam %d socket created to connect to %s",
1099                      proxy_function, backend_addr->family, backend_name);
1100
1101         /* make the connection out of the socket */
1102         rv = apr_connect(*newsock, backend_addr);
1103
1104         /* if an error occurred, loop round and try again */
1105         if (rv != APR_SUCCESS) {
1106             apr_socket_close(*newsock);
1107             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1108             ap_log_error(APLOG_MARK, loglevel, rv, s,
1109                          "proxy: %s: attempt to connect to %pI (%s) failed",
1110                          proxy_function,
1111                          backend_addr,
1112                          backend_name);
1113             backend_addr = backend_addr->next;
1114             continue;
1115         }
1116         connected = 1;
1117     }
1118     return connected ? 0 : 1;
1119 }
1120