]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
ed7590b969592adf1d943d8f60b59190a3765e9c
[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     if (r->proxyreq == PROXYREQ_PROXY) {
1241         if (conf->forward) {
1242             *balancer = NULL;
1243             *worker = conf->forward;
1244             access_status = OK;
1245         }
1246         else
1247             access_status = DECLINED;
1248         return access_status;
1249     }
1250     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
1251     if (access_status == DECLINED && *balancer == NULL) {
1252         *worker = ap_proxy_get_worker(r->pool, conf, *url);
1253         if (*worker) {
1254             *balancer = NULL;
1255             access_status = OK;
1256         }
1257         else
1258             access_status = DECLINED;
1259     }
1260     else if (access_status == DECLINED && balancer != NULL) {
1261         /* All the workers are busy */
1262         access_status = HTTP_SERVICE_UNAVAILABLE;
1263     }
1264     return access_status;
1265 }
1266
1267 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
1268                                          proxy_balancer *balancer,
1269                                          request_rec *r,
1270                                          proxy_server_conf *conf)
1271 {
1272     int access_status;
1273     if (balancer)
1274         access_status = proxy_run_post_request(worker, balancer, r, conf);
1275     else { 
1276         
1277
1278         access_status = OK;
1279     }
1280
1281     return access_status;
1282 }
1283
1284 /* DEPRECATED */
1285 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1286                                                const char *proxy_function,
1287                                                apr_sockaddr_t *backend_addr,
1288                                                const char *backend_name,
1289                                                proxy_server_conf *conf,
1290                                                server_rec *s,
1291                                                apr_pool_t *p)
1292 {
1293     apr_status_t rv;
1294     int connected = 0;
1295     int loglevel;
1296     
1297     while (backend_addr && !connected) {
1298         if ((rv = apr_socket_create(newsock, backend_addr->family,
1299                                     SOCK_STREAM, 0, p)) != APR_SUCCESS) {
1300             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1301             ap_log_error(APLOG_MARK, loglevel, rv, s,
1302                          "proxy: %s: error creating fam %d socket for target %s",
1303                          proxy_function,
1304                          backend_addr->family,
1305                          backend_name);
1306             /* this could be an IPv6 address from the DNS but the
1307              * local machine won't give us an IPv6 socket; hopefully the
1308              * DNS returned an additional address to try
1309              */
1310             backend_addr = backend_addr->next;
1311             continue;
1312         }
1313
1314 #if !defined(TPF) && !defined(BEOS)
1315         if (conf->recv_buffer_size > 0 &&
1316             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1317                                      conf->recv_buffer_size))) {
1318             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1319                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1320                          "ProxyReceiveBufferSize, using default");
1321         }
1322 #endif
1323
1324         /* Set a timeout on the socket */
1325         if (conf->timeout_set == 1) {
1326             apr_socket_timeout_set(*newsock, conf->timeout);
1327         }
1328         else {
1329              apr_socket_timeout_set(*newsock, s->timeout);
1330         }
1331
1332         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1333                      "proxy: %s: fam %d socket created to connect to %s",
1334                      proxy_function, backend_addr->family, backend_name);
1335
1336         /* make the connection out of the socket */
1337         rv = apr_socket_connect(*newsock, backend_addr);
1338
1339         /* if an error occurred, loop round and try again */
1340         if (rv != APR_SUCCESS) {
1341             apr_socket_close(*newsock);
1342             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1343             ap_log_error(APLOG_MARK, loglevel, rv, s,
1344                          "proxy: %s: attempt to connect to %pI (%s) failed",
1345                          proxy_function,
1346                          backend_addr,
1347                          backend_name);
1348             backend_addr = backend_addr->next;
1349             continue;
1350         }
1351         connected = 1;
1352     }
1353     return connected ? 0 : 1;
1354 }
1355
1356 static apr_status_t proxy_conn_cleanup(void *theconn)
1357 {
1358     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1359     /* Close the socket */
1360     if (conn->sock)
1361         apr_socket_close(conn->sock);
1362     conn->sock = NULL;
1363     conn->pool = NULL;
1364     return APR_SUCCESS;
1365 }
1366
1367 static apr_status_t connection_cleanup(void *theconn)
1368 {
1369     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1370     proxy_worker *worker = conn->worker;
1371     
1372     /* deterimine if the connection need to be closed */
1373     if (conn->close_on_recycle) {
1374         if (conn->sock)
1375             apr_socket_close(conn->sock);
1376         conn->sock = NULL;
1377     }
1378 #if APR_HAS_THREADS
1379     if (worker->hmax && worker->cp->res) {
1380         apr_reslist_release(worker->cp->res, (void *)conn);
1381     }
1382     else
1383 #endif
1384     {
1385         worker->cp->conn = conn;
1386     }
1387
1388     /* Allways return the SUCCESS */
1389     return APR_SUCCESS;
1390 }
1391
1392 /* reslist constructor */
1393 static apr_status_t connection_constructor(void **resource, void *params,
1394                                            apr_pool_t *pool)
1395 {
1396     apr_pool_t *ctx;
1397     proxy_conn_rec *conn;
1398     server_rec *s = (server_rec *)params;
1399     
1400     /* Create the subpool for each connection
1401      * This keeps the memory consumption constant
1402      * when disconnecting from backend.
1403      */
1404     apr_pool_create(&ctx, pool);
1405     conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec));
1406
1407     conn->pool = ctx;
1408     *resource = conn;
1409     /* register the pool cleanup */
1410     apr_pool_cleanup_register(ctx, (void *)conn,
1411                               proxy_conn_cleanup,
1412                               apr_pool_cleanup_null);      
1413
1414     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1415                  "proxy: socket is constructed");
1416
1417     return APR_SUCCESS;
1418 }
1419
1420 /* reslist destructor */
1421 static apr_status_t connection_destructor(void *resource, void *params,
1422                                           apr_pool_t *pool)
1423 {
1424     proxy_conn_rec *conn = (proxy_conn_rec *)resource;
1425     
1426 #if 0
1427     if (conn->sock)
1428         apr_socket_close(conn->sock);
1429     conn->sock = NULL;
1430     apr_pool_cleanup_kill(conn->pool, conn, proxy_conn_cleanup);
1431 #endif
1432     if (conn->pool)
1433         apr_pool_destroy(conn->pool);
1434     conn->pool = NULL;
1435 #if 0
1436     if (s != NULL)
1437         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1438                      "proxy: socket is destructed");
1439 #endif
1440     return APR_SUCCESS;
1441 }
1442
1443 /* Close the connection 
1444  * The proxy_conn_rec from now on can not be used
1445  */
1446 PROXY_DECLARE(apr_status_t) ap_proxy_close_connection(proxy_conn_rec *conn)
1447 {
1448
1449     if (conn->worker && conn->worker->cp)
1450         conn->worker->cp->conn = NULL;
1451     return connection_destructor(conn, NULL, NULL);
1452 }
1453
1454 static apr_status_t init_conn_worker(proxy_worker *worker, server_rec *s)
1455 {
1456     apr_status_t rv;
1457
1458 #if APR_HAS_THREADS
1459     int mpm_threads;
1460
1461     ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1462     if (mpm_threads > 1) {
1463         /* Set hard max to no more then mpm_threads */
1464         if (worker->hmax == 0 || worker->hmax > mpm_threads)
1465             worker->hmax = mpm_threads;
1466         if (worker->smax == 0 || worker->smax > worker->hmax)
1467             worker->smax = worker->hmax;
1468         /* Set min to be lower then smax */
1469         if (worker->min > worker->smax)
1470             worker->min = worker->smax; 
1471         worker->cp->nfree = worker->hmax;
1472     }
1473     else {
1474         /* This will supress the apr_reslist creation */
1475         worker->min = worker->smax = worker->hmax = 0;
1476     }
1477     if (worker->hmax) {
1478         rv = apr_reslist_create(&(worker->cp->res),
1479                                 worker->min, worker->smax,
1480                                 worker->hmax, worker->ttl,
1481                                 connection_constructor, connection_destructor,
1482                                 s, worker->cp->pool);
1483         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1484                      "proxy: initialized worker for (%s) min=%d max=%d smax=%d",
1485                       worker->hostname, worker->min, worker->hmax, worker->smax);
1486
1487 #if (APR_MAJOR_VERSION > 0)
1488         /* Set the acquire timeout */
1489         if (rv == APR_SUCCESS && worker->acquire_set)
1490             apr_reslist_timeout_set(worker->cp->res, worker->acquire);
1491 #endif
1492     }
1493     else
1494 #endif
1495     {
1496         
1497         connection_constructor((void **)&(worker->cp->conn), s, worker->cp->pool);
1498         rv = APR_SUCCESS;
1499         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1500                      "proxy: initialized single connection worker for (%s)",
1501                       worker->hostname);
1502     }
1503
1504     return rv;
1505 }
1506
1507 PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
1508                                          proxy_worker *worker,
1509                                          server_rec *s)
1510 {
1511     if (worker->status & PROXY_WORKER_IN_ERROR) {
1512         apr_interval_time_t diff;
1513         apr_time_t now = apr_time_now();
1514         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1515                     "proxy: %s: retrying the worker for (%s)",
1516                      proxy_function, worker->hostname);
1517         if (worker->retry)
1518             diff = worker->retry;
1519         else
1520             diff = apr_time_from_sec((60 + 60 * worker->retries++));
1521         if (now > worker->error_time + diff) {
1522             worker->status &= ~PROXY_WORKER_IN_ERROR;
1523             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1524                          "proxy: %s: worker for (%s) has been marked for retry",
1525                          proxy_function, worker->hostname);
1526             return OK;
1527         }
1528         else
1529             return DECLINED;
1530     }
1531     else
1532         return OK;
1533 }
1534
1535 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
1536                                                proxy_conn_rec **conn,
1537                                                proxy_worker *worker,
1538                                                server_rec *s)
1539 {
1540     apr_status_t rv;
1541
1542     if (!(worker->status & PROXY_WORKER_INITIALIZED)) {
1543         if ((rv = init_conn_worker(worker, s)) != APR_SUCCESS) {
1544             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1545                          "proxy: %s: failed to initialize worker for (%s)",
1546                          proxy_function, worker->hostname);
1547             return HTTP_INTERNAL_SERVER_ERROR;
1548         }
1549         worker->status = PROXY_WORKER_INITIALIZED;
1550     }
1551
1552     if (!PROXY_WORKER_IS_USABLE(worker)) {
1553         /* Retry the worker */
1554         ap_proxy_retry_worker(proxy_function, worker, s);
1555     
1556         if (!PROXY_WORKER_IS_USABLE(worker)) {
1557             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1558                          "proxy: %s: disabled connection for (%s)",
1559                          proxy_function, worker->hostname);
1560             return HTTP_SERVICE_UNAVAILABLE;
1561         }
1562     }
1563 #if APR_HAS_THREADS
1564     if (worker->hmax) {
1565         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
1566     }
1567     else
1568 #endif
1569     {
1570         /* create the new connection if the previous was destroyed */
1571         if (!worker->cp->conn)
1572             connection_constructor((void **)conn, s, worker->cp->pool);
1573         else {
1574             *conn = worker->cp->conn;
1575             worker->cp->conn = NULL;
1576         }
1577         rv = APR_SUCCESS;
1578     }
1579
1580     if (rv != APR_SUCCESS) {
1581         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1582                      "proxy: %s: failed to acquire connection for (%s)",
1583                      proxy_function, worker->hostname);
1584         return HTTP_SERVICE_UNAVAILABLE;
1585     }
1586     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1587                  "proxy: %s: has acquired connection for (%s)",
1588                  proxy_function, worker->hostname);
1589
1590     (*conn)->worker = worker;
1591
1592     return OK;
1593 }
1594
1595 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
1596                                                proxy_conn_rec *conn,
1597                                                server_rec *s)
1598 {
1599     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1600                  "proxy: %s: has relesed connection for (%s)",
1601                  proxy_function, conn->worker->hostname);
1602     /* If there is a connection kill it's cleanup */
1603     if (conn->connection)
1604         apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
1605     connection_cleanup(conn);
1606     conn->connection = NULL;
1607
1608     return OK;
1609 }
1610
1611 PROXY_DECLARE(int)
1612 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
1613                               proxy_server_conf *conf,
1614                               proxy_worker *worker,
1615                               proxy_conn_rec *conn,
1616                               apr_pool_t *ppool,
1617                               apr_uri_t *uri,
1618                               char **url,
1619                               const char *proxyname,
1620                               apr_port_t proxyport,
1621                               char *server_portstr,
1622                               int server_portstr_size)
1623 {
1624     int server_port;
1625     apr_status_t err = APR_SUCCESS;
1626     /*
1627      * Break up the URL to determine the host to connect to
1628      */
1629
1630     /* we break the URL into host, port, uri */
1631     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
1632         return ap_proxyerror(r, HTTP_BAD_REQUEST,
1633                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
1634                                          NULL));
1635     }
1636     if (!uri->port) {
1637         uri->port = apr_uri_port_of_scheme(uri->scheme);
1638     }
1639
1640     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1641                  "proxy: connecting %s to %s:%d", *url, uri->hostname,
1642                  uri->port);
1643
1644     /* allocate these out of the specified connection pool 
1645      * The scheme handler decides if this is permanent or
1646      * short living pool.
1647      */
1648     /* are we connecting directly, or via a proxy? */
1649     if (proxyname) {
1650         conn->hostname = apr_pstrdup(ppool, proxyname);
1651         conn->port = proxyport;
1652     } else {
1653         conn->hostname = apr_pstrdup(ppool, uri->hostname);
1654         conn->port = uri->port;
1655         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
1656                            uri->query ? uri->query : "",
1657                            uri->fragment ? "#" : "",
1658                            uri->fragment ? uri->fragment : "", NULL);
1659     }
1660     
1661     /* TODO: add address cache for forward proxies */
1662     conn->addr = worker->cp->addr;
1663     if (r->proxyreq == PROXYREQ_PROXY) {
1664         err = apr_sockaddr_info_get(&(conn->addr),
1665                                     conn->hostname, APR_UNSPEC,
1666                                     conn->port, 0,
1667                                     p);
1668     }
1669     else if (!worker->cp->addr) {
1670         /* Worker can have the single constant backend adress.
1671          * The single DNS lookup is used once per worker.
1672         * If dynamic change is needed then set the addr to NULL
1673         * inside dynamic config to force the lookup.
1674         */
1675         err = apr_sockaddr_info_get(&(worker->cp->addr),
1676                                     conn->hostname, APR_UNSPEC,
1677                                     conn->port, 0,
1678                                     worker->cp->pool);
1679         conn->addr = worker->cp->addr;
1680     }
1681     if (err != APR_SUCCESS) {
1682         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1683                              apr_pstrcat(p, "DNS lookup failure for: ",
1684                                          conn->hostname, NULL));
1685     }
1686
1687     /* Get the server port for the Via headers */
1688     {
1689         server_port = ap_get_server_port(r);
1690         if (ap_is_default_port(server_port, r)) {
1691             strcpy(server_portstr,"");
1692         } else {
1693             apr_snprintf(server_portstr, server_portstr_size, ":%d",
1694                          server_port);
1695         }
1696     }
1697
1698     /* check if ProxyBlock directive on this host */
1699     if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
1700         return ap_proxyerror(r, HTTP_FORBIDDEN,
1701                              "Connect to remote machine blocked");
1702     }
1703     return OK;
1704 }
1705
1706 static int is_socket_connected(apr_socket_t *sock)
1707
1708 {
1709     apr_size_t buffer_len = 1;
1710     char test_buffer[1]; 
1711     apr_status_t socket_status;
1712     apr_interval_time_t current_timeout;
1713     
1714     /* save timeout */
1715     apr_socket_timeout_get(sock, &current_timeout);
1716     /* set no timeout */
1717     apr_socket_timeout_set(sock, 0);
1718     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
1719     /* put back old timeout */
1720     apr_socket_timeout_set(sock, current_timeout);
1721     if (APR_STATUS_IS_EOF(socket_status))
1722         return 0;
1723     else
1724         return 1;
1725 }
1726
1727 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
1728                                             proxy_conn_rec *conn,
1729                                             proxy_worker *worker,
1730                                             server_rec *s)
1731 {
1732     apr_status_t rv;
1733     int connected = 0;
1734     int loglevel;
1735     apr_sockaddr_t *backend_addr = conn->addr;
1736     apr_socket_t *newsock;
1737     
1738     if (conn->sock) {
1739         /* This increases the connection pool size
1740          * but the number of dropped connections is
1741          * relatively small compared to connection lifetime
1742          */
1743         if (!(connected = is_socket_connected(conn->sock))) {        
1744             apr_socket_close(conn->sock);
1745             conn->sock = NULL;
1746         }
1747     }
1748     while (backend_addr && !connected) {
1749         if ((rv = apr_socket_create(&newsock, backend_addr->family,
1750                                 SOCK_STREAM, APR_PROTO_TCP,
1751                                 conn->pool)) != APR_SUCCESS) {
1752             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1753             ap_log_error(APLOG_MARK, loglevel, rv, s,
1754                          "proxy: %s: error creating fam %d socket for target %s",
1755                          proxy_function,
1756                          backend_addr->family,
1757                          worker->hostname);
1758             /* this could be an IPv6 address from the DNS but the
1759              * local machine won't give us an IPv6 socket; hopefully the
1760              * DNS returned an additional address to try
1761              */
1762             backend_addr = backend_addr->next;
1763             continue;
1764         }
1765
1766 #if !defined(TPF) && !defined(BEOS)
1767         if (worker->recv_buffer_size > 0 &&
1768             (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
1769                                      worker->recv_buffer_size))) {
1770             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1771                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1772                          "ProxyReceiveBufferSize, using default");
1773         }
1774 #endif
1775
1776         /* Set a timeout on the socket */
1777         if (worker->timeout_set == 1) {
1778             apr_socket_timeout_set(newsock, worker->timeout);
1779         }
1780         else {
1781              apr_socket_timeout_set(newsock, s->timeout);
1782         }
1783         /* Set a keepalive option */
1784         if (worker->keepalive) {
1785             if ((rv = apr_socket_opt_set(newsock, 
1786                             APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
1787                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1788                              "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
1789                              " Keepalive");
1790             }
1791         }
1792         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1793                      "proxy: %s: fam %d socket created to connect to %s",
1794                      proxy_function, backend_addr->family, worker->hostname);
1795
1796         /* make the connection out of the socket */
1797         rv = apr_socket_connect(newsock, backend_addr);
1798
1799         /* if an error occurred, loop round and try again */
1800         if (rv != APR_SUCCESS) {
1801             apr_socket_close(newsock);
1802             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1803             ap_log_error(APLOG_MARK, loglevel, rv, s,
1804                          "proxy: %s: attempt to connect to %pI (%s) failed",
1805                          proxy_function,
1806                          backend_addr,
1807                          worker->hostname);
1808             backend_addr = backend_addr->next;
1809             continue;
1810         }
1811         
1812         conn->sock   = newsock;
1813         connected    = 1;
1814     }
1815     /* Put the entire worker to error state if
1816      * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
1817      * Altrough some connections may be alive
1818      * no further connections to the worker could be made
1819      */
1820     if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
1821         !(worker->status & PROXY_WORKER_IGNORE_ERRORS)) {
1822         worker->status |= PROXY_WORKER_IN_ERROR;
1823         worker->error_time = apr_time_now();
1824         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1825             "ap_proxy_connect_backend disabling worker for (%s)",
1826             worker->hostname);
1827     }
1828     else {
1829         worker->error_time = 0;
1830         worker->retries = 0;
1831     }
1832     return connected ? OK : DECLINED;
1833 }
1834
1835 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
1836                                               proxy_conn_rec *conn,
1837                                               conn_rec *c,
1838                                               server_rec *s)
1839 {
1840     proxy_worker *worker = conn->worker;
1841     apr_sockaddr_t *backend_addr = conn->addr;
1842
1843     /* The socket is now open, create a new backend server connection 
1844     * 
1845     */
1846     conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
1847                                                 c->id, c->sbh,
1848                                                 c->bucket_alloc);
1849
1850     if (!conn->connection) {
1851         /* the peer reset the connection already; ap_run_create_connection() 
1852         * closed the socket
1853         */
1854         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1855                      s, "proxy: %s: an error occurred creating a "
1856                      "new connection to %pI (%s)", proxy_function,
1857                      backend_addr, conn->hostname);
1858         /* XXX: Will be closed when proxy_conn is closed */
1859         apr_socket_close(conn->sock);
1860         conn->sock = NULL;
1861         return HTTP_INTERNAL_SERVER_ERROR;
1862     }
1863     /* register the connection cleanup to client connection
1864      * so that the connection can be closed or reused
1865      */
1866     apr_pool_cleanup_register(c->pool, (void *)conn,
1867                               connection_cleanup,
1868                               apr_pool_cleanup_null);      
1869
1870     /* For ssl connection to backend */
1871     if (conn->is_ssl) {
1872         if (!ap_proxy_ssl_enable(conn->connection)) {
1873             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
1874                          s, "proxy: %s: failed to enable ssl support "
1875                          "for %pI (%s)", proxy_function, 
1876                          backend_addr, conn->hostname);
1877             return HTTP_INTERNAL_SERVER_ERROR;
1878         }
1879     }
1880     else {
1881         /* TODO: See if this will break FTP */
1882         ap_proxy_ssl_disable(conn->connection);
1883     }
1884
1885     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1886                  "proxy: %s: connection complete to %pI (%s)",
1887                  proxy_function, backend_addr, conn->hostname);
1888
1889     /* set up the connection filters */
1890     ap_run_pre_connection(conn->connection, conn->sock);
1891
1892     return OK;
1893 }
1894
1895 PROXY_DECLARE(int) ap_proxy_lb_workers(void)
1896 {
1897     /* Set the dynamic #workers limit */
1898     lb_workers_limit = lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
1899     return lb_workers_limit;
1900 }