]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
2a55c5bcacd603bcc5c79f15d2a63188408a91aa
[apache] / modules / proxy / proxy_util.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* Utility routines for Apache proxy */
18 #include "mod_proxy.h"
19 #include "ap_mpm.h"
20 #include "scoreboard.h"
21 #include "apr_version.h"
22
23 #if APR_HAVE_UNISTD_H
24 #include <unistd.h>         /* for getpid() */
25 #endif
26
27 #if (APR_MAJOR_VERSION < 1)
28 #undef apr_socket_create
29 #define apr_socket_create apr_socket_create_ex
30 #endif
31
32 /* Global balancer counter */
33 int PROXY_DECLARE_DATA proxy_lb_workers = 0;
34 static int lb_workers_limit = 0;
35
36 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
37 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
38 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
39 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
40
41 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
42                                    (request_rec *r, request_rec *pr), (r, pr),
43                                    OK, DECLINED)
44
45 /* already called in the knowledge that the characters are hex digits */
46 PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
47 {
48     int i, ch;
49
50 #if !APR_CHARSET_EBCDIC
51     ch = x[0];
52     if (apr_isdigit(ch)) {
53         i = ch - '0';
54     }
55     else if (apr_isupper(ch)) {
56         i = ch - ('A' - 10);
57     }
58     else {
59         i = ch - ('a' - 10);
60     }
61     i <<= 4;
62
63     ch = x[1];
64     if (apr_isdigit(ch)) {
65         i += ch - '0';
66     }
67     else if (apr_isupper(ch)) {
68         i += ch - ('A' - 10);
69     }
70     else {
71         i += ch - ('a' - 10);
72     }
73     return i;
74 #else /*APR_CHARSET_EBCDIC*/
75     /*
76      * we assume that the hex value refers to an ASCII character
77      * so convert to EBCDIC so that it makes sense locally;
78      *
79      * example:
80      *
81      * client specifies %20 in URL to refer to a space char;
82      * at this point we're called with EBCDIC "20"; after turning
83      * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
84      * represents an ASCII char and convert 0x20 to EBCDIC, yielding
85      * 0x40
86      */
87     char buf[1];
88
89     if (1 == sscanf(x, "%2x", &i)) {
90         buf[0] = i & 0xFF;
91         ap_xlate_proto_from_ascii(buf, 1);
92         return buf[0];
93     }
94     else {
95         return 0;
96     }
97 #endif /*APR_CHARSET_EBCDIC*/
98 }
99
100 PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
101 {
102 #if !APR_CHARSET_EBCDIC
103     int i;
104
105     x[0] = '%';
106     i = (ch & 0xF0) >> 4;
107     if (i >= 10) {
108         x[1] = ('A' - 10) + i;
109     }
110     else {
111         x[1] = '0' + i;
112     }
113
114     i = ch & 0x0F;
115     if (i >= 10) {
116         x[2] = ('A' - 10) + i;
117     }
118     else {
119         x[2] = '0' + i;
120     }
121 #else /*APR_CHARSET_EBCDIC*/
122     static const char ntoa[] = { "0123456789ABCDEF" };
123     char buf[1];
124
125     ch &= 0xFF;
126
127     buf[0] = ch;
128     ap_xlate_proto_to_ascii(buf, 1);
129
130     x[0] = '%';
131     x[1] = ntoa[(buf[0] >> 4) & 0x0F];
132     x[2] = ntoa[buf[0] & 0x0F];
133     x[3] = '\0';
134 #endif /*APR_CHARSET_EBCDIC*/
135 }
136
137 /*
138  * canonicalise a URL-encoded string
139  */
140
141 /*
142  * Convert a URL-encoded string to canonical form.
143  * It decodes characters which need not be encoded,
144  * and encodes those which must be encoded, and does not touch
145  * those which must not be touched.
146  */
147 PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len,
148                                        enum enctype t, int forcedec,
149                                        int proxyreq)
150 {
151     int i, j, ch;
152     char *y;
153     char *allowed;  /* characters which should not be encoded */
154     char *reserved; /* characters which much not be en/de-coded */
155
156 /*
157  * N.B. in addition to :@&=, this allows ';' in an http path
158  * and '?' in an ftp path -- this may be revised
159  *
160  * Also, it makes a '+' character in a search string reserved, as
161  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
162  * it only permits ; / ? : @ = & as reserved chars.)
163  */
164     if (t == enc_path) {
165         allowed = "~$-_.+!*'(),;:@&=";
166     }
167     else if (t == enc_search) {
168         allowed = "$-_.!*'(),;:@&=";
169     }
170     else if (t == enc_user) {
171         allowed = "$-_.+!*'(),;@&=";
172     }
173     else if (t == enc_fpath) {
174         allowed = "$-_.+!*'(),?:@&=";
175     }
176     else {            /* if (t == enc_parm) */
177         allowed = "$-_.+!*'(),?/:@&=";
178     }
179
180     if (t == enc_path) {
181         reserved = "/";
182     }
183     else if (t == enc_search) {
184         reserved = "+";
185     }
186     else {
187         reserved = "";
188     }
189
190     y = apr_palloc(p, 3 * len + 1);
191
192     for (i = 0, j = 0; i < len; i++, j++) {
193 /* always handle '/' first */
194         ch = x[i];
195         if (strchr(reserved, ch)) {
196             y[j] = ch;
197             continue;
198         }
199 /*
200  * decode it if not already done. do not decode reverse proxied URLs
201  * unless specifically forced
202  */
203         if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
204             if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) {
205                 return NULL;
206             }
207             ch = ap_proxy_hex2c(&x[i + 1]);
208             i += 2;
209             if (ch != 0 && strchr(reserved, ch)) {  /* keep it encoded */
210                 ap_proxy_c2hex(ch, &y[j]);
211                 j += 2;
212                 continue;
213             }
214         }
215 /* recode it, if necessary */
216         if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
217             ap_proxy_c2hex(ch, &y[j]);
218             j += 2;
219         }
220         else {
221             y[j] = ch;
222         }
223     }
224     y[j] = '\0';
225     return y;
226 }
227
228 /*
229  * Parses network-location.
230  *    urlp           on input the URL; on output the path, after the leading /
231  *    user           NULL if no user/password permitted
232  *    password       holder for password
233  *    host           holder for host
234  *    port           port number; only set if one is supplied.
235  *
236  * Returns an error string.
237  */
238 PROXY_DECLARE(char *)
239      ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
240             char **passwordp, char **hostp, apr_port_t *port)
241 {
242     char *addr, *scope_id, *strp, *host, *url = *urlp;
243     char *user = NULL, *password = NULL;
244     apr_port_t tmp_port;
245     apr_status_t rv;
246
247     if (url[0] != '/' || url[1] != '/') {
248         return "Malformed URL";
249     }
250     host = url + 2;
251     url = strchr(host, '/');
252     if (url == NULL) {
253         url = "";
254     }
255     else {
256         *(url++) = '\0';    /* skip seperating '/' */
257     }
258
259     /* find _last_ '@' since it might occur in user/password part */
260     strp = strrchr(host, '@');
261
262     if (strp != NULL) {
263         *strp = '\0';
264         user = host;
265         host = strp + 1;
266
267 /* find password */
268         strp = strchr(user, ':');
269         if (strp != NULL) {
270             *strp = '\0';
271             password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
272             if (password == NULL) {
273                 return "Bad %-escape in URL (password)";
274             }
275         }
276
277         user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
278         if (user == NULL) {
279             return "Bad %-escape in URL (username)";
280         }
281     }
282     if (userp != NULL) {
283         *userp = user;
284     }
285     if (passwordp != NULL) {
286         *passwordp = password;
287     }
288
289     /*
290      * Parse the host string to separate host portion from optional port.
291      * Perform range checking on port.
292      */
293     rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
294     if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
295         return "Invalid host/port";
296     }
297     if (tmp_port != 0) { /* only update caller's port if port was specified */
298         *port = tmp_port;
299     }
300
301     ap_str_tolower(addr); /* DNS names are case-insensitive */
302
303     *urlp = url;
304     *hostp = addr;
305
306     return NULL;
307 }
308
309 /*
310  * If the date is a valid RFC 850 date or asctime() date, then it
311  * is converted to the RFC 1123 format.
312  */
313 PROXY_DECLARE(const char *)
314      ap_proxy_date_canon(apr_pool_t *p, const char *date)
315 {
316     apr_status_t rv;
317     char* ndate;
318
319     apr_time_t time = apr_date_parse_http(date);
320     if (!time) {
321         return date;
322     }
323
324     ndate = apr_palloc(p, APR_RFC822_DATE_LEN);
325     rv = apr_rfc822_date(ndate, time);
326     if (rv != APR_SUCCESS) {
327         return date;
328     }
329
330     return ndate;
331 }
332
333 PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
334 {
335     request_rec *rp = apr_pcalloc(r->pool, sizeof(*r));
336
337     rp->pool            = r->pool;
338     rp->status          = HTTP_OK;
339
340     rp->headers_in      = apr_table_make(r->pool, 50);
341     rp->subprocess_env  = apr_table_make(r->pool, 50);
342     rp->headers_out     = apr_table_make(r->pool, 12);
343     rp->err_headers_out = apr_table_make(r->pool, 5);
344     rp->notes           = apr_table_make(r->pool, 5);
345
346     rp->server = r->server;
347     rp->proxyreq = r->proxyreq;
348     rp->request_time = r->request_time;
349     rp->connection      = c;
350     rp->output_filters  = c->output_filters;
351     rp->input_filters   = c->input_filters;
352     rp->proto_output_filters  = c->output_filters;
353     rp->proto_input_filters   = c->input_filters;
354
355     rp->request_config  = ap_create_request_config(r->pool);
356     proxy_run_create_req(r, rp);
357
358     return rp;
359 }
360
361
362 /*
363  * list is a comma-separated list of case-insensitive tokens, with
364  * optional whitespace around the tokens.
365  * The return returns 1 if the token val is found in the list, or 0
366  * otherwise.
367  */
368 PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
369 {
370     int len, i;
371     const char *p;
372
373     len = strlen(val);
374
375     while (list != NULL) {
376         p = ap_strchr_c(list, ',');
377         if (p != NULL) {
378             i = p - list;
379             do {
380                 p++;
381             } while (apr_isspace(*p));
382         }
383         else {
384             i = strlen(list);
385         }
386
387         while (i > 0 && apr_isspace(list[i - 1])) {
388             i--;
389         }
390         if (i == len && strncasecmp(list, val, len) == 0) {
391             return 1;
392         }
393         list = p;
394     }
395     return 0;
396 }
397
398 /*
399  * list is a comma-separated list of case-insensitive tokens, with
400  * optional whitespace around the tokens.
401  * if val appears on the list of tokens, it is removed from the list,
402  * and the new list is returned.
403  */
404 PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
405 {
406     int len, i;
407     const char *p;
408     char *new = NULL;
409
410     len = strlen(val);
411
412     while (list != NULL) {
413         p = ap_strchr_c(list, ',');
414         if (p != NULL) {
415             i = p - list;
416             do {
417                 p++;
418             } while (apr_isspace(*p));
419         }
420         else {
421             i = strlen(list);
422         }
423
424         while (i > 0 && apr_isspace(list[i - 1])) {
425             i--;
426         }
427         if (i == len && strncasecmp(list, val, len) == 0) {
428             /* do nothing */
429         }
430         else {
431             if (new) {
432                 new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
433             }
434             else {
435                 new = apr_pstrndup(pool, list, i);
436             }
437         }
438         list = p;
439     }
440     return new;
441 }
442
443 /*
444  * Converts 8 hex digits to a time integer
445  */
446 PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
447 {
448     int i, ch;
449     unsigned int j;
450
451     for (i = 0, j = 0; i < 8; i++) {
452         ch = x[i];
453         j <<= 4;
454         if (apr_isdigit(ch)) {
455             j |= ch - '0';
456         }
457         else if (apr_isupper(ch)) {
458             j |= ch - ('A' - 10);
459         }
460         else {
461             j |= ch - ('a' - 10);
462         }
463     }
464     if (j == 0xffffffff) {
465         return -1;      /* so that it works with 8-byte ints */
466     }
467     else {
468         return j;
469     }
470 }
471
472 /*
473  * Converts a time integer to 8 hex digits
474  */
475 PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
476 {
477     int i, ch;
478     unsigned int j = t;
479
480     for (i = 7; i >= 0; i--) {
481         ch = j & 0xF;
482         j >>= 4;
483         if (ch >= 10) {
484             y[i] = ch + ('A' - 10);
485         }
486         else {
487             y[i] = ch + '0';
488         }
489     }
490     y[8] = '\0';
491 }
492
493 PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
494 {
495     apr_table_setn(r->notes, "error-notes",
496     apr_pstrcat(r->pool,
497         "The proxy server could not handle the request "
498         "<em><a href=\"", ap_escape_html(r->pool, r->uri),
499         "\">", ap_escape_html(r->pool, r->method),
500         "&nbsp;",
501         ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
502         "Reason: <strong>",
503         ap_escape_html(r->pool, message),
504         "</strong></p>", NULL));
505
506     /* Allow "error-notes" string to be printed by ap_send_error_response() */
507     apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
508
509     r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
510     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
511              "proxy: %s returned by %s", message, r->uri);
512     return statuscode;
513 }
514
515 static const char *
516      proxy_get_host_of_request(request_rec *r)
517 {
518     char *url, *user = NULL, *password = NULL, *err, *host;
519     apr_port_t port;
520
521     if (r->hostname != NULL) {
522         return r->hostname;
523     }
524
525     /* Set url to the first char after "scheme://" */
526     if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') {
527         return NULL;
528     }
529
530     url = apr_pstrdup(r->pool, &url[1]);    /* make it point to "//", which is what proxy_canon_netloc expects */
531
532     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
533
534     if (err != NULL) {
535         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", err);
536     }
537
538     r->hostname = host;
539
540     return host;        /* ought to return the port, too */
541 }
542
543 /* Return TRUE if addr represents an IP address (or an IP network address) */
544 PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
545 {
546     const char *addr = This->name;
547     long ip_addr[4];
548     int i, quads;
549     long bits;
550
551     /*
552      * if the address is given with an explicit netmask, use that
553      * Due to a deficiency in apr_inet_addr(), it is impossible to parse
554      * "partial" addresses (with less than 4 quads) correctly, i.e.
555      * 192.168.123 is parsed as 192.168.0.123, which is not what I want.
556      * I therefore have to parse the IP address manually:
557      * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0)
558      * addr and mask were set by proxy_readmask()
559      * return 1;
560      */
561
562     /*
563      * Parse IP addr manually, optionally allowing
564      * abbreviated net addresses like 192.168.
565      */
566
567     /* Iterate over up to 4 (dotted) quads. */
568     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
569         char *tmp;
570
571         if (*addr == '/' && quads > 0) {  /* netmask starts here. */
572             break;
573         }
574
575         if (!apr_isdigit(*addr)) {
576             return 0;       /* no digit at start of quad */
577         }
578
579         ip_addr[quads] = strtol(addr, &tmp, 0);
580
581         if (tmp == addr) {  /* expected a digit, found something else */
582             return 0;
583         }
584
585         if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
586             /* invalid octet */
587             return 0;
588         }
589
590         addr = tmp;
591
592         if (*addr == '.' && quads != 3) {
593             ++addr;     /* after the 4th quad, a dot would be illegal */
594         }
595     }
596
597     for (This->addr.s_addr = 0, i = 0; i < quads; ++i) {
598         This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
599     }
600
601     if (addr[0] == '/' && apr_isdigit(addr[1])) {   /* net mask follows: */
602         char *tmp;
603
604         ++addr;
605
606         bits = strtol(addr, &tmp, 0);
607
608         if (tmp == addr) {   /* expected a digit, found something else */
609             return 0;
610         }
611
612         addr = tmp;
613
614         if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */
615             return 0;
616         }
617
618     }
619     else {
620         /*
621          * Determine (i.e., "guess") netmask by counting the
622          * number of trailing .0's; reduce #quads appropriately
623          * (so that 192.168.0.0 is equivalent to 192.168.)
624          */
625         while (quads > 0 && ip_addr[quads - 1] == 0) {
626             --quads;
627         }
628
629         /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
630         if (quads < 1) {
631             return 0;
632         }
633
634         /* every zero-byte counts as 8 zero-bits */
635         bits = 8 * quads;
636
637         if (bits != 32) {     /* no warning for fully qualified IP address */
638             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
639                          "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
640                          inet_ntoa(This->addr), bits);
641         }
642     }
643
644     This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
645
646     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
647         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
648                      "Warning: NetMask and IP-Addr disagree in %s/%ld",
649                      inet_ntoa(This->addr), bits);
650         This->addr.s_addr &= This->mask.s_addr;
651         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
652                      "         Set to %s/%ld", inet_ntoa(This->addr), bits);
653     }
654
655     if (*addr == '\0') {
656         This->matcher = proxy_match_ipaddr;
657         return 1;
658     }
659     else {
660         return (*addr == '\0'); /* okay iff we've parsed the whole string */
661     }
662 }
663
664 /* Return TRUE if addr represents an IP address (or an IP network address) */
665 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
666 {
667     int i, ip_addr[4];
668     struct in_addr addr, *ip;
669     const char *host = proxy_get_host_of_request(r);
670
671     if (host == NULL) {   /* oops! */
672        return 0;
673     }
674
675     memset(&addr, '\0', sizeof addr);
676     memset(ip_addr, '\0', sizeof ip_addr);
677
678     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
679         for (addr.s_addr = 0, i = 0; i < 4; ++i) {
680             /* ap_proxy_is_ipaddr() already confirmed that we have
681              * a valid octet in ip_addr[i]
682              */
683             addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
684         }
685
686         if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
687 #if DEBUGGING
688             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
689                          "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
690             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
691                          "%s/", inet_ntoa(This->addr));
692             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
693                          "%s", inet_ntoa(This->mask));
694 #endif
695             return 1;
696         }
697 #if DEBUGGING
698         else {
699             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
700                          "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
701             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
702                          "%s/", inet_ntoa(This->addr));
703             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
704                          "%s", inet_ntoa(This->mask));
705         }
706 #endif
707     }
708     else {
709         struct apr_sockaddr_t *reqaddr;
710
711         if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
712             != APR_SUCCESS) {
713 #if DEBUGGING
714             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
715              "2)IP-NoMatch: hostname=%s msg=Host not found", host);
716 #endif
717             return 0;
718         }
719
720         /* Try to deal with multiple IP addr's for a host */
721         /* FIXME: This needs to be able to deal with IPv6 */
722         while (reqaddr) {
723             ip = (struct in_addr *) reqaddr->ipaddr_ptr;
724             if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
725 #if DEBUGGING
726                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
727                              "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
728                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
729                              "%s/", inet_ntoa(This->addr));
730                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
731                              "%s", inet_ntoa(This->mask));
732 #endif
733                 return 1;
734             }
735 #if DEBUGGING
736             else {
737                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
738                              "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
739                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
740                              "%s/", inet_ntoa(This->addr));
741                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
742                              "%s", inet_ntoa(This->mask));
743             }
744 #endif
745             reqaddr = reqaddr->next;
746         }
747     }
748
749     return 0;
750 }
751
752 /* Return TRUE if addr represents a domain name */
753 PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
754 {
755     char *addr = This->name;
756     int i;
757
758     /* Domain name must start with a '.' */
759     if (addr[0] != '.') {
760         return 0;
761     }
762
763     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
764     for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) {
765         continue;
766     }
767
768 #if 0
769     if (addr[i] == ':') {
770     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
771                      "@@@@ handle optional port in proxy_is_domainname()");
772     /* @@@@ handle optional port */
773     }
774 #endif
775
776     if (addr[i] != '\0') {
777         return 0;
778     }
779
780     /* Strip trailing dots */
781     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) {
782         addr[i] = '\0';
783     }
784
785     This->matcher = proxy_match_domainname;
786     return 1;
787 }
788
789 /* Return TRUE if host "host" is in domain "domain" */
790 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
791 {
792     const char *host = proxy_get_host_of_request(r);
793     int d_len = strlen(This->name), h_len;
794
795     if (host == NULL) {      /* some error was logged already */
796         return 0;
797     }
798
799     h_len = strlen(host);
800
801     /* @@@ do this within the setup? */
802     /* Ignore trailing dots in domain comparison: */
803     while (d_len > 0 && This->name[d_len - 1] == '.') {
804         --d_len;
805     }
806     while (h_len > 0 && host[h_len - 1] == '.') {
807         --h_len;
808     }
809     return h_len > d_len
810         && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
811 }
812
813 /* Return TRUE if host represents a host name */
814 PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
815 {
816     struct apr_sockaddr_t *addr;
817     char *host = This->name;
818     int i;
819
820     /* Host names must not start with a '.' */
821     if (host[0] == '.') {
822         return 0;
823     }
824     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
825     for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
826
827     if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) {
828         return 0;
829     }
830
831     This->hostaddr = addr;
832
833     /* Strip trailing dots */
834     for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) {
835         host[i] = '\0';
836     }
837
838     This->matcher = proxy_match_hostname;
839     return 1;
840 }
841
842 /* Return TRUE if host "host" is equal to host2 "host2" */
843 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
844 {
845     char *host = This->name;
846     const char *host2 = proxy_get_host_of_request(r);
847     int h2_len;
848     int h1_len;
849
850     if (host == NULL || host2 == NULL) {
851         return 0; /* oops! */
852     }
853
854     h2_len = strlen(host2);
855     h1_len = strlen(host);
856
857 #if 0
858     struct apr_sockaddr_t *addr = *This->hostaddr;
859
860     /* Try to deal with multiple IP addr's for a host */
861     while (addr) {
862         if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
863             return 1;
864         addr = addr->next;
865     }
866 #endif
867
868     /* Ignore trailing dots in host2 comparison: */
869     while (h2_len > 0 && host2[h2_len - 1] == '.') {
870         --h2_len;
871     }
872     while (h1_len > 0 && host[h1_len - 1] == '.') {
873         --h1_len;
874     }
875     return h1_len == h2_len
876         && strncasecmp(host, host2, h1_len) == 0;
877 }
878
879 /* Return TRUE if addr is to be matched as a word */
880 PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
881 {
882     This->matcher = proxy_match_word;
883     return 1;
884 }
885
886 /* Return TRUE if string "str2" occurs literally in "str1" */
887 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
888 {
889     const char *host = proxy_get_host_of_request(r);
890     return host != NULL && ap_strstr_c(host, This->name) != NULL;
891 }
892
893 /* checks whether a host in uri_addr matches proxyblock */
894 PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
895                              apr_sockaddr_t *uri_addr)
896 {
897     int j;
898     apr_sockaddr_t * src_uri_addr = uri_addr;
899     /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
900     for (j = 0; j < conf->noproxies->nelts; j++) {
901         struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
902         struct apr_sockaddr_t *conf_addr = npent[j].addr;
903         uri_addr = src_uri_addr;
904         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
905                      "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
906         if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
907             || npent[j].name[0] == '*') {
908             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
909                          "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
910             return HTTP_FORBIDDEN;
911         }
912         while (conf_addr) {
913             uri_addr = src_uri_addr;
914             while (uri_addr) {
915                 char *conf_ip;
916                 char *uri_ip;
917                 apr_sockaddr_ip_get(&conf_ip, conf_addr);
918                 apr_sockaddr_ip_get(&uri_ip, uri_addr);
919                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
920                              "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
921                 if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
922                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
923                                  "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
924                     return HTTP_FORBIDDEN;
925                 }
926                 uri_addr = uri_addr->next;
927             }
928             conf_addr = conf_addr->next;
929         }
930     }
931     return OK;
932 }
933
934 /* set up the minimal filter set */
935 PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
936 {
937     ap_add_input_filter("HTTP_IN", NULL, r, c);
938     return OK;
939 }
940
941 /*
942  * converts a series of buckets into a string
943  * XXX: BillS says this function performs essentially the same function as
944  * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline()
945  * instead? I think ap_proxy_string_read() will not work properly on non ASCII
946  * (EBCDIC) machines either.
947  */
948 PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
949                                                  char *buff, apr_size_t bufflen, int *eos)
950 {
951     apr_bucket *e;
952     apr_status_t rv;
953     char *pos = buff;
954     char *response;
955     int found = 0;
956     apr_size_t len;
957
958     /* start with an empty string */
959     buff[0] = 0;
960     *eos = 0;
961
962     /* loop through each brigade */
963     while (!found) {
964         /* get brigade from network one line at a time */
965         if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb,
966                                                 AP_MODE_GETLINE,
967                                                 APR_BLOCK_READ,
968                                                 0))) {
969             return rv;
970         }
971         /* loop through each bucket */
972         while (!found) {
973             if (*eos || APR_BRIGADE_EMPTY(bb)) {
974                 /* The connection aborted or timed out */
975                 return APR_ECONNABORTED;
976             }
977             e = APR_BRIGADE_FIRST(bb);
978             if (APR_BUCKET_IS_EOS(e)) {
979                 *eos = 1;
980             }
981             else {
982                 if (APR_SUCCESS != (rv = apr_bucket_read(e,
983                                                          (const char **)&response,
984                                                          &len,
985                                                          APR_BLOCK_READ))) {
986                     return rv;
987                 }
988                 /*
989                  * is string LF terminated?
990                  * XXX: This check can be made more efficient by simply checking
991                  * if the last character in the 'response' buffer is an ASCII_LF.
992                  * See ap_rgetline() for an example.
993                  */
994                 if (memchr(response, APR_ASCII_LF, len)) {
995                     found = 1;
996                 }
997                 /* concat strings until buff is full - then throw the data away */
998                 if (len > ((bufflen-1)-(pos-buff))) {
999                     len = (bufflen-1)-(pos-buff);
1000                 }
1001                 if (len > 0) {
1002                     memcpy(pos, response, len);
1003                     pos += len;
1004                 }
1005             }
1006             APR_BUCKET_REMOVE(e);
1007             apr_bucket_destroy(e);
1008         }
1009         *pos = '\0';
1010     }
1011
1012     return APR_SUCCESS;
1013 }
1014
1015 /* unmerge an element in the table */
1016 PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
1017 {
1018     apr_off_t offset = 0;
1019     apr_off_t count = 0;
1020     char *value = NULL;
1021
1022     /* get the value to unmerge */
1023     const char *initial = apr_table_get(t, key);
1024     if (!initial) {
1025         return;
1026     }
1027     value = apr_pstrdup(p, initial);
1028
1029     /* remove the value from the headers */
1030     apr_table_unset(t, key);
1031
1032     /* find each comma */
1033     while (value[count]) {
1034         if (value[count] == ',') {
1035             value[count] = 0;
1036             apr_table_add(t, key, value + offset);
1037             offset = count + 1;
1038         }
1039         count++;
1040     }
1041     apr_table_add(t, key, value + offset);
1042 }
1043
1044 PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
1045                               proxy_dir_conf *conf, const char *url)
1046 {
1047     proxy_req_conf *rconf;
1048     struct proxy_alias *ent;
1049     int i, l1, l2;
1050     char *u;
1051
1052     /*
1053      * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
1054      * after the hostname
1055      */
1056     if (r->proxyreq != PROXYREQ_REVERSE) {
1057         return url;
1058     }
1059
1060     l1 = strlen(url);
1061     if (conf->interpolate_env == 1) {
1062         rconf = ap_get_module_config(r->request_config, &proxy_module);
1063         ent = (struct proxy_alias *)rconf->raliases->elts;
1064     }
1065     else {
1066         ent = (struct proxy_alias *)conf->raliases->elts;
1067     }
1068     for (i = 0; i < conf->raliases->nelts; i++) {
1069         proxy_server_conf *sconf = (proxy_server_conf *)
1070             ap_get_module_config(r->server->module_config, &proxy_module);
1071         proxy_balancer *balancer;
1072         const char *real;
1073         real = ent[i].real;
1074         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1075                          "ppr: real: %s", real);
1076         /*
1077          * First check if mapping against a balancer and see
1078          * if we have such a entity. If so, then we need to
1079          * find the particulars of the actual worker which may
1080          * or may not be the right one... basically, we need
1081          * to find which member actually handled this request.
1082          */
1083         if ((strncasecmp(real, "balancer:", 9) == 0) &&
1084             (balancer = ap_proxy_get_balancer(r->pool, sconf, real))) {
1085             int n;
1086             proxy_worker *worker;
1087             worker = (proxy_worker *)balancer->workers->elts;
1088             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1089                          "ppr: checking balancer: %s",
1090                          balancer->name);
1091             for (n = 0; n < balancer->workers->nelts; n++) {
1092                 if (worker->port) {
1093                     u = apr_psprintf(r->pool, "%s://%s:%d/", worker->scheme,
1094                                      worker->hostname, worker->port);
1095                 }
1096                 else {
1097                     u = apr_psprintf(r->pool, "%s://%s/", worker->scheme,
1098                                      worker->hostname);
1099                 }
1100                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1101                          "ppr: matching member (%s) and URL (%s)",
1102                          u, url);
1103
1104                 l2 = strlen(u);
1105                 if (l1 >= l2 && strncasecmp(u, url, l2) == 0) {
1106                     u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
1107                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1108                          "ppr: matched member (%s)", u);
1109                     return ap_construct_url(r->pool, u, r);
1110                 }
1111                 worker++;
1112             }
1113         }
1114
1115         l2 = strlen(real);
1116         if (l1 >= l2 && strncasecmp(real, url, l2) == 0) {
1117             u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
1118             return ap_construct_url(r->pool, u, r);
1119         }
1120     }
1121
1122     return url;
1123 }
1124
1125 /*
1126  * Cookies are a bit trickier to match: we've got two substrings to worry
1127  * about, and we can't just find them with strstr 'cos of case.  Regexp
1128  * matching would be an easy fix, but for better consistency with all the
1129  * other matches we'll refrain and use apr_strmatch to find path=/domain=
1130  * and stick to plain strings for the config values.
1131  */
1132 PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
1133                               proxy_dir_conf *conf, const char *str)
1134 {
1135     proxy_req_conf *rconf = ap_get_module_config(r->request_config,
1136                                                  &proxy_module);
1137     struct proxy_alias *ent;
1138     size_t len = strlen(str);
1139     const char *newpath = NULL;
1140     const char *newdomain = NULL;
1141     const char *pathp;
1142     const char *domainp;
1143     const char *pathe = NULL;
1144     const char *domaine = NULL;
1145     size_t l1, l2, poffs = 0, doffs = 0;
1146     int i;
1147     int ddiff = 0;
1148     int pdiff = 0;
1149     char *ret;
1150
1151     if (r->proxyreq != PROXYREQ_REVERSE) {
1152         return str;
1153     }
1154
1155    /*
1156     * Find the match and replacement, but save replacing until we've done
1157     * both path and domain so we know the new strlen
1158     */
1159     if ((pathp = apr_strmatch(conf->cookie_path_str, str, len)) != NULL) {
1160         pathp += 5;
1161         poffs = pathp - str;
1162         pathe = ap_strchr_c(pathp, ';');
1163         l1 = pathe ? (pathe - pathp) : strlen(pathp);
1164         pathe = pathp + l1 ;
1165         if (conf->interpolate_env == 1) {
1166             ent = (struct proxy_alias *)rconf->cookie_paths->elts;
1167         }
1168         else {
1169             ent = (struct proxy_alias *)conf->cookie_paths->elts;
1170         }
1171         for (i = 0; i < conf->cookie_paths->nelts; i++) {
1172             l2 = strlen(ent[i].fake);
1173             if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
1174                 newpath = ent[i].real;
1175                 pdiff = strlen(newpath) - l1;
1176                 break;
1177             }
1178         }
1179     }
1180
1181     if ((domainp = apr_strmatch(conf->cookie_domain_str, str, len)) != NULL) {
1182         domainp += 7;
1183         doffs = domainp - str;
1184         domaine = ap_strchr_c(domainp, ';');
1185         l1 = domaine ? (domaine - domainp) : strlen(domainp);
1186         domaine = domainp + l1;
1187         if (conf->interpolate_env == 1) {
1188             ent = (struct proxy_alias *)rconf->cookie_domains->elts;
1189         }
1190         else {
1191             ent = (struct proxy_alias *)conf->cookie_domains->elts;
1192         }
1193         for (i = 0; i < conf->cookie_domains->nelts; i++) {
1194             l2 = strlen(ent[i].fake);
1195             if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
1196                 newdomain = ent[i].real;
1197                 ddiff = strlen(newdomain) - l1;
1198                 break;
1199             }
1200         }
1201     }
1202
1203     if (newpath) {
1204         ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1205         l1 = strlen(newpath);
1206         if (newdomain) {
1207             l2 = strlen(newdomain);
1208             if (doffs > poffs) {
1209                 memcpy(ret, str, poffs);
1210                 memcpy(ret + poffs, newpath, l1);
1211                 memcpy(ret + poffs + l1, pathe, domainp - pathe);
1212                 memcpy(ret + doffs + pdiff, newdomain, l2);
1213                 strcpy(ret + doffs + pdiff + l2, domaine);
1214             }
1215             else {
1216                 memcpy(ret, str, doffs) ;
1217                 memcpy(ret + doffs, newdomain, l2);
1218                 memcpy(ret + doffs + l2, domaine, pathp - domaine);
1219                 memcpy(ret + poffs + ddiff, newpath, l1);
1220                 strcpy(ret + poffs + ddiff + l1, pathe);
1221             }
1222         }
1223         else {
1224             memcpy(ret, str, poffs);
1225             memcpy(ret + poffs, newpath, l1);
1226             strcpy(ret + poffs + l1, pathe);
1227         }
1228     }
1229     else {
1230         if (newdomain) {
1231             ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1232             l2 = strlen(newdomain);
1233             memcpy(ret, str, doffs);
1234             memcpy(ret + doffs, newdomain, l2);
1235             strcpy(ret + doffs+l2, domaine);
1236         }
1237         else {
1238             ret = (char *)str; /* no change */
1239         }
1240     }
1241
1242     return ret;
1243 }
1244
1245 PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
1246                                                       proxy_server_conf *conf,
1247                                                       const char *url)
1248 {
1249     proxy_balancer *balancer;
1250     char *c, *uri = apr_pstrdup(p, url);
1251     int i;
1252
1253     c = strchr(uri, ':');
1254     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1255        return NULL;
1256     }
1257     /* remove path from uri */
1258     if ((c = strchr(c + 3, '/'))) {
1259         *c = '\0';
1260     }
1261     balancer = (proxy_balancer *)conf->balancers->elts;
1262     for (i = 0; i < conf->balancers->nelts; i++) {
1263         if (strcasecmp(balancer->name, uri) == 0) {
1264             return balancer;
1265         }
1266         balancer++;
1267     }
1268     return NULL;
1269 }
1270
1271 PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
1272                                                   apr_pool_t *p,
1273                                                   proxy_server_conf *conf,
1274                                                   const char *url)
1275 {
1276     char *c, *q, *uri = apr_pstrdup(p, url);
1277     proxy_balancer_method *lbmethod;
1278
1279     c = strchr(uri, ':');
1280     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1281        return "Bad syntax for a balancer name";
1282     /* remove path from uri */
1283     if ((q = strchr(c + 3, '/')))
1284         *q = '\0';
1285
1286     ap_str_tolower(uri);
1287     *balancer = apr_array_push(conf->balancers);
1288     memset(*balancer, 0, sizeof(proxy_balancer));
1289
1290     /*
1291      * NOTE: The default method is byrequests, which we assume
1292      * exists!
1293      */
1294     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
1295     if (!lbmethod) {
1296         return "Can't find 'byrequests' lb method";
1297     }
1298
1299     (*balancer)->name = uri;
1300     (*balancer)->lbmethod = lbmethod;
1301     (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker));
1302     /* XXX Is this a right place to create mutex */
1303 #if APR_HAS_THREADS
1304     if (apr_thread_mutex_create(&((*balancer)->mutex),
1305                 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1306         /* XXX: Do we need to log something here */
1307         return "can not create thread mutex";
1308     }
1309 #endif
1310
1311     return NULL;
1312 }
1313
1314 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1315                                                   proxy_server_conf *conf,
1316                                                   const char *url)
1317 {
1318     proxy_worker *worker;
1319     proxy_worker *max_worker = NULL;
1320     int max_match = 0;
1321     int url_length;
1322     int min_match;
1323     int worker_name_length;
1324     const char *c;
1325     char *url_copy;
1326     int i;
1327
1328     c = ap_strchr_c(url, ':');
1329     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1330        return NULL;
1331     }
1332
1333     url_copy = apr_pstrdup(p, url);
1334     url_length = strlen(url);
1335
1336     /*
1337      * We need to find the start of the path and
1338      * therefore we know the length of the scheme://hostname/
1339      * part to we can force-lowercase everything up to
1340      * the start of the path.
1341      */
1342     c = ap_strchr_c(c+3, '/');
1343     if (c) {
1344         char *pathstart;
1345         pathstart = url_copy + (c - url);
1346         *pathstart = '\0';
1347         ap_str_tolower(url_copy);
1348         min_match = strlen(url_copy);
1349         *pathstart = '/';
1350     }
1351     else {
1352         ap_str_tolower(url_copy);
1353         min_match = strlen(url_copy);
1354     }
1355
1356     worker = (proxy_worker *)conf->workers->elts;
1357
1358     /*
1359      * Do a "longest match" on the worker name to find the worker that
1360      * fits best to the URL, but keep in mind that we must have at least
1361      * a minimum matching of length min_match such that
1362      * scheme://hostname[:port] matches between worker and url.
1363      */
1364     for (i = 0; i < conf->workers->nelts; i++) {
1365         if ( ((worker_name_length = strlen(worker->name)) <= url_length)
1366            && (worker_name_length >= min_match)
1367            && (worker_name_length > max_match)
1368            && (strncmp(url_copy, worker->name, worker_name_length) == 0) ) {
1369             max_worker = worker;
1370             max_match = worker_name_length;
1371         }
1372         worker++;
1373     }
1374     return max_worker;
1375 }
1376
1377 #if APR_HAS_THREADS
1378 static apr_status_t conn_pool_cleanup(void *theworker)
1379 {
1380     proxy_worker *worker = (proxy_worker *)theworker;
1381     if (worker->cp->res) {
1382         worker->cp->pool = NULL;
1383     }
1384     return APR_SUCCESS;
1385 }
1386 #endif
1387
1388 static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1389 {
1390     apr_pool_t *pool;
1391     proxy_conn_pool *cp;
1392
1393     /*
1394      * Create a connection pool's subpool.
1395      * This pool is used for connection recycling.
1396      * Once the worker is added it is never removed but
1397      * it can be disabled.
1398      */
1399     apr_pool_create(&pool, p);
1400     apr_pool_tag(pool, "proxy_worker_cp");
1401     /*
1402      * Alloc from the same pool as worker.
1403      * proxy_conn_pool is permanently attached to the worker.
1404      */
1405     cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1406     cp->pool = pool;
1407     worker->cp = cp;
1408 }
1409
1410 PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
1411                                                 apr_pool_t *p,
1412                                                 proxy_server_conf *conf,
1413                                                 const char *url)
1414 {
1415     int rv;
1416     apr_uri_t uri;
1417
1418     rv = apr_uri_parse(p, url, &uri);
1419
1420     if (rv != APR_SUCCESS) {
1421         return "Unable to parse URL";
1422     }
1423     if (!uri.hostname || !uri.scheme) {
1424         return "URL must be absolute!";
1425     }
1426
1427     ap_str_tolower(uri.hostname);
1428     ap_str_tolower(uri.scheme);
1429     *worker = apr_array_push(conf->workers);
1430     memset(*worker, 0, sizeof(proxy_worker));
1431     (*worker)->name = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
1432     (*worker)->scheme = uri.scheme;
1433     (*worker)->hostname = uri.hostname;
1434     (*worker)->port = uri.port;
1435     (*worker)->id   = proxy_lb_workers;
1436     (*worker)->flush_packets = flush_off;
1437     (*worker)->flush_wait = PROXY_FLUSH_WAIT;
1438     /* Increase the total worker count */
1439     proxy_lb_workers++;
1440     init_conn_pool(p, *worker);
1441 #if APR_HAS_THREADS
1442     if (apr_thread_mutex_create(&((*worker)->mutex),
1443                 APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
1444         /* XXX: Do we need to log something here */
1445         return "can not create thread mutex";
1446     }
1447 #endif
1448
1449     return NULL;
1450 }
1451
1452 PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
1453 {
1454
1455     proxy_worker *worker;
1456     worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
1457     worker->id = proxy_lb_workers;
1458     /* Increase the total worker count */
1459     proxy_lb_workers++;
1460     init_conn_pool(p, worker);
1461
1462     return worker;
1463 }
1464
1465 PROXY_DECLARE(void)
1466 ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer,
1467                                 proxy_worker *worker)
1468 {
1469     proxy_worker *runtime;
1470
1471     runtime = apr_array_push(balancer->workers);
1472     memcpy(runtime, worker, sizeof(proxy_worker));
1473     runtime->id = proxy_lb_workers;
1474     /* Increase the total runtime count */
1475     proxy_lb_workers++;
1476
1477 }
1478
1479 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
1480                                         proxy_balancer **balancer,
1481                                         request_rec *r,
1482                                         proxy_server_conf *conf, char **url)
1483 {
1484     int access_status;
1485
1486     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
1487     if (access_status == DECLINED && *balancer == NULL) {
1488         *worker = ap_proxy_get_worker(r->pool, conf, *url);
1489         if (*worker) {
1490             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1491                           "proxy: %s: found worker %s for %s",
1492                            (*worker)->scheme, (*worker)->name, *url);
1493
1494             *balancer = NULL;
1495             access_status = OK;
1496         }
1497         else if (r->proxyreq == PROXYREQ_PROXY) {
1498             if (conf->forward) {
1499                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1500                               "proxy: *: found forward proxy worker for %s",
1501                                *url);
1502                 *balancer = NULL;
1503                 *worker = conf->forward;
1504                 access_status = OK;
1505             }
1506         }
1507         else if (r->proxyreq == PROXYREQ_REVERSE) {
1508             if (conf->reverse) {
1509                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1510                               "proxy: *: found reverse proxy worker for %s",
1511                                *url);
1512                 *balancer = NULL;
1513                 *worker = conf->reverse;
1514                 access_status = OK;
1515             }
1516         }
1517     }
1518     else if (access_status == DECLINED && *balancer != NULL) {
1519         /* All the workers are busy */
1520         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1521           "proxy: all workers are busy.  Unable to serve %s",
1522           *url);
1523         access_status = HTTP_SERVICE_UNAVAILABLE;
1524     }
1525     return access_status;
1526 }
1527
1528 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
1529                                          proxy_balancer *balancer,
1530                                          request_rec *r,
1531                                          proxy_server_conf *conf)
1532 {
1533     int access_status = OK;
1534     if (balancer) {
1535         access_status = proxy_run_post_request(worker, balancer, r, conf);
1536         if (access_status == DECLINED) {
1537             access_status = OK; /* no post_request handler available */
1538             /* TODO: recycle direct worker */
1539         }
1540     }
1541
1542     return access_status;
1543 }
1544
1545 /* DEPRECATED */
1546 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
1547                                                const char *proxy_function,
1548                                                apr_sockaddr_t *backend_addr,
1549                                                const char *backend_name,
1550                                                proxy_server_conf *conf,
1551                                                server_rec *s,
1552                                                apr_pool_t *p)
1553 {
1554     apr_status_t rv;
1555     int connected = 0;
1556     int loglevel;
1557
1558     while (backend_addr && !connected) {
1559         if ((rv = apr_socket_create(newsock, backend_addr->family,
1560                                     SOCK_STREAM, 0, p)) != APR_SUCCESS) {
1561             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1562             ap_log_error(APLOG_MARK, loglevel, rv, s,
1563                          "proxy: %s: error creating fam %d socket for target %s",
1564                          proxy_function,
1565                          backend_addr->family,
1566                          backend_name);
1567             /*
1568              * this could be an IPv6 address from the DNS but the
1569              * local machine won't give us an IPv6 socket; hopefully the
1570              * DNS returned an additional address to try
1571              */
1572             backend_addr = backend_addr->next;
1573             continue;
1574         }
1575
1576 #if !defined(TPF) && !defined(BEOS)
1577         if (conf->recv_buffer_size > 0 &&
1578             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
1579                                      conf->recv_buffer_size))) {
1580             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1581                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
1582                          "ProxyReceiveBufferSize, using default");
1583         }
1584 #endif
1585
1586         rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1);
1587         if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
1588              ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1589                           "apr_socket_opt_set(APR_TCP_NODELAY): "
1590                           "Failed to set");
1591         }
1592
1593         /* Set a timeout on the socket */
1594         if (conf->timeout_set == 1) {
1595             apr_socket_timeout_set(*newsock, conf->timeout);
1596         }
1597         else {
1598              apr_socket_timeout_set(*newsock, s->timeout);
1599         }
1600
1601         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1602                      "proxy: %s: fam %d socket created to connect to %s",
1603                      proxy_function, backend_addr->family, backend_name);
1604
1605         /* make the connection out of the socket */
1606         rv = apr_socket_connect(*newsock, backend_addr);
1607
1608         /* if an error occurred, loop round and try again */
1609         if (rv != APR_SUCCESS) {
1610             apr_socket_close(*newsock);
1611             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
1612             ap_log_error(APLOG_MARK, loglevel, rv, s,
1613                          "proxy: %s: attempt to connect to %pI (%s) failed",
1614                          proxy_function,
1615                          backend_addr,
1616                          backend_name);
1617             backend_addr = backend_addr->next;
1618             continue;
1619         }
1620         connected = 1;
1621     }
1622     return connected ? 0 : 1;
1623 }
1624
1625 static apr_status_t connection_cleanup(void *theconn)
1626 {
1627     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1628     proxy_worker *worker = conn->worker;
1629     apr_bucket_brigade *bb;
1630     conn_rec *c;
1631     request_rec *r;
1632
1633     /*
1634      * If the connection pool is NULL the worker
1635      * cleanup has been run. Just return.
1636      */
1637     if (!worker->cp) {
1638         return APR_SUCCESS;
1639     }
1640
1641 #if APR_HAS_THREADS
1642     /* Sanity check: Did we already return the pooled connection? */
1643     if (conn->inreslist) {
1644         ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool,
1645                       "proxy: Pooled connection 0x%pp for worker %s has been"
1646                       " already returned to the connection pool.", conn,
1647                       worker->name);
1648         return APR_SUCCESS;
1649     }
1650 #endif
1651
1652     r = conn->r;
1653     if (conn->need_flush && r && (r->bytes_sent || r->eos_sent)) {
1654         /*
1655          * We need to ensure that buckets that may have been buffered in the
1656          * network filters get flushed to the network. This is needed since
1657          * these buckets have been created with the bucket allocator of the
1658          * backend connection. This allocator either gets destroyed if
1659          * conn->close is set or the worker address is not reusable which
1660          * causes the connection to the backend to be closed or it will be used
1661          * again by another frontend connection that wants to recycle the
1662          * backend connection.
1663          * In this case we could run into nasty race conditions (e.g. if the
1664          * next user of the backend connection destroys the allocator before we
1665          * sent the buckets to the network).
1666          *
1667          * Remark 1: Only do this if buckets where sent down the chain before
1668          * that could still be buffered in the network filter. This is the case
1669          * if we have sent an EOS bucket or if we actually sent buckets with
1670          * data down the chain. In all other cases we either have not sent any
1671          * buckets at all down the chain or we only sent meta buckets that are
1672          * not EOS buckets down the chain. The only meta bucket that remains in
1673          * this case is the flush bucket which would have removed all possibly
1674          * buffered buckets in the network filter.
1675          * If we sent a flush bucket in the case where not ANY buckets were
1676          * sent down the chain, we break error handling which happens AFTER us.
1677          *
1678          * Remark 2: Doing a setaside does not help here as the buckets remain
1679          * created by the wrong allocator in this case.
1680          *
1681          * Remark 3: Yes, this creates a possible performance penalty in the case
1682          * of pipelined requests as we may send only a small amount of data over
1683          * the wire.
1684          */
1685         c = r->connection;
1686         bb = apr_brigade_create(r->pool, c->bucket_alloc);
1687         if (r->eos_sent) {
1688             /*
1689              * If we have already sent an EOS bucket send directly to the
1690              * connection based filters. We just want to flush the buckets
1691              * if something hasn't been sent to the network yet.
1692              */
1693             ap_fflush(c->output_filters, bb);
1694         }
1695         else {
1696             ap_fflush(r->output_filters, bb);
1697         }
1698         apr_brigade_destroy(bb);
1699         conn->r = NULL;
1700         conn->need_flush = 0;
1701     }
1702
1703     /* determine if the connection need to be closed */
1704     if (conn->close || !worker->is_address_reusable || worker->disablereuse) {
1705         apr_pool_t *p = conn->pool;
1706         apr_pool_clear(p);
1707         conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
1708         conn->pool = p;
1709         conn->worker = worker;
1710         apr_pool_create(&(conn->scpool), p);
1711         apr_pool_tag(conn->scpool, "proxy_conn_scpool");
1712     }
1713 #if APR_HAS_THREADS
1714     if (worker->hmax && worker->cp->res) {
1715         conn->inreslist = 1;
1716         apr_reslist_release(worker->cp->res, (void *)conn);
1717     }
1718     else
1719 #endif
1720     {
1721         worker->cp->conn = conn;
1722     }
1723
1724     /* Always return the SUCCESS */
1725     return APR_SUCCESS;
1726 }
1727
1728 static void socket_cleanup(proxy_conn_rec *conn)
1729 {
1730     conn->sock = NULL;
1731     conn->connection = NULL;
1732     apr_pool_clear(conn->scpool);
1733 }
1734
1735 PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
1736                                                             request_rec *r)
1737 {
1738     apr_bucket_brigade *bb;
1739     apr_status_t rv;
1740
1741     /*
1742      * If we have an existing SSL connection it might be possible that the
1743      * server sent some SSL message we have not read so far (e.g. a SSL
1744      * shutdown message if the server closed the keepalive connection while
1745      * the connection was held unused in our pool).
1746      * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
1747      * processed. We don't expect any data to be in the returned brigade.
1748      */
1749     if (conn->sock && conn->connection) {
1750         bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
1751         rv = ap_get_brigade(conn->connection->input_filters, bb,
1752                             AP_MODE_READBYTES, APR_NONBLOCK_READ,
1753                             HUGE_STRING_LEN);
1754         if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
1755             socket_cleanup(conn);
1756         }
1757         if (!APR_BRIGADE_EMPTY(bb)) {
1758             apr_off_t len;
1759
1760             rv = apr_brigade_length(bb, 0, &len);
1761             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,
1762                           "proxy: SSL cleanup brigade contained %"
1763                           APR_OFF_T_FMT " bytes of data.", len);
1764         }
1765         apr_brigade_destroy(bb);
1766     }
1767     return APR_SUCCESS;
1768 }
1769
1770 /* reslist constructor */
1771 static apr_status_t connection_constructor(void **resource, void *params,
1772                                            apr_pool_t *pool)
1773 {
1774     apr_pool_t *ctx;
1775     apr_pool_t *scpool;
1776     proxy_conn_rec *conn;
1777     proxy_worker *worker = (proxy_worker *)params;
1778
1779     /*
1780      * Create the subpool for each connection
1781      * This keeps the memory consumption constant
1782      * when disconnecting from backend.
1783      */
1784     apr_pool_create(&ctx, pool);
1785     apr_pool_tag(ctx, "proxy_conn_pool");
1786     /*
1787      * Create another subpool that manages the data for the
1788      * socket and the connection member of the proxy_conn_rec struct as we
1789      * destroy this data more frequently than other data in the proxy_conn_rec
1790      * struct like hostname and addr (at least in the case where we have
1791      * keepalive connections that timed out).
1792      */
1793     apr_pool_create(&scpool, ctx);
1794     apr_pool_tag(scpool, "proxy_conn_scpool");
1795     conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec));
1796
1797     conn->pool   = ctx;
1798     conn->scpool = scpool;
1799     conn->worker = worker;
1800 #if APR_HAS_THREADS
1801     conn->inreslist = 1;
1802 #endif
1803     *resource = conn;
1804
1805     return APR_SUCCESS;
1806 }
1807
1808 #if APR_HAS_THREADS /* only needed when threads are used */
1809 /* reslist destructor */
1810 static apr_status_t connection_destructor(void *resource, void *params,
1811                                           apr_pool_t *pool)
1812 {
1813     proxy_conn_rec *conn = (proxy_conn_rec *)resource;
1814
1815     /* Destroy the pool only if not called from reslist_destroy */
1816     if (conn->worker->cp->pool) {
1817         apr_pool_destroy(conn->pool);
1818     }
1819
1820     return APR_SUCCESS;
1821 }
1822 #endif
1823
1824 /*
1825  * ap_proxy_initialize_worker_share() concerns itself
1826  * with initializing those parts of worker which
1827  * are, or could be, shared. Basically worker->s
1828  */
1829 PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
1830                                                      proxy_worker *worker,
1831                                                      server_rec *s)
1832 {
1833     proxy_worker_stat *score = NULL;
1834
1835     if (PROXY_WORKER_IS_INITIALIZED(worker)) {
1836         /* The worker share is already initialized */
1837         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1838               "proxy: worker %s already initialized",
1839               worker->name);
1840         return;
1841     }
1842     /* Get scoreboard slot */
1843     if (ap_scoreboard_image) {
1844         score = (proxy_worker_stat *) ap_get_scoreboard_lb(worker->id);
1845         if (!score) {
1846             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1847                   "proxy: ap_get_scoreboard_lb(%d) failed in child %" APR_PID_T_FMT " for worker %s",
1848                   worker->id, getpid(), worker->name);
1849         }
1850         else {
1851              ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1852                   "proxy: grabbed scoreboard slot %d in child %" APR_PID_T_FMT " for worker %s",
1853                   worker->id, getpid(), worker->name);
1854         }
1855     }
1856     if (!score) {
1857         score = (proxy_worker_stat *) apr_pcalloc(conf->pool, sizeof(proxy_worker_stat));
1858         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1859               "proxy: initialized plain memory in child %" APR_PID_T_FMT " for worker %s",
1860               getpid(), worker->name);
1861     }
1862     worker->s = score;
1863     /*
1864      * recheck to see if we've already been here. Possible
1865      * if proxy is using scoreboard to hold shared stats
1866      */
1867     if (PROXY_WORKER_IS_INITIALIZED(worker)) {
1868         /* The worker share is already initialized */
1869         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1870               "proxy: worker %s already initialized",
1871               worker->name);
1872         return;
1873     }
1874     if (worker->route) {
1875         strcpy(worker->s->route, worker->route);
1876     }
1877     else {
1878         *worker->s->route = '\0';
1879     }
1880     if (worker->redirect) {
1881         strcpy(worker->s->redirect, worker->redirect);
1882     }
1883     else {
1884         *worker->s->redirect = '\0';
1885     }
1886
1887     worker->s->status |= (worker->status | PROXY_WORKER_INITIALIZED);
1888
1889 }
1890
1891 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
1892 {
1893     apr_status_t rv;
1894
1895 #if APR_HAS_THREADS
1896     int mpm_threads;
1897 #endif
1898
1899     if (worker->status & PROXY_WORKER_INITIALIZED) {
1900         /* The worker is already initialized */
1901         return APR_SUCCESS;
1902     }
1903
1904     /* Set default parameters */
1905     if (!worker->retry_set) {
1906         worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
1907     }
1908     /* By default address is reusable unless DisableReuse is set */
1909     if (worker->disablereuse) {
1910         worker->is_address_reusable = 0;
1911     }
1912     else {
1913         worker->is_address_reusable = 1;
1914     }
1915
1916 #if APR_HAS_THREADS
1917     ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1918     if (mpm_threads > 1) {
1919         /* Set hard max to no more then mpm_threads */
1920         if (worker->hmax == 0 || worker->hmax > mpm_threads) {
1921             worker->hmax = mpm_threads;
1922         }
1923         if (worker->smax == 0 || worker->smax > worker->hmax) {
1924             worker->smax = worker->hmax;
1925         }
1926         /* Set min to be lower then smax */
1927         if (worker->min > worker->smax) {
1928             worker->min = worker->smax;
1929         }
1930     }
1931     else {
1932         /* This will supress the apr_reslist creation */
1933         worker->min = worker->smax = worker->hmax = 0;
1934     }
1935     if (worker->hmax) {
1936         rv = apr_reslist_create(&(worker->cp->res),
1937                                 worker->min, worker->smax,
1938                                 worker->hmax, worker->ttl,
1939                                 connection_constructor, connection_destructor,
1940                                 worker, worker->cp->pool);
1941
1942         apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
1943                                   conn_pool_cleanup,
1944                                   apr_pool_cleanup_null);
1945
1946         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1947             "proxy: initialized worker %d in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
1948              worker->id, getpid(), worker->hostname, worker->min,
1949              worker->hmax, worker->smax);
1950
1951 #if (APR_MAJOR_VERSION > 0)
1952         /* Set the acquire timeout */
1953         if (rv == APR_SUCCESS && worker->acquire_set) {
1954             apr_reslist_timeout_set(worker->cp->res, worker->acquire);
1955         }
1956 #endif
1957     }
1958     else
1959 #endif
1960     {
1961         void *conn;
1962
1963         rv = connection_constructor(&conn, worker, worker->cp->pool);
1964         worker->cp->conn = conn;
1965
1966         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1967              "proxy: initialized single connection worker %d in child %" APR_PID_T_FMT " for (%s)",
1968              worker->id, getpid(), worker->hostname);
1969     }
1970     if (rv == APR_SUCCESS) {
1971         worker->status |= (PROXY_WORKER_INITIALIZED);
1972     }
1973     return rv;
1974 }
1975
1976 PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
1977                                          proxy_worker *worker,
1978                                          server_rec *s)
1979 {
1980     if (worker->s->status & PROXY_WORKER_IN_ERROR) {
1981         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1982                     "proxy: %s: retrying the worker for (%s)",
1983                      proxy_function, worker->hostname);
1984         if (apr_time_now() > worker->s->error_time + worker->retry) {
1985             ++worker->s->retries;
1986             worker->s->status &= ~PROXY_WORKER_IN_ERROR;
1987             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1988                          "proxy: %s: worker for (%s) has been marked for retry",
1989                          proxy_function, worker->hostname);
1990             return OK;
1991         }
1992         else {
1993             return DECLINED;
1994         }
1995     }
1996     else {
1997         return OK;
1998     }
1999 }
2000
2001 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
2002                                                proxy_conn_rec **conn,
2003                                                proxy_worker *worker,
2004                                                server_rec *s)
2005 {
2006     apr_status_t rv;
2007
2008     if (!PROXY_WORKER_IS_USABLE(worker)) {
2009         /* Retry the worker */
2010         ap_proxy_retry_worker(proxy_function, worker, s);
2011
2012         if (!PROXY_WORKER_IS_USABLE(worker)) {
2013             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
2014                          "proxy: %s: disabled connection for (%s)",
2015                          proxy_function, worker->hostname);
2016             return HTTP_SERVICE_UNAVAILABLE;
2017         }
2018     }
2019 #if APR_HAS_THREADS
2020     if (worker->hmax && worker->cp->res) {
2021         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
2022     }
2023     else
2024 #endif
2025     {
2026         /* create the new connection if the previous was destroyed */
2027         if (!worker->cp->conn) {
2028             connection_constructor((void **)conn, worker, worker->cp->pool);
2029         }
2030         else {
2031             *conn = worker->cp->conn;
2032             worker->cp->conn = NULL;
2033         }
2034         rv = APR_SUCCESS;
2035     }
2036
2037     if (rv != APR_SUCCESS) {
2038         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
2039                      "proxy: %s: failed to acquire connection for (%s)",
2040                      proxy_function, worker->hostname);
2041         return HTTP_SERVICE_UNAVAILABLE;
2042     }
2043     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2044                  "proxy: %s: has acquired connection for (%s)",
2045                  proxy_function, worker->hostname);
2046
2047     (*conn)->worker = worker;
2048     (*conn)->close  = 0;
2049 #if APR_HAS_THREADS
2050     (*conn)->inreslist = 0;
2051 #endif
2052
2053     return OK;
2054 }
2055
2056 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
2057                                                proxy_conn_rec *conn,
2058                                                server_rec *s)
2059 {
2060     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2061                  "proxy: %s: has released connection for (%s)",
2062                  proxy_function, conn->worker->hostname);
2063     connection_cleanup(conn);
2064
2065     return OK;
2066 }
2067
2068 PROXY_DECLARE(int)
2069 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
2070                               proxy_server_conf *conf,
2071                               proxy_worker *worker,
2072                               proxy_conn_rec *conn,
2073                               apr_uri_t *uri,
2074                               char **url,
2075                               const char *proxyname,
2076                               apr_port_t proxyport,
2077                               char *server_portstr,
2078                               int server_portstr_size)
2079 {
2080     int server_port;
2081     apr_status_t err = APR_SUCCESS;
2082     apr_status_t uerr = APR_SUCCESS;
2083
2084     conn->r = r;
2085
2086     /*
2087      * Break up the URL to determine the host to connect to
2088      */
2089
2090     /* we break the URL into host, port, uri */
2091     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
2092         return ap_proxyerror(r, HTTP_BAD_REQUEST,
2093                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
2094                                          NULL));
2095     }
2096     if (!uri->port) {
2097         uri->port = apr_uri_port_of_scheme(uri->scheme);
2098     }
2099
2100     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2101                  "proxy: connecting %s to %s:%d", *url, uri->hostname,
2102                  uri->port);
2103
2104     /*
2105      * allocate these out of the specified connection pool
2106      * The scheme handler decides if this is permanent or
2107      * short living pool.
2108      */
2109     /* are we connecting directly, or via a proxy? */
2110     if (!proxyname) {
2111         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
2112                            uri->query ? uri->query : "",
2113                            uri->fragment ? "#" : "",
2114                            uri->fragment ? uri->fragment : "", NULL);
2115     }
2116     /*
2117      * Make sure that we pick the the correct and valid worker.
2118      * If a single keepalive connection triggers different workers,
2119      * then we have a problem (we don't select the correct one).
2120      * Do an expensive check in this case, where we compare the
2121      * the hostnames associated between the two.
2122      *
2123      * TODO: Handle this much better...
2124      */
2125     if (!conn->hostname || !worker->is_address_reusable || 
2126          worker->disablereuse ||
2127          (r->connection->keepalives &&
2128          (r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE) &&
2129          (strcasecmp(conn->hostname, uri->hostname) != 0) ) ) {
2130         if (proxyname) {
2131             conn->hostname = apr_pstrdup(conn->pool, proxyname);
2132             conn->port = proxyport;
2133         }
2134         else {
2135             conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
2136             conn->port = uri->port;
2137         }
2138         socket_cleanup(conn);
2139         err = apr_sockaddr_info_get(&(conn->addr),
2140                                     conn->hostname, APR_UNSPEC,
2141                                     conn->port, 0,
2142                                     conn->pool);
2143     }
2144     else if (!worker->cp->addr) {
2145         if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
2146             ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server,
2147                          "proxy: lock");
2148             return HTTP_INTERNAL_SERVER_ERROR;
2149         }
2150
2151         /*
2152          * Worker can have the single constant backend adress.
2153          * The single DNS lookup is used once per worker.
2154          * If dynamic change is needed then set the addr to NULL
2155          * inside dynamic config to force the lookup.
2156          */
2157         err = apr_sockaddr_info_get(&(worker->cp->addr),
2158                                     conn->hostname, APR_UNSPEC,
2159                                     conn->port, 0,
2160                                     worker->cp->pool);
2161         conn->addr = worker->cp->addr;
2162         if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
2163             ap_log_error(APLOG_MARK, APLOG_ERR, uerr, r->server,
2164                          "proxy: unlock");
2165         }
2166     }
2167     else {
2168         conn->addr = worker->cp->addr;
2169     }
2170
2171     if (err != APR_SUCCESS) {
2172         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
2173                              apr_pstrcat(p, "DNS lookup failure for: ",
2174                                          conn->hostname, NULL));
2175     }
2176
2177     /* Get the server port for the Via headers */
2178     {
2179         server_port = ap_get_server_port(r);
2180         if (ap_is_default_port(server_port, r)) {
2181             strcpy(server_portstr,"");
2182         }
2183         else {
2184             apr_snprintf(server_portstr, server_portstr_size, ":%d",
2185                          server_port);
2186         }
2187     }
2188     /* check if ProxyBlock directive on this host */
2189     if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
2190         return ap_proxyerror(r, HTTP_FORBIDDEN,
2191                              "Connect to remote machine blocked");
2192     }
2193     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2194                  "proxy: connected %s to %s:%d", *url, conn->hostname,
2195                  conn->port);
2196     return OK;
2197 }
2198
2199 #define USE_ALTERNATE_IS_CONNECTED 1
2200
2201 #if !defined(APR_MSG_PEEK) && defined(MSG_PEEK)
2202 #define APR_MSG_PEEK MSG_PEEK
2203 #endif
2204
2205 #if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK)
2206 static int is_socket_connected(apr_socket_t *socket)
2207 {
2208     apr_pollfd_t pfds[1];
2209     apr_status_t status;
2210     apr_int32_t  nfds;
2211
2212     pfds[0].reqevents = APR_POLLIN;
2213     pfds[0].desc_type = APR_POLL_SOCKET;
2214     pfds[0].desc.s = socket;
2215
2216     do {
2217         status = apr_poll(&pfds[0], 1, &nfds, 0);
2218     } while (APR_STATUS_IS_EINTR(status));
2219
2220     if (status == APR_SUCCESS && nfds == 1 &&
2221         pfds[0].rtnevents == APR_POLLIN) {
2222         apr_sockaddr_t unused;
2223         apr_size_t len = 1;
2224         char buf[1];
2225         /* The socket might be closed in which case
2226          * the poll will return POLLIN.
2227          * If there is no data available the socket
2228          * is closed.
2229          */
2230         status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK,
2231                                      &buf[0], &len);
2232         if (status == APR_SUCCESS && len)
2233             return 1;
2234         else
2235             return 0;
2236     }
2237     else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2238         return 1;
2239     }
2240     return 0;
2241
2242 }
2243 #else
2244 static int is_socket_connected(apr_socket_t *sock)
2245
2246 {
2247     apr_size_t buffer_len = 1;
2248     char test_buffer[1];
2249     apr_status_t socket_status;
2250     apr_interval_time_t current_timeout;
2251
2252     /* save timeout */
2253     apr_socket_timeout_get(sock, &current_timeout);
2254     /* set no timeout */
2255     apr_socket_timeout_set(sock, 0);
2256     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
2257     /* put back old timeout */
2258     apr_socket_timeout_set(sock, current_timeout);
2259     if (APR_STATUS_IS_EOF(socket_status)
2260         || APR_STATUS_IS_ECONNRESET(socket_status)) {
2261         return 0;
2262     }
2263     else {
2264         return 1;
2265     }
2266 }
2267 #endif /* USE_ALTERNATE_IS_CONNECTED */
2268
2269 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
2270                                             proxy_conn_rec *conn,
2271                                             proxy_worker *worker,
2272                                             server_rec *s)
2273 {
2274     apr_status_t rv;
2275     int connected = 0;
2276     int loglevel;
2277     apr_sockaddr_t *backend_addr = conn->addr;
2278     apr_socket_t *newsock;
2279     void *sconf = s->module_config;
2280     proxy_server_conf *conf =
2281         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
2282
2283     if (conn->sock) {
2284         if (!(connected = is_socket_connected(conn->sock))) {
2285             socket_cleanup(conn);
2286             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2287                          "proxy: %s: backend socket is disconnected.",
2288                          proxy_function);
2289         }
2290     }
2291     while (backend_addr && !connected) {
2292         if ((rv = apr_socket_create(&newsock, backend_addr->family,
2293                                 SOCK_STREAM, APR_PROTO_TCP,
2294                                 conn->scpool)) != APR_SUCCESS) {
2295             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2296             ap_log_error(APLOG_MARK, loglevel, rv, s,
2297                          "proxy: %s: error creating fam %d socket for target %s",
2298                          proxy_function,
2299                          backend_addr->family,
2300                          worker->hostname);
2301             /*
2302              * this could be an IPv6 address from the DNS but the
2303              * local machine won't give us an IPv6 socket; hopefully the
2304              * DNS returned an additional address to try
2305              */
2306             backend_addr = backend_addr->next;
2307             continue;
2308         }
2309         conn->connection = NULL;
2310
2311 #if !defined(TPF) && !defined(BEOS)
2312         if (worker->recv_buffer_size > 0 &&
2313             (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
2314                                      worker->recv_buffer_size))) {
2315             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
2316                          "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2317                          "ProxyReceiveBufferSize, using default");
2318         }
2319 #endif
2320
2321         rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1);
2322         if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2323              ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
2324                           "apr_socket_opt_set(APR_TCP_NODELAY): "
2325                           "Failed to set");
2326         }
2327
2328         /* Set a timeout for connecting to the backend on the socket */
2329         if (worker->conn_timeout_set) {
2330             apr_socket_timeout_set(newsock, worker->conn_timeout);
2331         }
2332         else if (worker->timeout_set == 1) {
2333             apr_socket_timeout_set(newsock, worker->timeout);
2334         }
2335         else if (conf->timeout_set == 1) {
2336             apr_socket_timeout_set(newsock, conf->timeout);
2337         }
2338         else {
2339              apr_socket_timeout_set(newsock, s->timeout);
2340         }
2341         /* Set a keepalive option */
2342         if (worker->keepalive) {
2343             if ((rv = apr_socket_opt_set(newsock,
2344                             APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
2345                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
2346                              "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
2347                              " Keepalive");
2348             }
2349         }
2350         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2351                      "proxy: %s: fam %d socket created to connect to %s",
2352                      proxy_function, backend_addr->family, worker->hostname);
2353
2354         /* make the connection out of the socket */
2355         rv = apr_socket_connect(newsock, backend_addr);
2356
2357         /* if an error occurred, loop round and try again */
2358         if (rv != APR_SUCCESS) {
2359             apr_socket_close(newsock);
2360             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2361             ap_log_error(APLOG_MARK, loglevel, rv, s,
2362                          "proxy: %s: attempt to connect to %pI (%s) failed",
2363                          proxy_function,
2364                          backend_addr,
2365                          worker->hostname);
2366             backend_addr = backend_addr->next;
2367             continue;
2368         }
2369
2370         /* Set a timeout on the socket */
2371         if (worker->timeout_set == 1) {
2372             apr_socket_timeout_set(newsock, worker->timeout);
2373         }
2374         else if (conf->timeout_set == 1) {
2375             apr_socket_timeout_set(newsock, conf->timeout);
2376         }
2377         else {
2378              apr_socket_timeout_set(newsock, s->timeout);
2379         }
2380
2381         conn->sock   = newsock;
2382         connected    = 1;
2383     }
2384     /*
2385      * Put the entire worker to error state if
2386      * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
2387      * Altrough some connections may be alive
2388      * no further connections to the worker could be made
2389      */
2390     if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
2391         !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
2392         worker->s->status |= PROXY_WORKER_IN_ERROR;
2393         worker->s->error_time = apr_time_now();
2394         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
2395             "ap_proxy_connect_backend disabling worker for (%s)",
2396             worker->hostname);
2397     }
2398     else {
2399         worker->s->error_time = 0;
2400         worker->s->retries = 0;
2401     }
2402     return connected ? OK : DECLINED;
2403 }
2404
2405 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
2406                                               proxy_conn_rec *conn,
2407                                               conn_rec *c,
2408                                               server_rec *s)
2409 {
2410     apr_sockaddr_t *backend_addr = conn->addr;
2411     int rc;
2412     apr_interval_time_t current_timeout;
2413     apr_bucket_alloc_t *bucket_alloc;
2414
2415     if (conn->connection) {
2416         return OK;
2417     }
2418
2419     /*
2420      * We need to flush the buckets before we return the connection to the
2421      * connection pool. See comment in connection_cleanup for why this is
2422      * needed.
2423      */
2424     conn->need_flush = 1;
2425     bucket_alloc = apr_bucket_alloc_create(conn->scpool);
2426     /*
2427      * The socket is now open, create a new backend server connection
2428      */
2429     conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock,
2430                                                 0, NULL,
2431                                                 bucket_alloc);
2432
2433     if (!conn->connection) {
2434         /*
2435          * the peer reset the connection already; ap_run_create_connection()
2436          * closed the socket
2437          */
2438         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
2439                      s, "proxy: %s: an error occurred creating a "
2440                      "new connection to %pI (%s)", proxy_function,
2441                      backend_addr, conn->hostname);
2442         /* XXX: Will be closed when proxy_conn is closed */
2443         socket_cleanup(conn);
2444         return HTTP_INTERNAL_SERVER_ERROR;
2445     }
2446
2447     /* For ssl connection to backend */
2448     if (conn->is_ssl) {
2449         if (!ap_proxy_ssl_enable(conn->connection)) {
2450             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
2451                          s, "proxy: %s: failed to enable ssl support "
2452                          "for %pI (%s)", proxy_function,
2453                          backend_addr, conn->hostname);
2454             return HTTP_INTERNAL_SERVER_ERROR;
2455         }
2456     }
2457     else {
2458         /* TODO: See if this will break FTP */
2459         ap_proxy_ssl_disable(conn->connection);
2460     }
2461
2462     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2463                  "proxy: %s: connection complete to %pI (%s)",
2464                  proxy_function, backend_addr, conn->hostname);
2465
2466     /*
2467      * save the timeout of the socket because core_pre_connection
2468      * will set it to base_server->timeout
2469      * (core TimeOut directive).
2470      */
2471     apr_socket_timeout_get(conn->sock, &current_timeout);
2472     /* set up the connection filters */
2473     rc = ap_run_pre_connection(conn->connection, conn->sock);
2474     if (rc != OK && rc != DONE) {
2475         conn->connection->aborted = 1;
2476         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2477                      "proxy: %s: pre_connection setup failed (%d)",
2478                      proxy_function, rc);
2479         return rc;
2480     }
2481     apr_socket_timeout_set(conn->sock, current_timeout);
2482
2483     return OK;
2484 }
2485
2486 int ap_proxy_lb_workers(void)
2487 {
2488     /*
2489      * Since we can't resize the scoreboard when reconfiguring, we
2490      * have to impose a limit on the number of workers, we are
2491      * able to reconfigure to.
2492      */
2493     if (!lb_workers_limit)
2494         lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
2495     return lb_workers_limit;
2496 }
2497
2498 PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
2499                                            apr_bucket_brigade *brigade)
2500 {
2501     apr_bucket *e;
2502     conn_rec *c = r->connection;
2503
2504     r->no_cache = 1;
2505     /*
2506      * If this is a subrequest, then prevent also caching of the main
2507      * request.
2508      */
2509     if (r->main)
2510         r->main->no_cache = 1;
2511     e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool,
2512                                c->bucket_alloc);
2513     APR_BRIGADE_INSERT_TAIL(brigade, e);
2514     e = apr_bucket_eos_create(c->bucket_alloc);
2515     APR_BRIGADE_INSERT_TAIL(brigade, e);
2516 }