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