]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
Fix worker retries and limit default retry timeout to 10 minutes.
[apache] / modules / proxy / proxy_util.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* Utility routines for Apache proxy */
17 #include "mod_proxy.h"
18 #include "ap_mpm.h"
19 #include "apr_version.h"
20
21 #if APR_HAVE_UNISTD_H
22 #include <unistd.h>         /* for getpid() */
23 #endif
24
25 #if (APR_MAJOR_VERSION < 1)
26 #undef apr_socket_create
27 #define apr_socket_create apr_socket_create_ex
28 #endif
29
30 /* Global balancer counter */
31 static int lb_workers = 0;
32 static int lb_workers_limit = 0;
33
34 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
35 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
36 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
37 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
38
39 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, 
40                                    (request_rec *r, request_rec *pr), (r, pr),
41                                    OK, DECLINED)
42
43 /* already called in the knowledge that the characters are hex digits */
44 PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
45 {
46     int i, ch;
47
48 #if !APR_CHARSET_EBCDIC
49     ch = x[0];
50     if (apr_isdigit(ch))
51         i = ch - '0';
52     else if (apr_isupper(ch))
53         i = ch - ('A' - 10);
54     else
55         i = ch - ('a' - 10);
56     i <<= 4;
57
58     ch = x[1];
59     if (apr_isdigit(ch))
60         i += ch - '0';
61     else if (apr_isupper(ch))
62         i += ch - ('A' - 10);
63     else
64         i += ch - ('a' - 10);
65     return i;
66 #else /*APR_CHARSET_EBCDIC*/
67     /* we assume that the hex value refers to an ASCII character
68      * so convert to EBCDIC so that it makes sense locally;
69      *
70      * example:
71      *
72      * client specifies %20 in URL to refer to a space char;
73      * at this point we're called with EBCDIC "20"; after turning
74      * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
75      * represents an ASCII char and convert 0x20 to EBCDIC, yielding
76      * 0x40
77      */
78     char buf[1];
79
80     if (1 == sscanf(x, "%2x", &i)) {
81         buf[0] = i & 0xFF;
82         ap_xlate_proto_from_ascii(buf, 1);
83         return buf[0];
84     }
85     else {
86         return 0;
87     }
88 #endif /*APR_CHARSET_EBCDIC*/
89 }
90
91 PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
92 {
93 #if !APR_CHARSET_EBCDIC
94     int i;
95
96     x[0] = '%';
97     i = (ch & 0xF0) >> 4;
98     if (i >= 10)
99         x[1] = ('A' - 10) + i;
100     else
101         x[1] = '0' + i;
102
103     i = ch & 0x0F;
104     if (i >= 10)
105         x[2] = ('A' - 10) + i;
106     else
107         x[2] = '0' + i;
108 #else /*APR_CHARSET_EBCDIC*/
109     static const char ntoa[] = { "0123456789ABCDEF" };
110     char buf[1];
111
112     ch &= 0xFF;
113
114     buf[0] = ch;
115     ap_xlate_proto_to_ascii(buf, 1);
116
117     x[0] = '%';
118     x[1] = ntoa[(buf[0] >> 4) & 0x0F];
119     x[2] = ntoa[buf[0] & 0x0F];
120     x[3] = '\0';
121 #endif /*APR_CHARSET_EBCDIC*/
122 }
123
124 /*
125  * canonicalise a URL-encoded string
126  */
127
128 /*
129  * Convert a URL-encoded string to canonical form.
130  * It decodes characters which need not be encoded,
131  * and encodes those which must be encoded, and does not touch
132  * those which must not be touched.
133  */
134 PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
135         int isenc)
136 {
137     int i, j, ch;
138     char *y;
139     char *allowed;      /* characters which should not be encoded */
140     char *reserved;     /* characters which much not be en/de-coded */
141
142 /* N.B. in addition to :@&=, this allows ';' in an http path
143  * and '?' in an ftp path -- this may be revised
144  * 
145  * Also, it makes a '+' character in a search string reserved, as
146  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
147  * it only permits ; / ? : @ = & as reserved chars.)
148  */
149     if (t == enc_path)
150         allowed = "$-_.+!*'(),;:@&=";
151     else if (t == enc_search)
152         allowed = "$-_.!*'(),;:@&=";
153     else if (t == enc_user)
154         allowed = "$-_.+!*'(),;@&=";
155     else if (t == enc_fpath)
156         allowed = "$-_.+!*'(),?:@&=";
157     else                        /* if (t == enc_parm) */
158         allowed = "$-_.+!*'(),?/:@&=";
159
160     if (t == enc_path)
161         reserved = "/";
162     else if (t == enc_search)
163         reserved = "+";
164     else
165         reserved = "";
166
167     y = apr_palloc(p, 3 * len + 1);
168
169     for (i = 0, j = 0; i < len; i++, j++) {
170 /* always handle '/' first */
171         ch = x[i];
172         if (strchr(reserved, ch)) {
173             y[j] = ch;
174             continue;
175         }
176 /* decode it if not already done */
177         if (isenc && ch == '%') {
178             if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
179                 return NULL;
180             ch = ap_proxy_hex2c(&x[i + 1]);
181             i += 2;
182             if (ch != 0 && strchr(reserved, ch)) {      /* keep it encoded */
183                 ap_proxy_c2hex(ch, &y[j]);
184                 j += 2;
185                 continue;
186             }
187         }
188 /* recode it, if necessary */
189         if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
190             ap_proxy_c2hex(ch, &y[j]);
191             j += 2;
192         }
193         else
194             y[j] = ch;
195     }
196     y[j] = '\0';
197     return y;
198 }
199
200 /*
201  * Parses network-location.
202  *    urlp           on input the URL; on output the path, after the leading /
203  *    user           NULL if no user/password permitted
204  *    password       holder for password
205  *    host           holder for host
206  *    port           port number; only set if one is supplied.
207  *
208  * Returns an error string.
209  */
210 PROXY_DECLARE(char *)
211      ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
212                         char **passwordp, char **hostp, apr_port_t *port)
213 {
214     char *addr, *scope_id, *strp, *host, *url = *urlp;
215     char *user = NULL, *password = NULL;
216     apr_port_t tmp_port;
217     apr_status_t rv;
218
219     if (url[0] != '/' || url[1] != '/')
220         return "Malformed URL";
221     host = url + 2;
222     url = strchr(host, '/');
223     if (url == NULL)
224         url = "";
225     else
226         *(url++) = '\0';        /* skip seperating '/' */
227
228     /* find _last_ '@' since it might occur in user/password part */
229     strp = strrchr(host, '@');
230
231     if (strp != NULL) {
232         *strp = '\0';
233         user = host;
234         host = strp + 1;
235
236 /* find password */
237         strp = strchr(user, ':');
238         if (strp != NULL) {
239             *strp = '\0';
240             password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1);
241             if (password == NULL)
242                 return "Bad %-escape in URL (password)";
243         }
244
245         user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1);
246         if (user == NULL)
247             return "Bad %-escape in URL (username)";
248     }
249     if (userp != NULL) {
250         *userp = user;
251     }
252     if (passwordp != NULL) {
253         *passwordp = password;
254     }
255
256     /* Parse the host string to separate host portion from optional port.
257      * Perform range checking on port.
258      */
259     rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
260     if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
261         return "Invalid host/port";
262     }
263     if (tmp_port != 0) { /* only update caller's port if port was specified */
264         *port = tmp_port;
265     }
266
267     ap_str_tolower(addr); /* DNS names are case-insensitive */
268
269     *urlp = url;
270     *hostp = addr;
271
272     return NULL;
273 }
274
275 static const char * const lwday[7] =
276 {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
277
278 /*
279  * If the date is a valid RFC 850 date or asctime() date, then it
280  * is converted to the RFC 1123 format, otherwise it is not modified.
281  * This routine is not very fast at doing conversions, as it uses
282  * sscanf and sprintf. However, if the date is already correctly
283  * formatted, then it exits very quickly.
284  */
285 PROXY_DECLARE(const char *)
286      ap_proxy_date_canon(apr_pool_t *p, const char *x1)
287 {
288     char *x = apr_pstrdup(p, x1);
289     int wk, mday, year, hour, min, sec, mon;
290     char *q, month[4], zone[4], week[4];
291
292     q = strchr(x, ',');
293     /* check for RFC 850 date */
294     if (q != NULL && q - x > 3 && q[1] == ' ') {
295         *q = '\0';
296         for (wk = 0; wk < 7; wk++)
297             if (strcmp(x, lwday[wk]) == 0)
298                 break;
299         *q = ',';
300         if (wk == 7)
301             return x;           /* not a valid date */
302         if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
303             q[17] != ':' || strcmp(&q[20], " GMT") != 0)
304             return x;
305         if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
306                    &hour, &min, &sec, zone) != 7)
307             return x;
308         if (year < 70)
309             year += 2000;
310         else
311             year += 1900;
312     }
313     else {
314 /* check for acstime() date */
315         if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
316             x[16] != ':' || x[19] != ' ' || x[24] != '\0')
317             return x;
318         if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
319                    &min, &sec, &year) != 7)
320             return x;
321         for (wk = 0; wk < 7; wk++)
322             if (strcmp(week, apr_day_snames[wk]) == 0)
323                 break;
324         if (wk == 7)
325             return x;
326     }
327
328 /* check date */
329     for (mon = 0; mon < 12; mon++)
330         if (strcmp(month, apr_month_snames[mon]) == 0)
331             break;
332     if (mon == 12)
333         return x;
334
335     q = apr_palloc(p, 30);
336     apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk],
337        mday, apr_month_snames[mon], year, hour, min, sec);
338     return q;
339 }
340
341 PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
342 {
343     request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
344
345     rp->pool            = c->pool;
346     rp->status          = HTTP_OK;
347
348     rp->headers_in      = apr_table_make(c->pool, 50);
349     rp->subprocess_env  = apr_table_make(c->pool, 50);
350     rp->headers_out     = apr_table_make(c->pool, 12);
351     rp->err_headers_out = apr_table_make(c->pool, 5);
352     rp->notes           = apr_table_make(c->pool, 5);
353
354     rp->server = r->server;
355     rp->proxyreq = r->proxyreq;
356     rp->request_time = r->request_time;
357     rp->connection      = c;
358     rp->output_filters  = c->output_filters;
359     rp->input_filters   = c->input_filters;
360     rp->proto_output_filters  = c->output_filters;
361     rp->proto_input_filters   = c->input_filters;
362
363     rp->request_config  = ap_create_request_config(c->pool);
364     proxy_run_create_req(r, rp);
365
366     return rp;
367 }
368
369
370 /*
371  * list is a comma-separated list of case-insensitive tokens, with
372  * optional whitespace around the tokens.
373  * The return returns 1 if the token val is found in the list, or 0
374  * otherwise.
375  */
376 PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
377 {
378     int len, i;
379     const char *p;
380
381     len = strlen(val);
382
383     while (list != NULL) {
384         p = ap_strchr_c(list, ',');
385         if (p != NULL) {
386             i = p - list;
387             do
388                 p++;
389             while (apr_isspace(*p));
390         }
391         else
392             i = strlen(list);
393
394         while (i > 0 && apr_isspace(list[i - 1]))
395             i--;
396         if (i == len && strncasecmp(list, val, len) == 0)
397             return 1;
398         list = p;
399     }
400     return 0;
401 }
402
403 /*
404  * list is a comma-separated list of case-insensitive tokens, with
405  * optional whitespace around the tokens.
406  * if val appears on the list of tokens, it is removed from the list,
407  * and the new list is returned.
408  */
409 PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
410 {
411     int len, i;
412     const char *p;
413     char *new = NULL;
414
415     len = strlen(val);
416
417     while (list != NULL) {
418         p = ap_strchr_c(list, ',');
419         if (p != NULL) {
420             i = p - list;
421             do
422                 p++;
423             while (apr_isspace(*p));
424         }
425         else
426             i = strlen(list);
427
428         while (i > 0 && apr_isspace(list[i - 1]))
429             i--;
430         if (i == len && strncasecmp(list, val, len) == 0) {
431             /* do nothing */
432         }
433         else {
434             if (new)
435                 new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
436             else
437                 new = apr_pstrndup(pool, list, i);
438         }
439         list = p;
440     }
441     return new;
442 }
443
444 /*
445  * Converts 8 hex digits to a time integer
446  */
447 PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
448 {
449     int i, ch;
450     unsigned int j;
451
452     for (i = 0, j = 0; i < 8; i++) {
453         ch = x[i];
454         j <<= 4;
455         if (apr_isdigit(ch))
456             j |= ch - '0';
457         else if (apr_isupper(ch))
458             j |= ch - ('A' - 10);
459         else
460             j |= ch - ('a' - 10);
461     }
462     if (j == 0xffffffff)
463         return -1;              /* so that it works with 8-byte ints */
464     else
465         return j;
466 }
467
468 /*
469  * Converts a time integer to 8 hex digits
470  */
471 PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
472 {
473     int i, ch;
474     unsigned int j = t;
475
476     for (i = 7; i >= 0; i--) {
477         ch = j & 0xF;
478         j >>= 4;
479         if (ch >= 10)
480             y[i] = ch + ('A' - 10);
481         else
482             y[i] = ch + '0';
483     }
484     y[8] = '\0';
485 }
486
487 PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
488 {
489     apr_table_setn(r->notes, "error-notes",
490         apr_pstrcat(r->pool, 
491                 "The proxy server could not handle the request "
492                 "<em><a href=\"", ap_escape_uri(r->pool, r->uri),
493                 "\">", ap_escape_html(r->pool, r->method),
494                 "&nbsp;", 
495                 ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
496                 "Reason: <strong>",
497                 ap_escape_html(r->pool, message), 
498                 "</strong></p>", NULL));
499
500     /* Allow "error-notes" string to be printed by ap_send_error_response() */
501     apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
502
503     r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
504     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
505                          "proxy: %s returned by %s", message, r->uri);
506     return statuscode;
507 }
508
509 static const char *
510      proxy_get_host_of_request(request_rec *r)
511 {
512     char *url, *user = NULL, *password = NULL, *err, *host;
513     apr_port_t port;
514
515     if (r->hostname != NULL)
516         return r->hostname;
517
518     /* Set url to the first char after "scheme://" */
519     if ((url = strchr(r->uri, ':')) == NULL
520         || url[1] != '/' || url[2] != '/')
521         return NULL;
522
523     url = apr_pstrdup(r->pool, &url[1]);        /* make it point to "//", which is what proxy_canon_netloc expects */
524
525     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
526
527     if (err != NULL)
528         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
529                      "%s", err);
530
531     r->hostname = host;
532
533     return host;                /* ought to return the port, too */
534 }
535
536 /* Return TRUE if addr represents an IP address (or an IP network address) */
537 PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
538 {
539     const char *addr = This->name;
540     long ip_addr[4];
541     int i, quads;
542     long bits;
543
544     /* if the address is given with an explicit netmask, use that */
545     /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */
546     /* "partial" addresses (with less than 4 quads) correctly, i.e.  */
547     /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
548     /* I therefore have to parse the IP address manually: */
549     /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
550     /* addr and mask were set by proxy_readmask() */
551     /*return 1; */
552
553     /* Parse IP addr manually, optionally allowing */
554     /* abbreviated net addresses like 192.168. */
555
556     /* Iterate over up to 4 (dotted) quads. */
557     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
558         char *tmp;
559
560         if (*addr == '/' && quads > 0)  /* netmask starts here. */
561             break;
562
563         if (!apr_isdigit(*addr))
564             return 0;           /* no digit at start of quad */
565
566         ip_addr[quads] = strtol(addr, &tmp, 0);
567
568         if (tmp == addr)        /* expected a digit, found something else */
569             return 0;
570
571         if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
572             /* invalid octet */
573             return 0;
574         }
575
576         addr = tmp;
577
578         if (*addr == '.' && quads != 3)
579             ++addr;             /* after the 4th quad, a dot would be illegal */
580     }
581
582     for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
583         This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
584
585     if (addr[0] == '/' && apr_isdigit(addr[1])) {       /* net mask follows: */
586         char *tmp;
587
588         ++addr;
589
590         bits = strtol(addr, &tmp, 0);
591
592         if (tmp == addr)        /* expected a digit, found something else */
593             return 0;
594
595         addr = tmp;
596
597         if (bits < 0 || bits > 32)      /* netmask must be between 0 and 32 */
598             return 0;
599
600     }
601     else {
602         /* Determine (i.e., "guess") netmask by counting the */
603         /* number of trailing .0's; reduce #quads appropriately */
604         /* (so that 192.168.0.0 is equivalent to 192.168.)        */
605         while (quads > 0 && ip_addr[quads - 1] == 0)
606             --quads;
607
608         /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
609         if (quads < 1)
610             return 0;
611
612         /* every zero-byte counts as 8 zero-bits */
613         bits = 8 * quads;
614
615         if (bits != 32)         /* no warning for fully qualified IP address */
616             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
617               "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
618                  inet_ntoa(This->addr), bits);
619     }
620
621     This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
622
623     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
624         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
625             "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
626                 inet_ntoa(This->addr), bits);
627         This->addr.s_addr &= This->mask.s_addr;
628         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
629             "         Set to %s/%ld\n",
630                 inet_ntoa(This->addr), bits);
631     }
632
633     if (*addr == '\0') {
634         This->matcher = proxy_match_ipaddr;
635         return 1;
636     }
637     else
638         return (*addr == '\0'); /* okay iff we've parsed the whole string */
639 }
640
641 /* Return TRUE if addr represents an IP address (or an IP network address) */
642 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
643 {
644     int i, ip_addr[4];
645     struct in_addr addr, *ip;
646     const char *host = proxy_get_host_of_request(r);
647
648     if (host == NULL)   /* oops! */
649        return 0;
650
651     memset(&addr, '\0', sizeof addr);
652     memset(ip_addr, '\0', sizeof ip_addr);
653
654     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
655         for (addr.s_addr = 0, i = 0; i < 4; ++i)
656             addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
657
658         if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
659 #if DEBUGGING
660         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
661                          "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
662         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
663                          "%s/", inet_ntoa(This->addr));
664         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
665                          "%s", inet_ntoa(This->mask));
666 #endif
667             return 1;
668         }
669 #if DEBUGGING
670         else {
671         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
672                          "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
673         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
674                          "%s/", inet_ntoa(This->addr));
675         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
676                          "%s", inet_ntoa(This->mask));
677         }
678 #endif
679     }
680     else {
681         struct apr_sockaddr_t *reqaddr;
682
683         if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
684             != APR_SUCCESS) {
685 #if DEBUGGING
686             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
687                          "2)IP-NoMatch: hostname=%s msg=Host not found", 
688                          host);
689 #endif
690             return 0;
691         }
692
693         /* Try to deal with multiple IP addr's for a host */
694         /* FIXME: This needs to be able to deal with IPv6 */
695         while (reqaddr) {
696             ip = (struct in_addr *) reqaddr->ipaddr_ptr;
697             if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
698 #if DEBUGGING
699                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
700                              "3)IP-Match: %s[%s] <-> ", host, 
701                              inet_ntoa(*ip));
702                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
703                              "%s/", inet_ntoa(This->addr));
704                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
705                              "%s", inet_ntoa(This->mask));
706 #endif
707                 return 1;
708             }
709 #if DEBUGGING
710             else {
711                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
712                              "3)IP-NoMatch: %s[%s] <-> ", host, 
713                              inet_ntoa(*ip));
714                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
715                              "%s/", inet_ntoa(This->addr));
716                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
717                              "%s", inet_ntoa(This->mask));
718             }
719 #endif
720             reqaddr = reqaddr->next;
721         }
722     }
723
724     return 0;
725 }
726
727 /* Return TRUE if addr represents a domain name */
728 PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
729 {
730     char *addr = This->name;
731     int i;
732
733     /* Domain name must start with a '.' */
734     if (addr[0] != '.')
735         return 0;
736
737     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
738     for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
739         continue;
740
741 #if 0
742     if (addr[i] == ':') {
743     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
744                      "@@@@ handle optional port in proxy_is_domainname()");
745         /* @@@@ handle optional port */
746     }
747 #endif
748
749     if (addr[i] != '\0')
750         return 0;
751
752     /* Strip trailing dots */
753     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
754         addr[i] = '\0';
755
756     This->matcher = proxy_match_domainname;
757     return 1;
758 }
759
760 /* Return TRUE if host "host" is in domain "domain" */
761 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
762 {
763     const char *host = proxy_get_host_of_request(r);
764     int d_len = strlen(This->name), h_len;
765
766     if (host == NULL)           /* some error was logged already */
767         return 0;
768
769     h_len = strlen(host);
770
771     /* @@@ do this within the setup? */
772     /* Ignore trailing dots in domain comparison: */
773     while (d_len > 0 && This->name[d_len - 1] == '.')
774         --d_len;
775     while (h_len > 0 && host[h_len - 1] == '.')
776         --h_len;
777     return h_len > d_len
778         && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
779 }
780
781 /* Return TRUE if host represents a host name */
782 PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
783 {
784     struct apr_sockaddr_t *addr;
785     char *host = This->name;
786     int i;
787
788     /* Host names must not start with a '.' */
789     if (host[0] == '.')
790         return 0;
791
792     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
793     for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
794
795     if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)
796         return 0;
797     
798     This->hostaddr = addr;
799
800     /* Strip trailing dots */
801     for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)
802         host[i] = '\0';
803
804     This->matcher = proxy_match_hostname;
805     return 1;
806 }
807
808 /* Return TRUE if host "host" is equal to host2 "host2" */
809 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
810 {
811     char *host = This->name;
812     const char *host2 = proxy_get_host_of_request(r);
813     int h2_len;
814     int h1_len;
815
816     if (host == NULL || host2 == NULL)
817         return 0; /* oops! */
818
819     h2_len = strlen(host2);
820     h1_len = strlen(host);
821
822 #if 0
823     struct apr_sockaddr_t *addr = *This->hostaddr;
824
825     /* Try to deal with multiple IP addr's for a host */
826     while (addr) {
827         if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
828             return 1;
829         addr = addr->next;
830     }
831 #endif
832
833     /* Ignore trailing dots in host2 comparison: */
834     while (h2_len > 0 && host2[h2_len - 1] == '.')
835         --h2_len;
836     while (h1_len > 0 && host[h1_len - 1] == '.')
837         --h1_len;
838     return h1_len == h2_len
839         && strncasecmp(host, host2, h1_len) == 0;
840 }
841
842 /* Return TRUE if addr is to be matched as a word */
843 PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
844 {
845     This->matcher = proxy_match_word;
846     return 1;
847 }
848
849 /* Return TRUE if string "str2" occurs literally in "str1" */
850 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
851 {
852     const char *host = proxy_get_host_of_request(r);
853     return host != NULL && ap_strstr_c(host, This->name) != NULL;
854 }
855
856 /* checks whether a host in uri_addr matches proxyblock */
857 PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, 
858                              apr_sockaddr_t *uri_addr)
859 {
860     int j;
861     apr_sockaddr_t * src_uri_addr = uri_addr;
862     /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
863     for (j = 0; j < conf->noproxies->nelts; j++) {
864         struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
865         struct apr_sockaddr_t *conf_addr = npent[j].addr;
866         uri_addr = src_uri_addr;
867         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
868                      "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
869         if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
870             || npent[j].name[0] == '*') {
871             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
872                          "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
873             return HTTP_FORBIDDEN;
874         }
875         while (conf_addr) {
876             while (uri_addr) {
877                 char *conf_ip;
878                 char *uri_ip;
879                 apr_sockaddr_ip_get(&conf_ip, conf_addr);
880                 apr_sockaddr_ip_get(&uri_ip, uri_addr);
881                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
882                              "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
883                 if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
884                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
885                                  "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
886                     return HTTP_FORBIDDEN;
887                 }
888                 uri_addr = uri_addr->next;
889             }
890             conf_addr = conf_addr->next;
891         }
892     }
893     return OK;
894 }
895
896 /* set up the minimal filter set */
897 PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
898 {
899     ap_add_input_filter("HTTP_IN", NULL, r, c);
900     return OK;
901 }
902
903 /* converts a series of buckets into a string 
904  * XXX: BillS says this function performs essentially the same function as 
905  * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() 
906  * instead? I think ap_proxy_string_read() will not work properly on non ASCII
907  * (EBCDIC) machines either.
908  */
909 PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
910                                                  char *buff, apr_size_t bufflen, int *eos)
911 {
912     apr_bucket *e;
913     apr_status_t rv;
914     char *pos = buff;
915     char *response;
916     int found = 0;
917     apr_size_t len;
918
919     /* start with an empty string */
920     buff[0] = 0;
921     *eos = 0;
922
923     /* loop through each brigade */
924     while (!found) {
925         /* get brigade from network one line at a time */
926         if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, 
927                                                 AP_MODE_GETLINE,
928                                                 APR_BLOCK_READ,
929                                                 0))) {
930             return rv;
931         }
932         /* loop through each bucket */
933         while (!found) {
934             if (*eos || APR_BRIGADE_EMPTY(bb)) {
935                 /* The connection aborted or timed out */
936                 return APR_ECONNABORTED;
937             }
938             e = APR_BRIGADE_FIRST(bb);
939             if (APR_BUCKET_IS_EOS(e)) {
940                 *eos = 1;
941             }
942             else {
943                 if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {
944                     return rv;
945                 }
946                 /* is string LF terminated? 
947                  * XXX: This check can be made more efficient by simply checking 
948                  * if the last character in the 'response' buffer is an ASCII_LF.
949                  * See ap_rgetline() for an example.
950                  */
951                 if (memchr(response, APR_ASCII_LF, len)) {
952                     found = 1;
953                 }
954                 /* concat strings until buff is full - then throw the data away */
955                 if (len > ((bufflen-1)-(pos-buff))) {
956                     len = (bufflen-1)-(pos-buff);
957                 }
958                 if (len > 0) {
959                     pos = apr_cpystrn(pos, response, len);
960                 }
961             }
962             APR_BUCKET_REMOVE(e);
963             apr_bucket_destroy(e);
964         }
965     }
966
967     return APR_SUCCESS;
968 }
969
970 /* unmerge an element in the table */
971 PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
972 {
973     apr_off_t offset = 0;
974     apr_off_t count = 0;
975     char *value = NULL;
976
977     /* get the value to unmerge */
978     const char *initial = apr_table_get(t, key);
979     if (!initial) {
980         return;
981     }
982     value = apr_pstrdup(p, initial);
983
984     /* remove the value from the headers */
985     apr_table_unset(t, key);
986
987     /* find each comma */
988     while (value[count]) {
989         if (value[count] == ',') {
990             value[count] = 0;
991             apr_table_add(t, key, value + offset);
992             offset = count + 1;
993         }
994         count++;
995     }
996     apr_table_add(t, key, value + offset);
997 }
998
999 PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
1000                                                       proxy_server_conf *conf,
1001                                                       const char *url)
1002 {
1003     proxy_balancer *balancer;
1004     char *c, *uri = apr_pstrdup(p, url);
1005     int i;
1006     
1007     c = strchr(uri, ':');   
1008     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1009        return NULL;
1010     /* remove path from uri */
1011     if ((c = strchr(c + 3, '/')))
1012         *c = '\0';
1013     balancer = (proxy_balancer *)conf->balancers->elts;
1014     for (i = 0; i < conf->balancers->nelts; i++) {
1015         if (strcasecmp(balancer->name, uri) == 0)
1016             return balancer;
1017         balancer++;
1018     }
1019     return NULL;
1020 }
1021
1022 PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
1023                                                   apr_pool_t *p,
1024                                                   proxy_server_conf *conf,
1025                                                   const char *url)
1026 {
1027     char *c, *q, *uri = apr_pstrdup(p, url);
1028
1029     c = strchr(uri, ':');   
1030     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1031        return "Bad syntax for a balancer name";
1032     /* remove path from uri */
1033     if ((q = strchr(c + 3, '/')))
1034         *q = '\0';
1035
1036     ap_str_tolower(uri);
1037     *balancer = apr_array_push(conf->balancers);
1038     memset(*balancer, 0, sizeof(proxy_balancer));
1039
1040     (*balancer)->name = uri;
1041     (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_runtime_worker));
1042     /* XXX Is this a right place to create mutex */
1043 #if APR_HAS_THREADS
1044     if (apr_thread_mutex_create(&((*balancer)->mutex),
1045                 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1046         /* XXX: Do we need to log something here */
1047         return "can not create thread mutex";
1048     }
1049 #endif
1050
1051     return NULL;
1052 }
1053
1054 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1055                                                   proxy_server_conf *conf,
1056                                                   const char *url)
1057 {
1058     proxy_worker *worker;
1059     char *c, *uri = apr_pstrdup(p, url);
1060     int i;
1061     
1062     c = strchr(uri, ':');   
1063     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1064        return NULL;
1065     /* remove path from uri */
1066     if ((c = strchr(c + 3, '/')))
1067         *c = '\0';
1068
1069     worker = (proxy_worker *)conf->workers->elts;
1070     for (i = 0; i < conf->workers->nelts; i++) {
1071         if (strcasecmp(worker->name, uri) == 0) {
1072             return worker;
1073         }
1074         worker++;
1075     }
1076     return NULL;
1077 }
1078
1079 #if APR_HAS_THREADS
1080 static apr_status_t conn_pool_cleanup(void *theworker)
1081 {
1082     proxy_worker *worker = (proxy_worker *)theworker;
1083     if (worker->cp->res) {
1084         worker->cp->pool = NULL;
1085         apr_reslist_destroy(worker->cp->res);
1086     }
1087     return APR_SUCCESS;
1088 }
1089 #endif
1090
1091 static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1092 {
1093     apr_pool_t *pool;
1094     proxy_conn_pool *cp;
1095     
1096     /* Create a connection pool's subpool. 
1097      * This pool is used for connection recycling.
1098      * Once the worker is added it is never removed but
1099      * it can be disabled.
1100      */
1101     apr_pool_create(&pool, p);
1102     /* Alloc from the same pool as worker.
1103      * proxy_conn_pool is permanently attached to the worker. 
1104      */
1105     cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1106     cp->pool = pool;    
1107     worker->cp = cp;
1108 }
1109
1110 PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
1111                                                 apr_pool_t *p,
1112                                                 proxy_server_conf *conf,
1113                                                 const char *url)
1114 {
1115     char *c, *q, *uri = apr_pstrdup(p, url);
1116     int port;
1117     
1118     c = strchr(uri, ':');   
1119     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1120        return "Bad syntax for a remote proxy server";
1121     /* remove path from uri */
1122     if ((q = strchr(c + 3, '/')))
1123         *q = '\0';
1124
1125     q = strchr(c + 3, ':');
1126     if (q != NULL) {
1127         if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
1128             return "Bad syntax for a remote proxy server (bad port number)";
1129         }
1130     }
1131     else
1132         port = -1;
1133     ap_str_tolower(uri);
1134     *worker = apr_array_push(conf->workers);
1135     memset(*worker, 0, sizeof(proxy_worker));
1136     (*worker)->name = apr_pstrdup(p, uri);
1137     *c = '\0';
1138     (*worker)->scheme = uri;
1139     (*worker)->hostname = c + 3;
1140
1141     if (port == -1)
1142         port = apr_uri_port_of_scheme((*worker)->scheme);
1143     (*worker)->port = port;
1144
1145     init_conn_pool(p, *worker);
1146
1147     return NULL;
1148 }
1149
1150 PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
1151 {
1152
1153     proxy_worker *worker;
1154     worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
1155     init_conn_pool(p, worker);
1156
1157     return worker;
1158 }
1159
1160 PROXY_DECLARE(void) 
1161 ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer, proxy_worker *worker)
1162 {
1163     proxy_runtime_worker *runtime;
1164
1165 #if PROXY_HAS_SCOREBOARD
1166     int mpm_daemons;
1167
1168     ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &mpm_daemons);
1169     /* Check if we are prefork or single child */
1170     if (worker->hmax && mpm_daemons > 1) {
1171         /* Check only if workers_limit is set */
1172         if (lb_workers_limit && (lb_workers + 1) > lb_workers_limit) {
1173             ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
1174                           "proxy: Can not add worker (%s) to balancer (%s)."
1175                           " Dynamic limit reached.",
1176                           worker->name, balancer->name);
1177             return;
1178         }
1179     }
1180 #endif
1181     runtime = apr_array_push(balancer->workers);
1182     runtime->w  = worker;
1183     runtime->b  = balancer;
1184     runtime->id = lb_workers;
1185     runtime->s  = NULL;
1186     /* Increase the total runtime count */
1187     ++lb_workers;
1188
1189 }
1190
1191 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
1192                                         proxy_balancer **balancer,
1193                                         request_rec *r,
1194                                         proxy_server_conf *conf, char **url)
1195 {
1196     int access_status;
1197     
1198     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
1199     if (access_status == DECLINED && *balancer == NULL) {
1200         *worker = ap_proxy_get_worker(r->pool, conf, *url);
1201         if (*worker) {
1202             *balancer = NULL;
1203             access_status = OK;
1204         }
1205         else if (r->proxyreq == PROXYREQ_PROXY) {
1206             if (conf->forward) {
1207                 *balancer = NULL;
1208                 *worker = conf->forward;
1209                 access_status = OK;
1210             }
1211         }
1212     }
1213     else if (access_status == DECLINED && balancer != NULL) {
1214         /* All the workers are busy */
1215         access_status = HTTP_SERVICE_UNAVAILABLE;
1216     }
1217     return access_status;
1218 }
1219
1220 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
1221                                          proxy_balancer *balancer,
1222                                          request_rec *r,
1223                                          proxy_server_conf *conf)
1224 {
1225     int access_status;
1226     if (balancer)
1227         access_status = proxy_run_post_request(worker, balancer, r, conf);
1228     else { 
1229         
1230
1231         access_status = OK;
1232     }
1233
1234     return access_status;
1235 }
1236
1237 /* DEPRECATED */
1238 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1239                                                const char *proxy_function,
1240                                                apr_sockaddr_t *backend_addr,
1241                                                const char *backend_name,
1242                                                proxy_server_conf *conf,
1243                                                server_rec *s,
1244                                                apr_pool_t *p)
1245 {
1246     apr_status_t rv;
1247     int connected = 0;
1248     int loglevel;
1249     
1250     while (backend_addr && !connected) {
1251         if ((rv = apr_socket_create(newsock, backend_addr->family,
1252                                     SOCK_STREAM, 0, p)) != APR_SUCCESS) {
1253             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1254             ap_log_error(APLOG_MARK, loglevel, rv, s,
1255                          "proxy: %s: error creating fam %d socket for target %s",
1256                          proxy_function,
1257                          backend_addr->family,
1258                          backend_name);
1259             /* this could be an IPv6 address from the DNS but the
1260              * local machine won't give us an IPv6 socket; hopefully the
1261              * DNS returned an additional address to try
1262              */
1263             backend_addr = backend_addr->next;
1264             continue;
1265         }
1266
1267 #if !defined(TPF) && !defined(BEOS)
1268         if (conf->recv_buffer_size > 0 &&
1269             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1270                                      conf->recv_buffer_size))) {
1271             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1272                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1273                          "ProxyReceiveBufferSize, using default");
1274         }
1275 #endif
1276
1277         /* Set a timeout on the socket */
1278         if (conf->timeout_set == 1) {
1279             apr_socket_timeout_set(*newsock, conf->timeout);
1280         }
1281         else {
1282              apr_socket_timeout_set(*newsock, s->timeout);
1283         }
1284
1285         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1286                      "proxy: %s: fam %d socket created to connect to %s",
1287                      proxy_function, backend_addr->family, backend_name);
1288
1289         /* make the connection out of the socket */
1290         rv = apr_socket_connect(*newsock, backend_addr);
1291
1292         /* if an error occurred, loop round and try again */
1293         if (rv != APR_SUCCESS) {
1294             apr_socket_close(*newsock);
1295             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1296             ap_log_error(APLOG_MARK, loglevel, rv, s,
1297                          "proxy: %s: attempt to connect to %pI (%s) failed",
1298                          proxy_function,
1299                          backend_addr,
1300                          backend_name);
1301             backend_addr = backend_addr->next;
1302             continue;
1303         }
1304         connected = 1;
1305     }
1306     return connected ? 0 : 1;
1307 }
1308
1309 static apr_status_t connection_cleanup(void *theconn)
1310 {
1311     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1312     proxy_worker *worker = conn->worker;
1313     
1314     /* If the connection pool is NULL the worker
1315      * cleanup has been run. Just return.
1316      */
1317     if (!worker->cp)
1318         return APR_SUCCESS;
1319
1320     /* deterimine if the connection need to be closed */
1321     if (conn->close_on_recycle || conn->close) {
1322         apr_pool_t *p = conn->pool;
1323         apr_pool_clear(conn->pool);
1324         memset(conn, 0, sizeof(proxy_conn_rec));
1325         conn->pool = p;
1326         conn->worker = worker;
1327     }
1328 #if APR_HAS_THREADS
1329     if (worker->hmax && worker->cp->res) {
1330         apr_reslist_release(worker->cp->res, (void *)conn);
1331     }
1332     else
1333 #endif
1334     {
1335         worker->cp->conn = conn;
1336     }
1337
1338     /* Allways return the SUCCESS */
1339     return APR_SUCCESS;
1340 }
1341
1342 /* reslist constructor */
1343 static apr_status_t connection_constructor(void **resource, void *params,
1344                                            apr_pool_t *pool)
1345 {
1346     apr_pool_t *ctx;
1347     proxy_conn_rec *conn;
1348     proxy_worker *worker = (proxy_worker *)params;
1349     
1350     /* Create the subpool for each connection
1351      * This keeps the memory consumption constant
1352      * when disconnecting from backend.
1353      */
1354     apr_pool_create(&ctx, pool);
1355     conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));
1356
1357     conn->pool   = ctx;
1358     conn->worker = worker;
1359     *resource = conn;
1360
1361     return APR_SUCCESS;
1362 }
1363
1364 /* reslist destructor */
1365 static apr_status_t connection_destructor(void *resource, void *params,
1366                                           apr_pool_t *pool)
1367 {
1368     proxy_conn_rec *conn = (proxy_conn_rec *)resource;
1369
1370     /* Destroy the pool only if not called from reslist_destroy */    
1371     if (conn->worker->cp->pool)
1372         apr_pool_destroy(conn->pool);
1373
1374     return APR_SUCCESS;
1375 }
1376
1377 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
1378 {
1379     apr_status_t rv;
1380
1381 #if APR_HAS_THREADS
1382     int mpm_threads;
1383
1384     ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1385     if (mpm_threads > 1) {
1386         /* Set hard max to no more then mpm_threads */
1387         if (worker->hmax == 0 || worker->hmax > mpm_threads)
1388             worker->hmax = mpm_threads;
1389         if (worker->smax == 0 || worker->smax > worker->hmax)
1390             worker->smax = worker->hmax;
1391         /* Set min to be lower then smax */
1392         if (worker->min > worker->smax)
1393             worker->min = worker->smax; 
1394         worker->cp->nfree = worker->hmax;
1395     }
1396     else {
1397         /* This will supress the apr_reslist creation */
1398         worker->min = worker->smax = worker->hmax = 0;
1399     }
1400     if (worker->hmax) {
1401         rv = apr_reslist_create(&(worker->cp->res),
1402                                 worker->min, worker->smax,
1403                                 worker->hmax, worker->ttl,
1404                                 connection_constructor, connection_destructor,
1405                                 worker, worker->cp->pool);
1406
1407         apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
1408                                   conn_pool_cleanup,
1409                                   apr_pool_cleanup_null);
1410
1411         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1412                      "proxy: initialized worker for (%s) min=%d max=%d smax=%d",
1413                       worker->hostname, worker->min, worker->hmax, worker->smax);
1414
1415 #if (APR_MAJOR_VERSION > 0)
1416         /* Set the acquire timeout */
1417         if (rv == APR_SUCCESS && worker->acquire_set)
1418             apr_reslist_timeout_set(worker->cp->res, worker->acquire);
1419 #endif
1420     }
1421     else
1422 #endif
1423     {
1424         
1425         rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);
1426         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1427                      "proxy: initialized single connection worker for (%s)",
1428                       worker->hostname);
1429     }
1430     if (rv == APR_SUCCESS)
1431         worker->status |= PROXY_WORKER_INITIALIZED;
1432     return rv;
1433 }
1434
1435 PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
1436                                          proxy_worker *worker,
1437                                          server_rec *s)
1438 {
1439     if (worker->status & PROXY_WORKER_IN_ERROR) {
1440         apr_interval_time_t diff;
1441         apr_time_t now = apr_time_now();
1442         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1443                     "proxy: %s: retrying the worker for (%s)",
1444                      proxy_function, worker->hostname);
1445         if (worker->retry)
1446             diff = worker->retry;
1447         else {
1448             /* Increase the time by 1 minute on each retry */
1449             diff = apr_time_from_sec((60 + 60 * worker->retries));
1450             /* Use 10 minutes as maximum value for retry */
1451             if (worker->retries < 8)
1452                 ++worker->retries;
1453         }
1454         if (now > worker->error_time + diff) {
1455             worker->status &= ~PROXY_WORKER_IN_ERROR;
1456             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1457                          "proxy: %s: worker for (%s) has been marked for retry",
1458                          proxy_function, worker->hostname);
1459             return OK;
1460         }
1461         else
1462             return DECLINED;
1463     }
1464     else
1465         return OK;
1466 }
1467
1468 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
1469                                                proxy_conn_rec **conn,
1470                                                proxy_worker *worker,
1471                                                server_rec *s)
1472 {
1473     apr_status_t rv;
1474
1475     if (!PROXY_WORKER_IS_USABLE(worker)) {
1476         /* Retry the worker */
1477         ap_proxy_retry_worker(proxy_function, worker, s);
1478     
1479         if (!PROXY_WORKER_IS_USABLE(worker)) {
1480             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1481                          "proxy: %s: disabled connection for (%s)",
1482                          proxy_function, worker->hostname);
1483             return HTTP_SERVICE_UNAVAILABLE;
1484         }
1485     }
1486 #if APR_HAS_THREADS
1487     if (worker->hmax) {
1488         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
1489     }
1490     else
1491 #endif
1492     {
1493         /* create the new connection if the previous was destroyed */
1494         if (!worker->cp->conn)
1495             connection_constructor((void **)conn, worker, worker->cp->pool);
1496         else {
1497             *conn = worker->cp->conn;
1498             worker->cp->conn = NULL;
1499         }
1500         rv = APR_SUCCESS;
1501     }
1502
1503     if (rv != APR_SUCCESS) {
1504         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1505                      "proxy: %s: failed to acquire connection for (%s)",
1506                      proxy_function, worker->hostname);
1507         return HTTP_SERVICE_UNAVAILABLE;
1508     }
1509     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1510                  "proxy: %s: has acquired connection for (%s)",
1511                  proxy_function, worker->hostname);
1512
1513     (*conn)->worker = worker;
1514     (*conn)->close  = 0;
1515     (*conn)->close_on_recycle = 0;
1516
1517     return OK;
1518 }
1519
1520 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
1521                                                proxy_conn_rec *conn,
1522                                                server_rec *s)
1523 {
1524     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1525                  "proxy: %s: has released connection for (%s)",
1526                  proxy_function, conn->worker->hostname);
1527     /* If there is a connection kill it's cleanup */
1528     if (conn->connection) {
1529         apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
1530         conn->connection = NULL;
1531     }
1532     connection_cleanup(conn);
1533
1534     return OK;
1535 }
1536
1537 PROXY_DECLARE(int)
1538 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
1539                               proxy_server_conf *conf,
1540                               proxy_worker *worker,
1541                               proxy_conn_rec *conn,
1542                               apr_uri_t *uri,
1543                               char **url,
1544                               const char *proxyname,
1545                               apr_port_t proxyport,
1546                               char *server_portstr,
1547                               int server_portstr_size)
1548 {
1549     int server_port;
1550     apr_status_t err = APR_SUCCESS;
1551     /*
1552      * Break up the URL to determine the host to connect to
1553      */
1554
1555     /* we break the URL into host, port, uri */
1556     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
1557         return ap_proxyerror(r, HTTP_BAD_REQUEST,
1558                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
1559                                          NULL));
1560     }
1561     if (!uri->port) {
1562         uri->port = apr_uri_port_of_scheme(uri->scheme);
1563     }
1564
1565     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1566                  "proxy: connecting %s to %s:%d", *url, uri->hostname,
1567                  uri->port);
1568
1569     /* allocate these out of the specified connection pool 
1570      * The scheme handler decides if this is permanent or
1571      * short living pool.
1572      */
1573     /* are we connecting directly, or via a proxy? */
1574     if (!proxyname) {
1575         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
1576                            uri->query ? uri->query : "",
1577                            uri->fragment ? "#" : "",
1578                            uri->fragment ? uri->fragment : "", NULL);
1579     }
1580     if (!conn->hostname) {
1581         if (proxyname) {
1582             conn->hostname = apr_pstrdup(conn->pool, proxyname);
1583             conn->port = proxyport;
1584         } else {
1585             conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
1586             conn->port = uri->port;
1587         }
1588     }
1589     /* TODO: add address cache for forward proxies */
1590     conn->addr = worker->cp->addr;
1591     if (r->proxyreq == PROXYREQ_PROXY) {
1592         err = apr_sockaddr_info_get(&(conn->addr),
1593                                     conn->hostname, APR_UNSPEC,
1594                                     conn->port, 0,
1595                                     conn->pool);
1596     }
1597     else if (!worker->cp->addr) {
1598         /* Worker can have the single constant backend adress.
1599          * The single DNS lookup is used once per worker.
1600         * If dynamic change is needed then set the addr to NULL
1601         * inside dynamic config to force the lookup.
1602         */
1603         err = apr_sockaddr_info_get(&(worker->cp->addr),
1604                                     conn->hostname, APR_UNSPEC,
1605                                     conn->port, 0,
1606                                     worker->cp->pool);
1607         conn->addr = worker->cp->addr;
1608     }
1609     if (err != APR_SUCCESS) {
1610         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1611                              apr_pstrcat(p, "DNS lookup failure for: ",
1612                                          conn->hostname, NULL));
1613     }
1614
1615     /* Get the server port for the Via headers */
1616     {
1617         server_port = ap_get_server_port(r);
1618         if (ap_is_default_port(server_port, r)) {
1619             strcpy(server_portstr,"");
1620         } else {
1621             apr_snprintf(server_portstr, server_portstr_size, ":%d",
1622                          server_port);
1623         }
1624     }
1625
1626     /* check if ProxyBlock directive on this host */
1627     if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
1628         return ap_proxyerror(r, HTTP_FORBIDDEN,
1629                              "Connect to remote machine blocked");
1630     }
1631     return OK;
1632 }
1633
1634 static int is_socket_connected(apr_socket_t *sock)
1635
1636 {
1637     apr_size_t buffer_len = 1;
1638     char test_buffer[1]; 
1639     apr_status_t socket_status;
1640     apr_interval_time_t current_timeout;
1641     
1642     /* save timeout */
1643     apr_socket_timeout_get(sock, &current_timeout);
1644     /* set no timeout */
1645     apr_socket_timeout_set(sock, 0);
1646     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
1647     /* put back old timeout */
1648     apr_socket_timeout_set(sock, current_timeout);
1649     if (APR_STATUS_IS_EOF(socket_status))
1650         return 0;
1651     else
1652         return 1;
1653 }
1654
1655 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
1656                                             proxy_conn_rec *conn,
1657                                             proxy_worker *worker,
1658                                             server_rec *s)
1659 {
1660     apr_status_t rv;
1661     int connected = 0;
1662     int loglevel;
1663     apr_sockaddr_t *backend_addr = conn->addr;
1664     apr_socket_t *newsock;
1665     
1666     if (conn->sock) {
1667         /* This increases the connection pool size
1668          * but the number of dropped connections is
1669          * relatively small compared to connection lifetime
1670          */
1671         if (!(connected = is_socket_connected(conn->sock))) {        
1672             apr_socket_close(conn->sock);
1673             conn->sock = NULL;
1674         }
1675     }
1676     while (backend_addr && !connected) {
1677         if ((rv = apr_socket_create(&newsock, backend_addr->family,
1678                                 SOCK_STREAM, APR_PROTO_TCP,
1679                                 conn->pool)) != APR_SUCCESS) {
1680             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1681             ap_log_error(APLOG_MARK, loglevel, rv, s,
1682                          "proxy: %s: error creating fam %d socket for target %s",
1683                          proxy_function,
1684                          backend_addr->family,
1685                          worker->hostname);
1686             /* this could be an IPv6 address from the DNS but the
1687              * local machine won't give us an IPv6 socket; hopefully the
1688              * DNS returned an additional address to try
1689              */
1690             backend_addr = backend_addr->next;
1691             continue;
1692         }
1693
1694 #if !defined(TPF) && !defined(BEOS)
1695         if (worker->recv_buffer_size > 0 &&
1696             (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
1697                                      worker->recv_buffer_size))) {
1698             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1699                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1700                          "ProxyReceiveBufferSize, using default");
1701         }
1702 #endif
1703
1704         /* Set a timeout on the socket */
1705         if (worker->timeout_set == 1) {
1706             apr_socket_timeout_set(newsock, worker->timeout);
1707         }
1708         else {
1709              apr_socket_timeout_set(newsock, s->timeout);
1710         }
1711         /* Set a keepalive option */
1712         if (worker->keepalive) {
1713             if ((rv = apr_socket_opt_set(newsock, 
1714                             APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
1715                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1716                              "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
1717                              " Keepalive");
1718             }
1719         }
1720         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1721                      "proxy: %s: fam %d socket created to connect to %s",
1722                      proxy_function, backend_addr->family, worker->hostname);
1723
1724         /* make the connection out of the socket */
1725         rv = apr_socket_connect(newsock, backend_addr);
1726
1727         /* if an error occurred, loop round and try again */
1728         if (rv != APR_SUCCESS) {
1729             apr_socket_close(newsock);
1730             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1731             ap_log_error(APLOG_MARK, loglevel, rv, s,
1732                          "proxy: %s: attempt to connect to %pI (%s) failed",
1733                          proxy_function,
1734                          backend_addr,
1735                          worker->hostname);
1736             backend_addr = backend_addr->next;
1737             continue;
1738         }
1739         
1740         conn->sock   = newsock;
1741         connected    = 1;
1742     }
1743     /* Put the entire worker to error state if
1744      * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
1745      * Altrough some connections may be alive
1746      * no further connections to the worker could be made
1747      */
1748     if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
1749         !(worker->status & PROXY_WORKER_IGNORE_ERRORS)) {
1750         worker->status |= PROXY_WORKER_IN_ERROR;
1751         worker->error_time = apr_time_now();
1752         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1753             "ap_proxy_connect_backend disabling worker for (%s)",
1754             worker->hostname);
1755     }
1756     else {
1757         worker->error_time = 0;
1758         worker->retries = 0;
1759     }
1760     return connected ? OK : DECLINED;
1761 }
1762
1763 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
1764                                               proxy_conn_rec *conn,
1765                                               conn_rec *c,
1766                                               server_rec *s)
1767 {
1768     apr_sockaddr_t *backend_addr = conn->addr;
1769
1770     /* The socket is now open, create a new backend server connection 
1771     * 
1772     */
1773     conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
1774                                                 c->id, c->sbh,
1775                                                 c->bucket_alloc);
1776
1777     if (!conn->connection) {
1778         /* the peer reset the connection already; ap_run_create_connection() 
1779         * closed the socket
1780         */
1781         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1782                      s, "proxy: %s: an error occurred creating a "
1783                      "new connection to %pI (%s)", proxy_function,
1784                      backend_addr, conn->hostname);
1785         /* XXX: Will be closed when proxy_conn is closed */
1786         apr_socket_close(conn->sock);
1787         conn->sock = NULL;
1788         return HTTP_INTERNAL_SERVER_ERROR;
1789     }
1790     /* register the connection cleanup to client connection
1791      * so that the connection can be closed or reused
1792      */
1793     apr_pool_cleanup_register(c->pool, (void *)conn,
1794                               connection_cleanup,
1795                               apr_pool_cleanup_null);      
1796
1797     /* For ssl connection to backend */
1798     if (conn->is_ssl) {
1799         if (!ap_proxy_ssl_enable(conn->connection)) {
1800             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
1801                          s, "proxy: %s: failed to enable ssl support "
1802                          "for %pI (%s)", proxy_function, 
1803                          backend_addr, conn->hostname);
1804             return HTTP_INTERNAL_SERVER_ERROR;
1805         }
1806     }
1807     else {
1808         /* TODO: See if this will break FTP */
1809         ap_proxy_ssl_disable(conn->connection);
1810     }
1811
1812     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1813                  "proxy: %s: connection complete to %pI (%s)",
1814                  proxy_function, backend_addr, conn->hostname);
1815
1816     /* set up the connection filters */
1817     ap_run_pre_connection(conn->connection, conn->sock);
1818
1819     return OK;
1820 }
1821
1822 int ap_proxy_lb_workers(void)
1823 {
1824     /* Set the dynamic #workers limit */
1825     lb_workers_limit = lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
1826     return lb_workers_limit;
1827 }