]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
c6b03fd3f76db3a1e2e682ff4a7dccffbbf56de3
[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 #include "apr_hash.h"
23 #include "proxy_util.h"
24 #include "ajp.h"
25 #include "scgi.h"
26
27 #if APR_HAVE_UNISTD_H
28 #include <unistd.h>         /* for getpid() */
29 #endif
30
31 #if (APR_MAJOR_VERSION < 1)
32 #undef apr_socket_create
33 #define apr_socket_create apr_socket_create_ex
34 #endif
35
36 #if APR_HAVE_SYS_UN_H
37 #include <sys/un.h>
38 #endif
39 #if (APR_MAJOR_VERSION < 2)
40 #include "apr_support.h"        /* for apr_wait_for_io_or_timeout() */
41 #endif
42
43 APLOG_USE_MODULE(proxy);
44
45 /*
46  * Opaque structure containing target server info when
47  * using a forward proxy.
48  * Up to now only used in combination with HTTP CONNECT.
49  */
50 typedef struct {
51     int          use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */
52     const char   *target_host;     /* Target hostname */
53     apr_port_t   target_port;      /* Target port */
54     const char   *proxy_auth;      /* Proxy authorization */
55 } forward_info;
56
57 /* Global balancer counter */
58 int PROXY_DECLARE_DATA proxy_lb_workers = 0;
59 static int lb_workers_limit = 0;
60 const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_path;
61 const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_domain;
62
63 extern apr_global_mutex_t *proxy_mutex;
64
65 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
66 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
67 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
68 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
69
70 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
71                                    (request_rec *r, request_rec *pr), (r, pr),
72                                    OK, DECLINED)
73
74 PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src,
75                                              apr_size_t dlen)
76 {
77     char *thenil;
78     apr_size_t thelen;
79
80     /* special case handling */
81     if (!dlen) {
82         /* XXX: APR_ENOSPACE would be better */
83         return APR_EGENERAL;
84     }
85     if (!src) {
86         *dst = '\0';
87         return APR_SUCCESS;
88     }
89     thenil = apr_cpystrn(dst, src, dlen);
90     thelen = thenil - dst;
91     if (src[thelen] == '\0') {
92         return APR_SUCCESS;
93     }
94     return APR_EGENERAL;
95 }
96
97 /* already called in the knowledge that the characters are hex digits */
98 PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
99 {
100     int i;
101
102 #if !APR_CHARSET_EBCDIC
103     int ch = x[0];
104
105     if (apr_isdigit(ch)) {
106         i = ch - '0';
107     }
108     else if (apr_isupper(ch)) {
109         i = ch - ('A' - 10);
110     }
111     else {
112         i = ch - ('a' - 10);
113     }
114     i <<= 4;
115
116     ch = x[1];
117     if (apr_isdigit(ch)) {
118         i += ch - '0';
119     }
120     else if (apr_isupper(ch)) {
121         i += ch - ('A' - 10);
122     }
123     else {
124         i += ch - ('a' - 10);
125     }
126     return i;
127 #else /*APR_CHARSET_EBCDIC*/
128     /*
129      * we assume that the hex value refers to an ASCII character
130      * so convert to EBCDIC so that it makes sense locally;
131      *
132      * example:
133      *
134      * client specifies %20 in URL to refer to a space char;
135      * at this point we're called with EBCDIC "20"; after turning
136      * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
137      * represents an ASCII char and convert 0x20 to EBCDIC, yielding
138      * 0x40
139      */
140     char buf[1];
141
142     if (1 == sscanf(x, "%2x", &i)) {
143         buf[0] = i & 0xFF;
144         ap_xlate_proto_from_ascii(buf, 1);
145         return buf[0];
146     }
147     else {
148         return 0;
149     }
150 #endif /*APR_CHARSET_EBCDIC*/
151 }
152
153 PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
154 {
155 #if !APR_CHARSET_EBCDIC
156     int i;
157
158     x[0] = '%';
159     i = (ch & 0xF0) >> 4;
160     if (i >= 10) {
161         x[1] = ('A' - 10) + i;
162     }
163     else {
164         x[1] = '0' + i;
165     }
166
167     i = ch & 0x0F;
168     if (i >= 10) {
169         x[2] = ('A' - 10) + i;
170     }
171     else {
172         x[2] = '0' + i;
173     }
174 #else /*APR_CHARSET_EBCDIC*/
175     static const char ntoa[] = { "0123456789ABCDEF" };
176     char buf[1];
177
178     ch &= 0xFF;
179
180     buf[0] = ch;
181     ap_xlate_proto_to_ascii(buf, 1);
182
183     x[0] = '%';
184     x[1] = ntoa[(buf[0] >> 4) & 0x0F];
185     x[2] = ntoa[buf[0] & 0x0F];
186     x[3] = '\0';
187 #endif /*APR_CHARSET_EBCDIC*/
188 }
189
190 /*
191  * canonicalise a URL-encoded string
192  */
193
194 /*
195  * Convert a URL-encoded string to canonical form.
196  * It decodes characters which need not be encoded,
197  * and encodes those which must be encoded, and does not touch
198  * those which must not be touched.
199  */
200 PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len,
201                                        enum enctype t, int forcedec,
202                                        int proxyreq)
203 {
204     int i, j, ch;
205     char *y;
206     char *allowed;  /* characters which should not be encoded */
207     char *reserved; /* characters which much not be en/de-coded */
208
209 /*
210  * N.B. in addition to :@&=, this allows ';' in an http path
211  * and '?' in an ftp path -- this may be revised
212  *
213  * Also, it makes a '+' character in a search string reserved, as
214  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
215  * it only permits ; / ? : @ = & as reserved chars.)
216  */
217     if (t == enc_path) {
218         allowed = "~$-_.+!*'(),;:@&=";
219     }
220     else if (t == enc_search) {
221         allowed = "$-_.!*'(),;:@&=";
222     }
223     else if (t == enc_user) {
224         allowed = "$-_.+!*'(),;@&=";
225     }
226     else if (t == enc_fpath) {
227         allowed = "$-_.+!*'(),?:@&=";
228     }
229     else {            /* if (t == enc_parm) */
230         allowed = "$-_.+!*'(),?/:@&=";
231     }
232
233     if (t == enc_path) {
234         reserved = "/";
235     }
236     else if (t == enc_search) {
237         reserved = "+";
238     }
239     else {
240         reserved = "";
241     }
242
243     y = apr_palloc(p, 3 * len + 1);
244
245     for (i = 0, j = 0; i < len; i++, j++) {
246 /* always handle '/' first */
247         ch = x[i];
248         if (strchr(reserved, ch)) {
249             y[j] = ch;
250             continue;
251         }
252 /*
253  * decode it if not already done. do not decode reverse proxied URLs
254  * unless specifically forced
255  */
256         if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
257             if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) {
258                 return NULL;
259             }
260             ch = ap_proxy_hex2c(&x[i + 1]);
261             i += 2;
262             if (ch != 0 && strchr(reserved, ch)) {  /* keep it encoded */
263                 ap_proxy_c2hex(ch, &y[j]);
264                 j += 2;
265                 continue;
266             }
267         }
268 /* recode it, if necessary */
269         if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
270             ap_proxy_c2hex(ch, &y[j]);
271             j += 2;
272         }
273         else {
274             y[j] = ch;
275         }
276     }
277     y[j] = '\0';
278     return y;
279 }
280
281 /*
282  * Parses network-location.
283  *    urlp           on input the URL; on output the path, after the leading /
284  *    user           NULL if no user/password permitted
285  *    password       holder for password
286  *    host           holder for host
287  *    port           port number; only set if one is supplied.
288  *
289  * Returns an error string.
290  */
291 PROXY_DECLARE(char *)
292      ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
293             char **passwordp, char **hostp, apr_port_t *port)
294 {
295     char *addr, *scope_id, *strp, *host, *url = *urlp;
296     char *user = NULL, *password = NULL;
297     apr_port_t tmp_port;
298     apr_status_t rv;
299
300     if (url[0] != '/' || url[1] != '/') {
301         return "Malformed URL";
302     }
303     host = url + 2;
304     url = strchr(host, '/');
305     if (url == NULL) {
306         url = "";
307     }
308     else {
309         *(url++) = '\0';    /* skip separating '/' */
310     }
311
312     /* find _last_ '@' since it might occur in user/password part */
313     strp = strrchr(host, '@');
314
315     if (strp != NULL) {
316         *strp = '\0';
317         user = host;
318         host = strp + 1;
319
320 /* find password */
321         strp = strchr(user, ':');
322         if (strp != NULL) {
323             *strp = '\0';
324             password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
325             if (password == NULL) {
326                 return "Bad %-escape in URL (password)";
327             }
328         }
329
330         user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
331         if (user == NULL) {
332             return "Bad %-escape in URL (username)";
333         }
334     }
335     if (userp != NULL) {
336         *userp = user;
337     }
338     if (passwordp != NULL) {
339         *passwordp = password;
340     }
341
342     /*
343      * Parse the host string to separate host portion from optional port.
344      * Perform range checking on port.
345      */
346     rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
347     if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
348         return "Invalid host/port";
349     }
350     if (tmp_port != 0) { /* only update caller's port if port was specified */
351         *port = tmp_port;
352     }
353
354     ap_str_tolower(addr); /* DNS names are case-insensitive */
355
356     *urlp = url;
357     *hostp = addr;
358
359     return NULL;
360 }
361
362 PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
363 {
364     const char *uri = ap_escape_html(r->pool, r->uri);
365     apr_table_setn(r->notes, "error-notes",
366         apr_pstrcat(r->pool,
367             "The proxy server could not handle the request <em><a href=\"",
368             uri, "\">", ap_escape_html(r->pool, r->method), "&nbsp;", uri,
369             "</a></em>.<p>\n"
370             "Reason: <strong>", ap_escape_html(r->pool, message),
371             "</strong></p>",
372             NULL));
373
374     /* Allow "error-notes" string to be printed by ap_send_error_response() */
375     apr_table_setn(r->notes, "verbose-error-to", "*");
376
377     r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
378     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00898) "%s returned by %s", message,
379                   r->uri);
380     return statuscode;
381 }
382
383 static const char *
384      proxy_get_host_of_request(request_rec *r)
385 {
386     char *url, *user = NULL, *password = NULL, *err, *host = NULL;
387     apr_port_t port;
388
389     if (r->hostname != NULL) {
390         return r->hostname;
391     }
392
393     /* Set url to the first char after "scheme://" */
394     if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') {
395         return NULL;
396     }
397
398     url = apr_pstrdup(r->pool, &url[1]);    /* make it point to "//", which is what proxy_canon_netloc expects */
399
400     err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
401
402     if (err != NULL) {
403         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00899) "%s", err);
404     }
405
406     r->hostname = host;
407
408     return host;        /* ought to return the port, too */
409 }
410
411 /* Return TRUE if addr represents an IP address (or an IP network address) */
412 PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
413 {
414     const char *addr = This->name;
415     long ip_addr[4];
416     int i, quads;
417     long bits;
418
419     /*
420      * if the address is given with an explicit netmask, use that
421      * Due to a deficiency in apr_inet_addr(), it is impossible to parse
422      * "partial" addresses (with less than 4 quads) correctly, i.e.
423      * 192.168.123 is parsed as 192.168.0.123, which is not what I want.
424      * I therefore have to parse the IP address manually:
425      * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0)
426      * addr and mask were set by proxy_readmask()
427      * return 1;
428      */
429
430     /*
431      * Parse IP addr manually, optionally allowing
432      * abbreviated net addresses like 192.168.
433      */
434
435     /* Iterate over up to 4 (dotted) quads. */
436     for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
437         char *tmp;
438
439         if (*addr == '/' && quads > 0) {  /* netmask starts here. */
440             break;
441         }
442
443         if (!apr_isdigit(*addr)) {
444             return 0;       /* no digit at start of quad */
445         }
446
447         ip_addr[quads] = strtol(addr, &tmp, 0);
448
449         if (tmp == addr) {  /* expected a digit, found something else */
450             return 0;
451         }
452
453         if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
454             /* invalid octet */
455             return 0;
456         }
457
458         addr = tmp;
459
460         if (*addr == '.' && quads != 3) {
461             ++addr;     /* after the 4th quad, a dot would be illegal */
462         }
463     }
464
465     for (This->addr.s_addr = 0, i = 0; i < quads; ++i) {
466         This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
467     }
468
469     if (addr[0] == '/' && apr_isdigit(addr[1])) {   /* net mask follows: */
470         char *tmp;
471
472         ++addr;
473
474         bits = strtol(addr, &tmp, 0);
475
476         if (tmp == addr) {   /* expected a digit, found something else */
477             return 0;
478         }
479
480         addr = tmp;
481
482         if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */
483             return 0;
484         }
485
486     }
487     else {
488         /*
489          * Determine (i.e., "guess") netmask by counting the
490          * number of trailing .0's; reduce #quads appropriately
491          * (so that 192.168.0.0 is equivalent to 192.168.)
492          */
493         while (quads > 0 && ip_addr[quads - 1] == 0) {
494             --quads;
495         }
496
497         /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
498         if (quads < 1) {
499             return 0;
500         }
501
502         /* every zero-byte counts as 8 zero-bits */
503         bits = 8 * quads;
504
505         if (bits != 32) {     /* no warning for fully qualified IP address */
506             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00900)
507                          "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
508                          inet_ntoa(This->addr), bits);
509         }
510     }
511
512     This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
513
514     if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
515         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00901)
516                      "Warning: NetMask and IP-Addr disagree in %s/%ld",
517                      inet_ntoa(This->addr), bits);
518         This->addr.s_addr &= This->mask.s_addr;
519         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00902)
520                      "         Set to %s/%ld", inet_ntoa(This->addr), bits);
521     }
522
523     if (*addr == '\0') {
524         This->matcher = proxy_match_ipaddr;
525         return 1;
526     }
527     else {
528         return (*addr == '\0'); /* okay iff we've parsed the whole string */
529     }
530 }
531
532 /* Return TRUE if addr represents an IP address (or an IP network address) */
533 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
534 {
535     int i, ip_addr[4];
536     struct in_addr addr, *ip;
537     const char *host = proxy_get_host_of_request(r);
538
539     if (host == NULL) {   /* oops! */
540        return 0;
541     }
542
543     memset(&addr, '\0', sizeof addr);
544     memset(ip_addr, '\0', sizeof ip_addr);
545
546     if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
547         for (addr.s_addr = 0, i = 0; i < 4; ++i) {
548             /* ap_proxy_is_ipaddr() already confirmed that we have
549              * a valid octet in ip_addr[i]
550              */
551             addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
552         }
553
554         if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
555 #if DEBUGGING
556             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00903)
557                          "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
558             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00904)
559                          "%s/", inet_ntoa(This->addr));
560             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00905)
561                          "%s", inet_ntoa(This->mask));
562 #endif
563             return 1;
564         }
565 #if DEBUGGING
566         else {
567             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00906)
568                          "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
569             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00907)
570                          "%s/", inet_ntoa(This->addr));
571             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00908)
572                          "%s", inet_ntoa(This->mask));
573         }
574 #endif
575     }
576     else {
577         struct apr_sockaddr_t *reqaddr;
578
579         if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
580             != APR_SUCCESS) {
581 #if DEBUGGING
582             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00909)
583              "2)IP-NoMatch: hostname=%s msg=Host not found", host);
584 #endif
585             return 0;
586         }
587
588         /* Try to deal with multiple IP addr's for a host */
589         /* FIXME: This needs to be able to deal with IPv6 */
590         while (reqaddr) {
591             ip = (struct in_addr *) reqaddr->ipaddr_ptr;
592             if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
593 #if DEBUGGING
594                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00910)
595                              "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
596                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00911)
597                              "%s/", inet_ntoa(This->addr));
598                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00912)
599                              "%s", inet_ntoa(This->mask));
600 #endif
601                 return 1;
602             }
603 #if DEBUGGING
604             else {
605                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00913)
606                              "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
607                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00914)
608                              "%s/", inet_ntoa(This->addr));
609                 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00915)
610                              "%s", inet_ntoa(This->mask));
611             }
612 #endif
613             reqaddr = reqaddr->next;
614         }
615     }
616
617     return 0;
618 }
619
620 /* Return TRUE if addr represents a domain name */
621 PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
622 {
623     char *addr = This->name;
624     int i;
625
626     /* Domain name must start with a '.' */
627     if (addr[0] != '.') {
628         return 0;
629     }
630
631     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
632     for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) {
633         continue;
634     }
635
636 #if 0
637     if (addr[i] == ':') {
638     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03234)
639                      "@@@@ handle optional port in proxy_is_domainname()");
640     /* @@@@ handle optional port */
641     }
642 #endif
643
644     if (addr[i] != '\0') {
645         return 0;
646     }
647
648     /* Strip trailing dots */
649     for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) {
650         addr[i] = '\0';
651     }
652
653     This->matcher = proxy_match_domainname;
654     return 1;
655 }
656
657 /* Return TRUE if host "host" is in domain "domain" */
658 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
659 {
660     const char *host = proxy_get_host_of_request(r);
661     int d_len = strlen(This->name), h_len;
662
663     if (host == NULL) {      /* some error was logged already */
664         return 0;
665     }
666
667     h_len = strlen(host);
668
669     /* @@@ do this within the setup? */
670     /* Ignore trailing dots in domain comparison: */
671     while (d_len > 0 && This->name[d_len - 1] == '.') {
672         --d_len;
673     }
674     while (h_len > 0 && host[h_len - 1] == '.') {
675         --h_len;
676     }
677     return h_len > d_len
678         && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
679 }
680
681 /* Return TRUE if host represents a host name */
682 PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
683 {
684     struct apr_sockaddr_t *addr;
685     char *host = This->name;
686     int i;
687
688     /* Host names must not start with a '.' */
689     if (host[0] == '.') {
690         return 0;
691     }
692     /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
693     for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
694
695     if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) {
696         return 0;
697     }
698
699     This->hostaddr = addr;
700
701     /* Strip trailing dots */
702     for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) {
703         host[i] = '\0';
704     }
705
706     This->matcher = proxy_match_hostname;
707     return 1;
708 }
709
710 /* Return TRUE if host "host" is equal to host2 "host2" */
711 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
712 {
713     char *host = This->name;
714     const char *host2 = proxy_get_host_of_request(r);
715     int h2_len;
716     int h1_len;
717
718     if (host == NULL || host2 == NULL) {
719         return 0; /* oops! */
720     }
721
722     h2_len = strlen(host2);
723     h1_len = strlen(host);
724
725 #if 0
726     struct apr_sockaddr_t *addr = *This->hostaddr;
727
728     /* Try to deal with multiple IP addr's for a host */
729     while (addr) {
730         if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
731             return 1;
732         addr = addr->next;
733     }
734 #endif
735
736     /* Ignore trailing dots in host2 comparison: */
737     while (h2_len > 0 && host2[h2_len - 1] == '.') {
738         --h2_len;
739     }
740     while (h1_len > 0 && host[h1_len - 1] == '.') {
741         --h1_len;
742     }
743     return h1_len == h2_len
744         && strncasecmp(host, host2, h1_len) == 0;
745 }
746
747 /* Return TRUE if addr is to be matched as a word */
748 PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
749 {
750     This->matcher = proxy_match_word;
751     return 1;
752 }
753
754 /* Return TRUE if string "str2" occurs literally in "str1" */
755 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
756 {
757     const char *host = proxy_get_host_of_request(r);
758     return host != NULL && ap_strstr_c(host, This->name) != NULL;
759 }
760
761 #define MAX_IP_STR_LEN (46)
762
763 PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
764                                             const char *hostname, apr_sockaddr_t *addr)
765 {
766     int j;
767
768     /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
769     for (j = 0; j < conf->noproxies->nelts; j++) {
770         struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
771         struct apr_sockaddr_t *conf_addr;
772
773         ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
774                       "checking remote machine [%s] against [%s]",
775                       hostname, npent[j].name);
776         if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') {
777             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00916)
778                           "connect to remote machine %s blocked: name %s "
779                           "matched", hostname, npent[j].name);
780             return HTTP_FORBIDDEN;
781         }
782
783         /* No IP address checks if no IP address was passed in,
784          * i.e. the forward address proxy case, where this server does
785          * not resolve the hostname.  */
786         if (!addr)
787             continue;
788
789         for (conf_addr = npent[j].addr; conf_addr; conf_addr = conf_addr->next) {
790             char caddr[MAX_IP_STR_LEN], uaddr[MAX_IP_STR_LEN];
791             apr_sockaddr_t *uri_addr;
792
793             if (apr_sockaddr_ip_getbuf(caddr, sizeof caddr, conf_addr))
794                 continue;
795
796             for (uri_addr = addr; uri_addr; uri_addr = uri_addr->next) {
797                 if (apr_sockaddr_ip_getbuf(uaddr, sizeof uaddr, uri_addr))
798                     continue;
799                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
800                               "ProxyBlock comparing %s and %s", caddr, uaddr);
801                 if (!strcmp(caddr, uaddr)) {
802                     ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00917)
803                                   "connect to remote machine %s blocked: "
804                                   "IP %s matched", hostname, caddr);
805                     return HTTP_FORBIDDEN;
806                 }
807             }
808         }
809     }
810
811     return OK;
812 }
813
814 /* set up the minimal filter set */
815 PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
816 {
817     ap_add_input_filter("HTTP_IN", NULL, r, c);
818     return OK;
819 }
820
821 PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
822                               proxy_dir_conf *conf, const char *url)
823 {
824     proxy_req_conf *rconf;
825     struct proxy_alias *ent;
826     int i, l1, l2;
827     char *u;
828
829     /*
830      * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
831      * after the hostname
832      * XXX FIXME: Ensure the /uri component is a case sensitive match
833      */
834     if (r->proxyreq != PROXYREQ_REVERSE) {
835         return url;
836     }
837
838     l1 = strlen(url);
839     if (conf->interpolate_env == 1) {
840         rconf = ap_get_module_config(r->request_config, &proxy_module);
841         ent = (struct proxy_alias *)rconf->raliases->elts;
842     }
843     else {
844         ent = (struct proxy_alias *)conf->raliases->elts;
845     }
846     for (i = 0; i < conf->raliases->nelts; i++) {
847         proxy_server_conf *sconf = (proxy_server_conf *)
848             ap_get_module_config(r->server->module_config, &proxy_module);
849         proxy_balancer *balancer;
850         const char *real = ent[i].real;
851         /*
852          * First check if mapping against a balancer and see
853          * if we have such a entity. If so, then we need to
854          * find the particulars of the actual worker which may
855          * or may not be the right one... basically, we need
856          * to find which member actually handled this request.
857          */
858         if (ap_proxy_valid_balancer_name((char *)real, 0) &&
859             (balancer = ap_proxy_get_balancer(r->pool, sconf, real, 1))) {
860             int n, l3 = 0;
861             proxy_worker **worker = (proxy_worker **)balancer->workers->elts;
862             const char *urlpart = ap_strchr_c(real + sizeof(BALANCER_PREFIX) - 1, '/');
863             if (urlpart) {
864                 if (!urlpart[1])
865                     urlpart = NULL;
866                 else
867                     l3 = strlen(urlpart);
868             }
869             /* The balancer comparison is a bit trickier.  Given the context
870              *   BalancerMember balancer://alias http://example.com/foo
871              *   ProxyPassReverse /bash balancer://alias/bar
872              * translate url http://example.com/foo/bar/that to /bash/that
873              */
874             for (n = 0; n < balancer->workers->nelts; n++) {
875                 l2 = strlen((*worker)->s->name);
876                 if (urlpart) {
877                     /* urlpart (l3) assuredly starts with its own '/' */
878                     if ((*worker)->s->name[l2 - 1] == '/')
879                         --l2;
880                     if (l1 >= l2 + l3
881                             && strncasecmp((*worker)->s->name, url, l2) == 0
882                             && strncmp(urlpart, url + l2, l3) == 0) {
883                         u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3],
884                                         NULL);
885                         return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
886                     }
887                 }
888                 else if (l1 >= l2 && strncasecmp((*worker)->s->name, url, l2) == 0) {
889                     /* edge case where fake is just "/"... avoid double slash */
890                     if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) {
891                         u = apr_pstrdup(r->pool, &url[l2]);
892                     } else {
893                         u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
894                     }
895                     return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
896                 }
897                 worker++;
898             }
899         }
900         else {
901             const char *part = url;
902             l2 = strlen(real);
903             if (real[0] == '/') {
904                 part = ap_strstr_c(url, "://");
905                 if (part) {
906                     part = ap_strchr_c(part+3, '/');
907                     if (part) {
908                         l1 = strlen(part);
909                     }
910                     else {
911                         part = url;
912                     }
913                 }
914                 else {
915                     part = url;
916                 }
917             }
918             if (l2 > 0 && l1 >= l2 && strncasecmp(real, part, l2) == 0) {
919                 u = apr_pstrcat(r->pool, ent[i].fake, &part[l2], NULL);
920                 return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
921             }
922         }
923     }
924
925     return url;
926 }
927
928 /*
929  * Cookies are a bit trickier to match: we've got two substrings to worry
930  * about, and we can't just find them with strstr 'cos of case.  Regexp
931  * matching would be an easy fix, but for better consistency with all the
932  * other matches we'll refrain and use apr_strmatch to find path=/domain=
933  * and stick to plain strings for the config values.
934  */
935 PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
936                               proxy_dir_conf *conf, const char *str)
937 {
938     proxy_req_conf *rconf = ap_get_module_config(r->request_config,
939                                                  &proxy_module);
940     struct proxy_alias *ent;
941     apr_size_t len = strlen(str);
942     const char *newpath = NULL;
943     const char *newdomain = NULL;
944     const char *pathp;
945     const char *domainp;
946     const char *pathe = NULL;
947     const char *domaine = NULL;
948     apr_size_t l1, l2, poffs = 0, doffs = 0;
949     int i;
950     int ddiff = 0;
951     int pdiff = 0;
952     char *ret;
953
954     if (r->proxyreq != PROXYREQ_REVERSE) {
955         return str;
956     }
957
958    /*
959     * Find the match and replacement, but save replacing until we've done
960     * both path and domain so we know the new strlen
961     */
962     if ((pathp = apr_strmatch(ap_proxy_strmatch_path, str, len)) != NULL) {
963         pathp += 5;
964         poffs = pathp - str;
965         pathe = ap_strchr_c(pathp, ';');
966         l1 = pathe ? (pathe - pathp) : strlen(pathp);
967         pathe = pathp + l1 ;
968         if (conf->interpolate_env == 1) {
969             ent = (struct proxy_alias *)rconf->cookie_paths->elts;
970         }
971         else {
972             ent = (struct proxy_alias *)conf->cookie_paths->elts;
973         }
974         for (i = 0; i < conf->cookie_paths->nelts; i++) {
975             l2 = strlen(ent[i].fake);
976             if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
977                 newpath = ent[i].real;
978                 pdiff = strlen(newpath) - l1;
979                 break;
980             }
981         }
982     }
983
984     if ((domainp = apr_strmatch(ap_proxy_strmatch_domain, str, len)) != NULL) {
985         domainp += 7;
986         doffs = domainp - str;
987         domaine = ap_strchr_c(domainp, ';');
988         l1 = domaine ? (domaine - domainp) : strlen(domainp);
989         domaine = domainp + l1;
990         if (conf->interpolate_env == 1) {
991             ent = (struct proxy_alias *)rconf->cookie_domains->elts;
992         }
993         else {
994             ent = (struct proxy_alias *)conf->cookie_domains->elts;
995         }
996         for (i = 0; i < conf->cookie_domains->nelts; i++) {
997             l2 = strlen(ent[i].fake);
998             if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
999                 newdomain = ent[i].real;
1000                 ddiff = strlen(newdomain) - l1;
1001                 break;
1002             }
1003         }
1004     }
1005
1006     if (newpath) {
1007         ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1008         l1 = strlen(newpath);
1009         if (newdomain) {
1010             l2 = strlen(newdomain);
1011             if (doffs > poffs) {
1012                 memcpy(ret, str, poffs);
1013                 memcpy(ret + poffs, newpath, l1);
1014                 memcpy(ret + poffs + l1, pathe, domainp - pathe);
1015                 memcpy(ret + doffs + pdiff, newdomain, l2);
1016                 strcpy(ret + doffs + pdiff + l2, domaine);
1017             }
1018             else {
1019                 memcpy(ret, str, doffs) ;
1020                 memcpy(ret + doffs, newdomain, l2);
1021                 memcpy(ret + doffs + l2, domaine, pathp - domaine);
1022                 memcpy(ret + poffs + ddiff, newpath, l1);
1023                 strcpy(ret + poffs + ddiff + l1, pathe);
1024             }
1025         }
1026         else {
1027             memcpy(ret, str, poffs);
1028             memcpy(ret + poffs, newpath, l1);
1029             strcpy(ret + poffs + l1, pathe);
1030         }
1031     }
1032     else {
1033         if (newdomain) {
1034             ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
1035             l2 = strlen(newdomain);
1036             memcpy(ret, str, doffs);
1037             memcpy(ret + doffs, newdomain, l2);
1038             strcpy(ret + doffs+l2, domaine);
1039         }
1040         else {
1041             ret = (char *)str; /* no change */
1042         }
1043     }
1044
1045     return ret;
1046 }
1047
1048 /*
1049  * BALANCER related...
1050  */
1051
1052 /*
1053  * verifies that the balancer name conforms to standards.
1054  */
1055 PROXY_DECLARE(int) ap_proxy_valid_balancer_name(char *name, int i)
1056 {
1057     if (!i)
1058         i = sizeof(BALANCER_PREFIX)-1;
1059     return (!ap_casecmpstrn(name, BALANCER_PREFIX, i));
1060 }
1061
1062
1063 PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
1064                                                       proxy_server_conf *conf,
1065                                                       const char *url,
1066                                                       int care)
1067 {
1068     proxy_balancer *balancer;
1069     char *c, *uri = apr_pstrdup(p, url);
1070     int i;
1071     proxy_hashes hash;
1072
1073     c = strchr(uri, ':');
1074     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1075         return NULL;
1076     }
1077     /* remove path from uri */
1078     if ((c = strchr(c + 3, '/'))) {
1079         *c = '\0';
1080     }
1081     ap_str_tolower(uri);
1082     hash.def = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_DEFAULT);
1083     hash.fnv = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_FNV);
1084     balancer = (proxy_balancer *)conf->balancers->elts;
1085     for (i = 0; i < conf->balancers->nelts; i++) {
1086         if (balancer->hash.def == hash.def && balancer->hash.fnv == hash.fnv) {
1087             if (!care || !balancer->s->inactive) {
1088                 return balancer;
1089             }
1090         }
1091         balancer++;
1092     }
1093     return NULL;
1094 }
1095
1096
1097 PROXY_DECLARE(char *) ap_proxy_update_balancer(apr_pool_t *p,
1098                                                 proxy_balancer *balancer,
1099                                                 const char *url)
1100 {
1101     apr_uri_t puri;
1102     if (!url) {
1103         return NULL;
1104     }
1105     if (apr_uri_parse(p, url, &puri) != APR_SUCCESS) {
1106         return apr_psprintf(p, "unable to parse: %s", url);
1107     }
1108     if (puri.path && PROXY_STRNCPY(balancer->s->vpath, puri.path) != APR_SUCCESS) {
1109         return apr_psprintf(p, "balancer %s front-end virtual-path (%s) too long",
1110                             balancer->s->name, puri.path);
1111     }
1112     if (puri.hostname && PROXY_STRNCPY(balancer->s->vhost, puri.hostname) != APR_SUCCESS) {
1113         return apr_psprintf(p, "balancer %s front-end vhost name (%s) too long",
1114                             balancer->s->name, puri.hostname);
1115     }
1116     return NULL;
1117 }
1118
1119 #define PROXY_UNSET_NONCE '\n'
1120
1121 PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p,
1122                                                proxy_balancer **balancer,
1123                                                proxy_server_conf *conf,
1124                                                const char *url,
1125                                                const char *alias,
1126                                                int do_malloc)
1127 {
1128     proxy_balancer_method *lbmethod;
1129     proxy_balancer_shared *bshared;
1130     char *c, *q, *uri = apr_pstrdup(p, url);
1131     const char *sname;
1132
1133     /* We should never get here without a valid BALANCER_PREFIX... */
1134
1135     c = strchr(uri, ':');
1136     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
1137         return apr_psprintf(p, "Bad syntax for a balancer name (%s)", uri);
1138     /* remove path from uri */
1139     if ((q = strchr(c + 3, '/')))
1140         *q = '\0';
1141
1142     ap_str_tolower(uri);
1143     *balancer = apr_array_push(conf->balancers);
1144     memset(*balancer, 0, sizeof(proxy_balancer));
1145
1146     /*
1147      * NOTE: The default method is byrequests - if it doesn't
1148      * exist, that's OK at this time. We check when we share and sync
1149      */
1150     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
1151
1152     (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *));
1153     (*balancer)->gmutex = NULL;
1154     (*balancer)->tmutex = NULL;
1155     (*balancer)->lbmethod = lbmethod;
1156
1157     if (do_malloc)
1158         bshared = ap_malloc(sizeof(proxy_balancer_shared));
1159     else
1160         bshared = apr_palloc(p, sizeof(proxy_balancer_shared));
1161
1162     memset(bshared, 0, sizeof(proxy_balancer_shared));
1163
1164     bshared->was_malloced = (do_malloc != 0);
1165     PROXY_STRNCPY(bshared->lbpname, "byrequests");
1166     if (PROXY_STRNCPY(bshared->name, uri) != APR_SUCCESS) {
1167         return apr_psprintf(p, "balancer name (%s) too long", uri);
1168     }
1169     /*
1170      * We do the below for verification. The real sname will be
1171      * done post_config
1172      */
1173     ap_pstr2_alnum(p, bshared->name + sizeof(BALANCER_PREFIX) - 1,
1174                    &sname);
1175     sname = apr_pstrcat(p, conf->id, "_", sname, NULL);
1176     if (PROXY_STRNCPY(bshared->sname, sname) != APR_SUCCESS) {
1177         return apr_psprintf(p, "balancer safe-name (%s) too long", sname);
1178     }
1179     bshared->hash.def = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_DEFAULT);
1180     bshared->hash.fnv = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_FNV);
1181     (*balancer)->hash = bshared->hash;
1182
1183     bshared->forcerecovery = 1;
1184     bshared->sticky_separator = '.';
1185     *bshared->nonce = PROXY_UNSET_NONCE;  /* impossible valid input */
1186
1187     (*balancer)->s = bshared;
1188     (*balancer)->sconf = conf;
1189
1190     return ap_proxy_update_balancer(p, *balancer, alias);
1191 }
1192
1193 /*
1194  * Create an already defined balancer and free up memory.
1195  */
1196 PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer,
1197                                                     proxy_balancer_shared *shm,
1198                                                     int i)
1199 {
1200     apr_status_t rv = APR_SUCCESS;
1201     proxy_balancer_method *lbmethod;
1202     char *action = "copying";
1203     if (!shm || !balancer->s)
1204         return APR_EINVAL;
1205
1206     if ((balancer->s->hash.def != shm->hash.def) ||
1207         (balancer->s->hash.fnv != shm->hash.fnv)) {
1208         memcpy(shm, balancer->s, sizeof(proxy_balancer_shared));
1209         if (balancer->s->was_malloced)
1210             free(balancer->s);
1211     } else {
1212         action = "re-using";
1213     }
1214     balancer->s = shm;
1215     balancer->s->index = i;
1216     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02337)
1217                  "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm,
1218                  balancer->s->name);
1219     /* the below should always succeed */
1220     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, balancer->s->lbpname, "0");
1221     if (lbmethod) {
1222         balancer->lbmethod = lbmethod;
1223     } else {
1224         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(02432)
1225                      "Cannot find LB Method: %s", balancer->s->lbpname);
1226         return APR_EINVAL;
1227     }
1228     if (*balancer->s->nonce == PROXY_UNSET_NONCE) {
1229         char nonce[APR_UUID_FORMATTED_LENGTH + 1];
1230         apr_uuid_t uuid;
1231         /* Retrieve a UUID and store the nonce for the lifetime of
1232          * the process.
1233          */
1234         apr_uuid_get(&uuid);
1235         apr_uuid_format(nonce, &uuid);
1236         rv = PROXY_STRNCPY(balancer->s->nonce, nonce);
1237     }
1238     return rv;
1239 }
1240
1241 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p)
1242 {
1243     apr_status_t rv = APR_SUCCESS;
1244     ap_slotmem_provider_t *storage = balancer->storage;
1245     apr_size_t size;
1246     unsigned int num;
1247
1248     if (!storage) {
1249         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00918)
1250                      "no provider for %s", balancer->s->name);
1251         return APR_EGENERAL;
1252     }
1253     /*
1254      * for each balancer we need to init the global
1255      * mutex and then attach to the shared worker shm
1256      */
1257     if (!balancer->gmutex) {
1258         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00919)
1259                      "no mutex %s", balancer->s->name);
1260         return APR_EGENERAL;
1261     }
1262
1263     /* Re-open the mutex for the child. */
1264     rv = apr_global_mutex_child_init(&(balancer->gmutex),
1265                                      apr_global_mutex_lockfile(balancer->gmutex),
1266                                      p);
1267     if (rv != APR_SUCCESS) {
1268         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00920)
1269                      "Failed to reopen mutex %s in child",
1270                      balancer->s->name);
1271         return rv;
1272     }
1273
1274     /* now attach */
1275     storage->attach(&(balancer->wslot), balancer->s->sname, &size, &num, p);
1276     if (!balancer->wslot) {
1277         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00921) "slotmem_attach failed");
1278         return APR_EGENERAL;
1279     }
1280     if (balancer->lbmethod && balancer->lbmethod->reset)
1281         balancer->lbmethod->reset(balancer, s);
1282
1283     if (balancer->tmutex == NULL) {
1284         rv = apr_thread_mutex_create(&(balancer->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
1285         if (rv != APR_SUCCESS) {
1286             ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00922)
1287                          "can not create balancer thread mutex");
1288             return rv;
1289         }
1290     }
1291     return APR_SUCCESS;
1292 }
1293
1294 /*
1295  * CONNECTION related...
1296  */
1297
1298 static void socket_cleanup(proxy_conn_rec *conn)
1299 {
1300     conn->sock = NULL;
1301     conn->connection = NULL;
1302     conn->ssl_hostname = NULL;
1303     apr_pool_clear(conn->scpool);
1304 }
1305
1306 static apr_status_t conn_pool_cleanup(void *theworker)
1307 {
1308     proxy_worker *worker = (proxy_worker *)theworker;
1309     if (worker->cp->res) {
1310         worker->cp->pool = NULL;
1311     }
1312     return APR_SUCCESS;
1313 }
1314
1315 static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
1316 {
1317     apr_pool_t *pool;
1318     proxy_conn_pool *cp;
1319
1320     /*
1321      * Create a connection pool's subpool.
1322      * This pool is used for connection recycling.
1323      * Once the worker is added it is never removed but
1324      * it can be disabled.
1325      */
1326     apr_pool_create(&pool, p);
1327     apr_pool_tag(pool, "proxy_worker_cp");
1328     /*
1329      * Alloc from the same pool as worker.
1330      * proxy_conn_pool is permanently attached to the worker.
1331      */
1332     cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
1333     cp->pool = pool;
1334     worker->cp = cp;
1335 }
1336
1337 PROXY_DECLARE(int) ap_proxy_connection_reusable(proxy_conn_rec *conn)
1338 {
1339     proxy_worker *worker = conn->worker;
1340
1341     return ! (conn->close || !worker->s->is_address_reusable || worker->s->disablereuse);
1342 }
1343
1344 static apr_status_t connection_cleanup(void *theconn)
1345 {
1346     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
1347     proxy_worker *worker = conn->worker;
1348
1349     /*
1350      * If the connection pool is NULL the worker
1351      * cleanup has been run. Just return.
1352      */
1353     if (!worker->cp->pool) {
1354         return APR_SUCCESS;
1355     }
1356
1357     if (conn->r) {
1358         apr_pool_destroy(conn->r->pool);
1359         conn->r = NULL;
1360     }
1361
1362     /* Sanity check: Did we already return the pooled connection? */
1363     if (conn->inreslist) {
1364         ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923)
1365                       "Pooled connection 0x%pp for worker %s has been"
1366                       " already returned to the connection pool.", conn,
1367                       ap_proxy_worker_name(conn->pool, worker));
1368         return APR_SUCCESS;
1369     }
1370
1371     /* determine if the connection need to be closed */
1372     if (!worker->s->is_address_reusable || worker->s->disablereuse) {
1373         apr_pool_t *p = conn->pool;
1374         apr_pool_clear(p);
1375         conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
1376         conn->pool = p;
1377         conn->worker = worker;
1378         apr_pool_create(&(conn->scpool), p);
1379         apr_pool_tag(conn->scpool, "proxy_conn_scpool");
1380     }
1381     else if (conn->close
1382                 || (conn->connection
1383                     && conn->connection->keepalive == AP_CONN_CLOSE)) {
1384         socket_cleanup(conn);
1385         conn->close = 0;
1386     }
1387
1388     if (worker->s->hmax && worker->cp->res) {
1389         conn->inreslist = 1;
1390         apr_reslist_release(worker->cp->res, (void *)conn);
1391     }
1392     else
1393     {
1394         worker->cp->conn = conn;
1395     }
1396
1397     /* Always return the SUCCESS */
1398     return APR_SUCCESS;
1399 }
1400
1401 PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
1402                                                             request_rec *r)
1403 {
1404     apr_bucket_brigade *bb;
1405     apr_status_t rv;
1406
1407     /*
1408      * If we have an existing SSL connection it might be possible that the
1409      * server sent some SSL message we have not read so far (e.g. an SSL
1410      * shutdown message if the server closed the keepalive connection while
1411      * the connection was held unused in our pool).
1412      * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
1413      * processed. We don't expect any data to be in the returned brigade.
1414      */
1415     if (conn->sock && conn->connection) {
1416         bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
1417         rv = ap_get_brigade(conn->connection->input_filters, bb,
1418                             AP_MODE_READBYTES, APR_NONBLOCK_READ,
1419                             HUGE_STRING_LEN);
1420         if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
1421             socket_cleanup(conn);
1422         }
1423         if (!APR_BRIGADE_EMPTY(bb)) {
1424             apr_off_t len;
1425
1426             rv = apr_brigade_length(bb, 0, &len);
1427             ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r,
1428                           "SSL cleanup brigade contained %"
1429                           APR_OFF_T_FMT " bytes of data.", len);
1430         }
1431         apr_brigade_destroy(bb);
1432     }
1433     return APR_SUCCESS;
1434 }
1435
1436 /* reslist constructor */
1437 static apr_status_t connection_constructor(void **resource, void *params,
1438                                            apr_pool_t *pool)
1439 {
1440     apr_pool_t *ctx;
1441     apr_pool_t *scpool;
1442     proxy_conn_rec *conn;
1443     proxy_worker *worker = (proxy_worker *)params;
1444
1445     /*
1446      * Create the subpool for each connection
1447      * This keeps the memory consumption constant
1448      * when disconnecting from backend.
1449      */
1450     apr_pool_create(&ctx, pool);
1451     apr_pool_tag(ctx, "proxy_conn_pool");
1452     /*
1453      * Create another subpool that manages the data for the
1454      * socket and the connection member of the proxy_conn_rec struct as we
1455      * destroy this data more frequently than other data in the proxy_conn_rec
1456      * struct like hostname and addr (at least in the case where we have
1457      * keepalive connections that timed out).
1458      */
1459     apr_pool_create(&scpool, ctx);
1460     apr_pool_tag(scpool, "proxy_conn_scpool");
1461     conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec));
1462
1463     conn->pool   = ctx;
1464     conn->scpool = scpool;
1465     conn->worker = worker;
1466     conn->inreslist = 1;
1467     *resource = conn;
1468
1469     return APR_SUCCESS;
1470 }
1471
1472 /* reslist destructor */
1473 static apr_status_t connection_destructor(void *resource, void *params,
1474                                           apr_pool_t *pool)
1475 {
1476     proxy_worker *worker = params;
1477
1478     /* Destroy the pool only if not called from reslist_destroy */
1479     if (worker->cp->pool) {
1480         proxy_conn_rec *conn = resource;
1481         apr_pool_destroy(conn->pool);
1482     }
1483
1484     return APR_SUCCESS;
1485 }
1486
1487 /*
1488  * WORKER related...
1489  */
1490
1491 PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p,
1492                                            proxy_worker *worker)
1493 {
1494     if (!(*worker->s->uds_path) || !p) {
1495         /* just in case */
1496         return worker->s->name;
1497     }
1498     return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name, NULL);
1499 }
1500
1501 /*
1502  * Taken from ap_strcmp_match() :
1503  * Match = 0, NoMatch = 1, Abort = -1, Inval = -2
1504  * Based loosely on sections of wildmat.c by Rich Salz
1505  * Hmmm... shouldn't this really go component by component?
1506  *
1507  * Adds handling of the "\<any>" => "<any>" unescaping.
1508  */
1509 static int ap_proxy_strcmp_ematch(const char *str, const char *expected)
1510 {
1511     apr_size_t x, y;
1512
1513     for (x = 0, y = 0; expected[y]; ++y, ++x) {
1514         if ((!str[x]) && (expected[y] != '$' || !apr_isdigit(expected[y + 1])))
1515             return -1;
1516         if (expected[y] == '$' && apr_isdigit(expected[y + 1])) {
1517             while (expected[y] == '$' && apr_isdigit(expected[y + 1]))
1518                 y += 2;
1519             if (!expected[y])
1520                 return 0;
1521             while (str[x]) {
1522                 int ret;
1523                 if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1)
1524                     return ret;
1525             }
1526             return -1;
1527         }
1528         else if (expected[y] == '\\') {
1529             /* NUL is an invalid char! */
1530             if (!expected[++y])
1531                 return -2;
1532         }
1533         if (str[x] != expected[y])
1534             return 1;
1535     }
1536     /* We got all the way through the worker path without a difference */
1537     return 0;
1538 }
1539
1540 PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
1541                                                   proxy_balancer *balancer,
1542                                                   proxy_server_conf *conf,
1543                                                   const char *url)
1544 {
1545     proxy_worker *worker;
1546     proxy_worker *max_worker = NULL;
1547     int max_match = 0;
1548     int url_length;
1549     int min_match;
1550     int worker_name_length;
1551     const char *c;
1552     char *url_copy;
1553     int i;
1554
1555     if (!url) {
1556         return NULL;
1557     }
1558
1559     url = ap_proxy_de_socketfy(p, url);
1560
1561     c = ap_strchr_c(url, ':');
1562     if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
1563         return NULL;
1564     }
1565
1566     url_length = strlen(url);
1567     url_copy = apr_pstrmemdup(p, url, url_length);
1568
1569     /*
1570      * We need to find the start of the path and
1571      * therefore we know the length of the scheme://hostname/
1572      * part to we can force-lowercase everything up to
1573      * the start of the path.
1574      */
1575     c = ap_strchr_c(c+3, '/');
1576     if (c) {
1577         char *pathstart;
1578         pathstart = url_copy + (c - url);
1579         *pathstart = '\0';
1580         ap_str_tolower(url_copy);
1581         min_match = strlen(url_copy);
1582         *pathstart = '/';
1583     }
1584     else {
1585         ap_str_tolower(url_copy);
1586         min_match = strlen(url_copy);
1587     }
1588     /*
1589      * Do a "longest match" on the worker name to find the worker that
1590      * fits best to the URL, but keep in mind that we must have at least
1591      * a minimum matching of length min_match such that
1592      * scheme://hostname[:port] matches between worker and url.
1593      */
1594
1595     if (balancer) {
1596         proxy_worker **workers = (proxy_worker **)balancer->workers->elts;
1597         for (i = 0; i < balancer->workers->nelts; i++, workers++) {
1598             worker = *workers;
1599             if ( ((worker_name_length = strlen(worker->s->name)) <= url_length)
1600                 && (worker_name_length >= min_match)
1601                 && (worker_name_length > max_match)
1602                 && (worker->s->is_name_matchable
1603                     || strncmp(url_copy, worker->s->name,
1604                                worker_name_length) == 0)
1605                 && (!worker->s->is_name_matchable
1606                     || ap_proxy_strcmp_ematch(url_copy,
1607                                               worker->s->name) == 0) ) {
1608                 max_worker = worker;
1609                 max_match = worker_name_length;
1610             }
1611         }
1612     } else {
1613         worker = (proxy_worker *)conf->workers->elts;
1614         for (i = 0; i < conf->workers->nelts; i++, worker++) {
1615             if ( ((worker_name_length = strlen(worker->s->name)) <= url_length)
1616                 && (worker_name_length >= min_match)
1617                 && (worker_name_length > max_match)
1618                 && (worker->s->is_name_matchable
1619                     || strncmp(url_copy, worker->s->name,
1620                                worker_name_length) == 0)
1621                 && (!worker->s->is_name_matchable
1622                     || ap_proxy_strcmp_ematch(url_copy,
1623                                               worker->s->name) == 0) ) {
1624                 max_worker = worker;
1625                 max_match = worker_name_length;
1626             }
1627         }
1628     }
1629
1630     return max_worker;
1631 }
1632
1633
1634 /*
1635  * To create a worker from scratch first we define the
1636  * specifics of the worker; this is all local data.
1637  * We then allocate space for it if data needs to be
1638  * shared. This allows for dynamic addition during
1639  * config and runtime.
1640  */
1641 PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p,
1642                                              proxy_worker **worker,
1643                                              proxy_balancer *balancer,
1644                                              proxy_server_conf *conf,
1645                                              const char *url,
1646                                              int do_malloc)
1647 {
1648     int rv;
1649     apr_uri_t uri, urisock;
1650     proxy_worker_shared *wshared;
1651     char *ptr, *sockpath = NULL;
1652
1653     /*
1654      * Look to see if we are using UDS:
1655      * require format: unix:/path/foo/bar.sock|http://ignored/path2/
1656      * This results in talking http to the socket at /path/foo/bar.sock
1657      */
1658     ptr = ap_strchr((char *)url, '|');
1659     if (ptr) {
1660         *ptr = '\0';
1661         rv = apr_uri_parse(p, url, &urisock);
1662         if (rv == APR_SUCCESS && !ap_casecmpstr(urisock.scheme, "unix")) {
1663             sockpath = ap_runtime_dir_relative(p, urisock.path);;
1664             url = ptr+1;    /* so we get the scheme for the uds */
1665         }
1666         else {
1667             *ptr = '|';
1668         }
1669     }
1670     rv = apr_uri_parse(p, url, &uri);
1671
1672     if (rv != APR_SUCCESS) {
1673         return apr_pstrcat(p, "Unable to parse URL: ", url, NULL);
1674     }
1675     if (!uri.scheme) {
1676         return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
1677     }
1678     /* allow for unix:/path|http: */
1679     if (!uri.hostname) {
1680         if (sockpath) {
1681             uri.hostname = "localhost";
1682         }
1683         else {
1684             return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
1685         }
1686     }
1687     else {
1688         ap_str_tolower(uri.hostname);
1689     }
1690     ap_str_tolower(uri.scheme);
1691     /*
1692      * Workers can be associated w/ balancers or on their
1693      * own; ie: the generic reverse-proxy or a worker
1694      * in a simple ProxyPass statement. eg:
1695      *
1696      *      ProxyPass / http://www.example.com
1697      *
1698      * in which case the worker goes in the conf slot.
1699      */
1700     if (balancer) {
1701         proxy_worker **runtime;
1702         /* recall that we get a ptr to the ptr here */
1703         runtime = apr_array_push(balancer->workers);
1704         *worker = *runtime = apr_palloc(p, sizeof(proxy_worker));   /* right to left baby */
1705         /* we've updated the list of workers associated with
1706          * this balancer *locally* */
1707         balancer->wupdated = apr_time_now();
1708     } else if (conf) {
1709         *worker = apr_array_push(conf->workers);
1710     } else {
1711         /* we need to allocate space here */
1712         *worker = apr_palloc(p, sizeof(proxy_worker));
1713     }
1714
1715     memset(*worker, 0, sizeof(proxy_worker));
1716     /* right here we just want to tuck away the worker info.
1717      * if called during config, we don't have shm setup yet,
1718      * so just note the info for later. */
1719     if (do_malloc)
1720         wshared = ap_malloc(sizeof(proxy_worker_shared));  /* will be freed ap_proxy_share_worker */
1721     else
1722         wshared = apr_palloc(p, sizeof(proxy_worker_shared));
1723
1724     memset(wshared, 0, sizeof(proxy_worker_shared));
1725
1726     wshared->port = (uri.port ? uri.port : ap_proxy_port_of_scheme(uri.scheme));
1727     if (uri.port && uri.port == ap_proxy_port_of_scheme(uri.scheme)) {
1728         uri.port = 0;
1729     }
1730     ptr = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
1731     if (PROXY_STRNCPY(wshared->name, ptr) != APR_SUCCESS) {
1732         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02808)
1733         "Alert! worker name (%s) too long; truncated to: %s", ptr, wshared->name);
1734     }
1735     if (PROXY_STRNCPY(wshared->scheme, uri.scheme) != APR_SUCCESS) {
1736         return apr_psprintf(p, "worker scheme (%s) too long", uri.scheme);
1737     }
1738     if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) {
1739         return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname);
1740     }
1741     wshared->flush_packets = flush_off;
1742     wshared->flush_wait = PROXY_FLUSH_WAIT;
1743     wshared->is_address_reusable = 1;
1744     wshared->lbfactor = 1;
1745     wshared->passes = 1;
1746     wshared->fails = 1;
1747     wshared->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL);
1748     wshared->smax = -1;
1749     wshared->hash.def = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_DEFAULT);
1750     wshared->hash.fnv = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_FNV);
1751     wshared->was_malloced = (do_malloc != 0);
1752     wshared->is_name_matchable = 0;
1753     if (sockpath) {
1754         if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) {
1755             return apr_psprintf(p, "worker uds path (%s) too long", sockpath);
1756         }
1757
1758     }
1759     else {
1760         *wshared->uds_path = '\0';
1761     }
1762     if (!balancer) {
1763         wshared->status |= PROXY_WORKER_IGNORE_ERRORS;
1764     }
1765
1766     (*worker)->hash = wshared->hash;
1767     (*worker)->context = NULL;
1768     (*worker)->cp = NULL;
1769     (*worker)->balancer = balancer;
1770     (*worker)->s = wshared;
1771
1772     return NULL;
1773 }
1774
1775 PROXY_DECLARE(char *) ap_proxy_define_match_worker(apr_pool_t *p,
1776                                              proxy_worker **worker,
1777                                              proxy_balancer *balancer,
1778                                              proxy_server_conf *conf,
1779                                              const char *url,
1780                                              int do_malloc)
1781 {
1782     char *err;
1783
1784     err = ap_proxy_define_worker(p, worker, balancer, conf, url, do_malloc);
1785     if (err) {
1786         return err;
1787     }
1788
1789     (*worker)->s->is_name_matchable = 1;
1790     return NULL;
1791 }
1792
1793 /*
1794  * Create an already defined worker and free up memory
1795  */
1796 PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, proxy_worker_shared *shm,
1797                                                   int i)
1798 {
1799     char *action = "copying";
1800     if (!shm || !worker->s)
1801         return APR_EINVAL;
1802
1803     if ((worker->s->hash.def != shm->hash.def) ||
1804         (worker->s->hash.fnv != shm->hash.fnv)) {
1805         memcpy(shm, worker->s, sizeof(proxy_worker_shared));
1806         if (worker->s->was_malloced)
1807             free(worker->s); /* was malloced in ap_proxy_define_worker */
1808     } else {
1809         action = "re-using";
1810     }
1811     worker->s = shm;
1812     worker->s->index = i;
1813     {
1814         apr_pool_t *pool;
1815         apr_pool_create(&pool, ap_server_conf->process->pool);
1816         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338)
1817                      "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm,
1818                      ap_proxy_worker_name(pool, worker));
1819         if (pool) {
1820             apr_pool_destroy(pool);
1821         }
1822     }
1823     return APR_SUCCESS;
1824 }
1825
1826 PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s, apr_pool_t *p)
1827 {
1828     apr_status_t rv = APR_SUCCESS;
1829     int mpm_threads;
1830
1831     if (worker->s->status & PROXY_WORKER_INITIALIZED) {
1832         /* The worker is already initialized */
1833         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00924)
1834                      "worker %s shared already initialized",
1835                      ap_proxy_worker_name(p, worker));
1836     }
1837     else {
1838         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00925)
1839                      "initializing worker %s shared",
1840                      ap_proxy_worker_name(p, worker));
1841         /* Set default parameters */
1842         if (!worker->s->retry_set) {
1843             worker->s->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
1844         }
1845         /* By default address is reusable unless DisableReuse is set */
1846         if (worker->s->disablereuse) {
1847             worker->s->is_address_reusable = 0;
1848         }
1849         else {
1850             worker->s->is_address_reusable = 1;
1851         }
1852
1853         ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1854         if (mpm_threads > 1) {
1855             /* Set hard max to no more then mpm_threads */
1856             if (worker->s->hmax == 0 || worker->s->hmax > mpm_threads) {
1857                 worker->s->hmax = mpm_threads;
1858             }
1859             if (worker->s->smax == -1 || worker->s->smax > worker->s->hmax) {
1860                 worker->s->smax = worker->s->hmax;
1861             }
1862             /* Set min to be lower then smax */
1863             if (worker->s->min > worker->s->smax) {
1864                 worker->s->min = worker->s->smax;
1865             }
1866         }
1867         else {
1868             /* This will supress the apr_reslist creation */
1869             worker->s->min = worker->s->smax = worker->s->hmax = 0;
1870         }
1871     }
1872
1873     /* What if local is init'ed and shm isn't?? Even possible? */
1874     if (worker->local_status & PROXY_WORKER_INITIALIZED) {
1875         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00926)
1876                      "worker %s local already initialized",
1877                      ap_proxy_worker_name(p, worker));
1878     }
1879     else {
1880         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927)
1881                      "initializing worker %s local",
1882                      ap_proxy_worker_name(p, worker));
1883         apr_global_mutex_lock(proxy_mutex);
1884         /* Now init local worker data */
1885         if (worker->tmutex == NULL) {
1886             rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
1887             if (rv != APR_SUCCESS) {
1888                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00928)
1889                              "can not create worker thread mutex");
1890                 apr_global_mutex_unlock(proxy_mutex);
1891                 return rv;
1892             }
1893         }
1894         if (worker->cp == NULL)
1895             init_conn_pool(p, worker);
1896         if (worker->cp == NULL) {
1897             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929)
1898                          "can not create connection pool");
1899             apr_global_mutex_unlock(proxy_mutex);
1900             return APR_EGENERAL;
1901         }
1902
1903         if (worker->s->hmax) {
1904             rv = apr_reslist_create(&(worker->cp->res),
1905                                     worker->s->min, worker->s->smax,
1906                                     worker->s->hmax, worker->s->ttl,
1907                                     connection_constructor, connection_destructor,
1908                                     worker, worker->cp->pool);
1909
1910             apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
1911                                       conn_pool_cleanup,
1912                                       apr_pool_cleanup_null);
1913
1914             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00930)
1915                 "initialized pool in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
1916                  getpid(), worker->s->hostname, worker->s->min,
1917                  worker->s->hmax, worker->s->smax);
1918
1919             /* Set the acquire timeout */
1920             if (rv == APR_SUCCESS && worker->s->acquire_set) {
1921                 apr_reslist_timeout_set(worker->cp->res, worker->s->acquire);
1922             }
1923
1924         }
1925         else {
1926             void *conn;
1927
1928             rv = connection_constructor(&conn, worker, worker->cp->pool);
1929             worker->cp->conn = conn;
1930
1931             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00931)
1932                  "initialized single connection worker in child %" APR_PID_T_FMT " for (%s)",
1933                  getpid(), worker->s->hostname);
1934         }
1935         apr_global_mutex_unlock(proxy_mutex);
1936
1937     }
1938     if (rv == APR_SUCCESS) {
1939         worker->s->status |= (PROXY_WORKER_INITIALIZED);
1940         worker->local_status |= (PROXY_WORKER_INITIALIZED);
1941     }
1942     return rv;
1943 }
1944
1945 static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker,
1946         server_rec *s)
1947 {
1948     if (worker->s->status & PROXY_WORKER_IN_ERROR) {
1949         if ((worker->s->status & PROXY_WORKER_IGNORE_ERRORS)
1950             || apr_time_now() > worker->s->error_time + worker->s->retry) {
1951             ++worker->s->retries;
1952             worker->s->status &= ~PROXY_WORKER_IN_ERROR;
1953             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00932)
1954                          "%s: worker for (%s) has been marked for retry",
1955                          proxy_function, worker->s->hostname);
1956             return OK;
1957         }
1958         else {
1959             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00933)
1960                          "%s: too soon to retry worker for (%s)",
1961                          proxy_function, worker->s->hostname);
1962             return DECLINED;
1963         }
1964     }
1965     else {
1966         return OK;
1967     }
1968 }
1969
1970 /*
1971  * In the case of the reverse proxy, we need to see if we
1972  * were passed a UDS url (eg: from mod_proxy) and adjust uds_path
1973  * as required.  
1974  */
1975 static void fix_uds_filename(request_rec *r, char **url) 
1976 {
1977     char *ptr, *ptr2;
1978     if (!r || !r->filename) return;
1979
1980     if (!strncmp(r->filename, "proxy:", 6) &&
1981             (ptr2 = ap_strcasestr(r->filename, "unix:")) &&
1982             (ptr = ap_strchr(ptr2, '|'))) {
1983         apr_uri_t urisock;
1984         apr_status_t rv;
1985         *ptr = '\0';
1986         rv = apr_uri_parse(r->pool, ptr2, &urisock);
1987         if (rv == APR_SUCCESS) {
1988             char *rurl = ptr+1;
1989             char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path);
1990             apr_table_setn(r->notes, "uds_path", sockpath);
1991             *url = apr_pstrdup(r->pool, rurl); /* so we get the scheme for the uds */
1992             /* r->filename starts w/ "proxy:", so add after that */
1993             memmove(r->filename+6, rurl, strlen(rurl)+1);
1994             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
1995                     "*: rewrite of url due to UDS(%s): %s (%s)",
1996                     sockpath, *url, r->filename);
1997         }
1998         else {
1999             *ptr = '|';
2000         }
2001     }
2002 }
2003
2004 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
2005                                         proxy_balancer **balancer,
2006                                         request_rec *r,
2007                                         proxy_server_conf *conf, char **url)
2008 {
2009     int access_status;
2010
2011     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
2012     if (access_status == DECLINED && *balancer == NULL) {
2013         *worker = ap_proxy_get_worker(r->pool, NULL, conf, *url);
2014         if (*worker) {
2015             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2016                           "%s: found worker %s for %s",
2017                           (*worker)->s->scheme, (*worker)->s->name, *url);
2018             *balancer = NULL;
2019             fix_uds_filename(r, url);
2020             access_status = OK;
2021         }
2022         else if (r->proxyreq == PROXYREQ_PROXY) {
2023             if (conf->forward) {
2024                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2025                               "*: found forward proxy worker for %s", *url);
2026                 *balancer = NULL;
2027                 *worker = conf->forward;
2028                 access_status = OK;
2029                 /*
2030                  * The forward worker does not keep connections alive, so
2031                  * ensure that mod_proxy_http does the correct thing
2032                  * regarding the Connection header in the request.
2033                  */
2034                 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2035             }
2036         }
2037         else if (r->proxyreq == PROXYREQ_REVERSE) {
2038             if (conf->reverse) {
2039                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2040                               "*: using default reverse proxy worker for %s (no keepalive)", *url);
2041                 *balancer = NULL;
2042                 *worker = conf->reverse;
2043                 access_status = OK;
2044                 /*
2045                  * The reverse worker does not keep connections alive, so
2046                  * ensure that mod_proxy_http does the correct thing
2047                  * regarding the Connection header in the request.
2048                  */
2049                 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2050                 fix_uds_filename(r, url);
2051             }
2052         }
2053     }
2054     else if (access_status == DECLINED && *balancer != NULL) {
2055         /* All the workers are busy */
2056         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00934)
2057                       "all workers are busy.  Unable to serve %s", *url);
2058         access_status = HTTP_SERVICE_UNAVAILABLE;
2059     }
2060     return access_status;
2061 }
2062
2063 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
2064                                          proxy_balancer *balancer,
2065                                          request_rec *r,
2066                                          proxy_server_conf *conf)
2067 {
2068     int access_status = OK;
2069     if (balancer) {
2070         access_status = proxy_run_post_request(worker, balancer, r, conf);
2071         if (access_status == DECLINED) {
2072             access_status = OK; /* no post_request handler available */
2073             /* TODO: recycle direct worker */
2074         }
2075     }
2076
2077     return access_status;
2078 }
2079
2080 /* DEPRECATED */
2081 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
2082                                                const char *proxy_function,
2083                                                apr_sockaddr_t *backend_addr,
2084                                                const char *backend_name,
2085                                                proxy_server_conf *conf,
2086                                                request_rec *r)
2087 {
2088     apr_status_t rv;
2089     int connected = 0;
2090     int loglevel;
2091
2092     while (backend_addr && !connected) {
2093         if ((rv = apr_socket_create(newsock, backend_addr->family,
2094                                     SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
2095             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2096             ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935)
2097                           "%s: error creating fam %d socket for target %s",
2098                           proxy_function, backend_addr->family, backend_name);
2099             /*
2100              * this could be an IPv6 address from the DNS but the
2101              * local machine won't give us an IPv6 socket; hopefully the
2102              * DNS returned an additional address to try
2103              */
2104             backend_addr = backend_addr->next;
2105             continue;
2106         }
2107
2108         if (conf->recv_buffer_size > 0 &&
2109             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
2110                                      conf->recv_buffer_size))) {
2111             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00936)
2112                           "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2113                           "ProxyReceiveBufferSize, using default");
2114         }
2115
2116         rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1);
2117         if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2118             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00937)
2119                           "apr_socket_opt_set(APR_TCP_NODELAY): "
2120                           "Failed to set");
2121         }
2122
2123         /* Set a timeout on the socket */
2124         if (conf->timeout_set) {
2125             apr_socket_timeout_set(*newsock, conf->timeout);
2126         }
2127         else {
2128             apr_socket_timeout_set(*newsock, r->server->timeout);
2129         }
2130
2131         ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2132                       "%s: fam %d socket created to connect to %s",
2133                       proxy_function, backend_addr->family, backend_name);
2134
2135         if (conf->source_address) {
2136             apr_sockaddr_t *local_addr;
2137             /* Make a copy since apr_socket_bind() could change
2138              * conf->source_address, which we don't want.
2139              */
2140             local_addr = apr_pmemdup(r->pool, conf->source_address,
2141                                      sizeof(apr_sockaddr_t));
2142             local_addr->pool = r->pool;
2143             rv = apr_socket_bind(*newsock, local_addr);
2144             if (rv != APR_SUCCESS) {
2145                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00938)
2146                               "%s: failed to bind socket to local address",
2147                               proxy_function);
2148             }
2149         }
2150
2151         /* make the connection out of the socket */
2152         rv = apr_socket_connect(*newsock, backend_addr);
2153
2154         /* if an error occurred, loop round and try again */
2155         if (rv != APR_SUCCESS) {
2156             apr_socket_close(*newsock);
2157             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2158             ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939)
2159                           "%s: attempt to connect to %pI (%s) failed",
2160                           proxy_function, backend_addr, backend_name);
2161             backend_addr = backend_addr->next;
2162             continue;
2163         }
2164         connected = 1;
2165     }
2166     return connected ? 0 : 1;
2167 }
2168
2169 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
2170                                                proxy_conn_rec **conn,
2171                                                proxy_worker *worker,
2172                                                server_rec *s)
2173 {
2174     apr_status_t rv;
2175
2176     if (!PROXY_WORKER_IS_USABLE(worker)) {
2177         /* Retry the worker */
2178         ap_proxy_retry_worker(proxy_function, worker, s);
2179
2180         if (!PROXY_WORKER_IS_USABLE(worker)) {
2181             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00940)
2182                          "%s: disabled connection for (%s)",
2183                          proxy_function, worker->s->hostname);
2184             return HTTP_SERVICE_UNAVAILABLE;
2185         }
2186     }
2187
2188     if (worker->s->hmax && worker->cp->res) {
2189         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
2190     }
2191     else {
2192         /* create the new connection if the previous was destroyed */
2193         if (!worker->cp->conn) {
2194             connection_constructor((void **)conn, worker, worker->cp->pool);
2195         }
2196         else {
2197             *conn = worker->cp->conn;
2198             worker->cp->conn = NULL;
2199         }
2200         rv = APR_SUCCESS;
2201     }
2202
2203     if (rv != APR_SUCCESS) {
2204         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00941)
2205                      "%s: failed to acquire connection for (%s)",
2206                      proxy_function, worker->s->hostname);
2207         return HTTP_SERVICE_UNAVAILABLE;
2208     }
2209     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942)
2210                  "%s: has acquired connection for (%s)",
2211                  proxy_function, worker->s->hostname);
2212
2213     (*conn)->worker = worker;
2214     (*conn)->close  = 0;
2215     (*conn)->inreslist = 0;
2216
2217     return OK;
2218 }
2219
2220 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
2221                                                proxy_conn_rec *conn,
2222                                                server_rec *s)
2223 {
2224     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943)
2225                 "%s: has released connection for (%s)",
2226                 proxy_function, conn->worker->s->hostname);
2227     connection_cleanup(conn);
2228
2229     return OK;
2230 }
2231
2232 PROXY_DECLARE(int)
2233 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
2234                               proxy_server_conf *conf,
2235                               proxy_worker *worker,
2236                               proxy_conn_rec *conn,
2237                               apr_uri_t *uri,
2238                               char **url,
2239                               const char *proxyname,
2240                               apr_port_t proxyport,
2241                               char *server_portstr,
2242                               int server_portstr_size)
2243 {
2244     int server_port;
2245     apr_status_t err = APR_SUCCESS;
2246     apr_status_t uerr = APR_SUCCESS;
2247     const char *uds_path;
2248
2249     /*
2250      * Break up the URL to determine the host to connect to
2251      */
2252
2253     /* we break the URL into host, port, uri */
2254     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
2255         return ap_proxyerror(r, HTTP_BAD_REQUEST,
2256                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
2257                                          NULL));
2258     }
2259     if (!uri->port) {
2260         uri->port = ap_proxy_port_of_scheme(uri->scheme);
2261     }
2262
2263     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00944)
2264                  "connecting %s to %s:%d", *url, uri->hostname, uri->port);
2265
2266     /*
2267      * allocate these out of the specified connection pool
2268      * The scheme handler decides if this is permanent or
2269      * short living pool.
2270      */
2271     /* Unless we are connecting the backend via a (forward Proxy)Remote, we
2272      * have to use the original form of the URI (non absolute), but this is
2273      * also the case via a remote proxy using the CONNECT method since the
2274      * original request (and URI) is to be embedded in the body.
2275      */
2276     if (!proxyname || conn->is_ssl) {
2277         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
2278                            uri->query ? uri->query : "",
2279                            uri->fragment ? "#" : "",
2280                            uri->fragment ? uri->fragment : "", NULL);
2281     }
2282     /*
2283      * Figure out if our passed in proxy_conn_rec has a usable
2284      * address cached.
2285      *
2286      * TODO: Handle this much better... 
2287      *
2288      * XXX: If generic workers are ever address-reusable, we need 
2289      *      to check host and port on the conn and be careful about
2290      *      spilling the cached addr from the worker.
2291      */
2292     uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path"));
2293     if (uds_path) {
2294         if (conn->uds_path == NULL) {
2295             /* use (*conn)->pool instead of worker->cp->pool to match lifetime */
2296             conn->uds_path = apr_pstrdup(conn->pool, uds_path);
2297         }
2298         if (conn->uds_path) {
2299             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02545)
2300                          "%s: has determined UDS as %s",
2301                          uri->scheme, conn->uds_path);
2302         }
2303         else {
2304             /* should never happen */
2305             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02546)
2306                          "%s: cannot determine UDS (%s)",
2307                          uri->scheme, uds_path);
2308
2309         }
2310         /*
2311          * In UDS cases, some structs are NULL. Protect from de-refs
2312          * and provide info for logging at the same time.
2313          */
2314         if (!conn->addr) {
2315             apr_sockaddr_t *sa;
2316             apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, conn->pool);
2317             conn->addr = sa;
2318         }
2319         conn->hostname = "httpd-UDS";
2320         conn->port = 0;
2321     }
2322     else {
2323         int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse;
2324         if (!conn->hostname || !will_reuse) {
2325             if (proxyname) {
2326                 conn->hostname = apr_pstrdup(conn->pool, proxyname);
2327                 conn->port = proxyport;
2328                 /*
2329                  * If we have a forward proxy and the protocol is HTTPS,
2330                  * then we need to prepend a HTTP CONNECT request before
2331                  * sending our actual HTTPS requests.
2332                  * Save our real backend data for using it later during HTTP CONNECT.
2333                  */
2334                 if (conn->is_ssl) {
2335                     const char *proxy_auth;
2336
2337                     forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info));
2338                     conn->forward = forward;
2339                     forward->use_http_connect = 1;
2340                     forward->target_host = apr_pstrdup(conn->pool, uri->hostname);
2341                     forward->target_port = uri->port;
2342                     /* Do we want to pass Proxy-Authorization along?
2343                      * If we haven't used it, then YES
2344                      * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
2345                      * So let's make it configurable by env.
2346                      * The logic here is the same used in mod_proxy_http.
2347                      */
2348                     proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
2349                     if (proxy_auth != NULL &&
2350                         proxy_auth[0] != '\0' &&
2351                         r->user == NULL && /* we haven't yet authenticated */
2352                         apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
2353                         forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth);
2354                     }
2355                 }
2356             }
2357             else {
2358                 conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
2359                 conn->port = uri->port;
2360             }
2361             if (!will_reuse) {
2362                 /*
2363                  * Only do a lookup if we should not reuse the backend address.
2364                  * Otherwise we will look it up once for the worker.
2365                  */
2366                 err = apr_sockaddr_info_get(&(conn->addr),
2367                                             conn->hostname, APR_UNSPEC,
2368                                             conn->port, 0,
2369                                             conn->pool);
2370             }
2371             socket_cleanup(conn);
2372             conn->close = 0;
2373         }
2374         if (will_reuse) {
2375             /*
2376              * Looking up the backend address for the worker only makes sense if
2377              * we can reuse the address.
2378              */
2379             if (!worker->cp->addr) {
2380                 if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
2381                     ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock");
2382                     return HTTP_INTERNAL_SERVER_ERROR;
2383                 }
2384
2385                 /*
2386                  * Worker can have the single constant backend adress.
2387                  * The single DNS lookup is used once per worker.
2388                  * If dynamic change is needed then set the addr to NULL
2389                  * inside dynamic config to force the lookup.
2390                  */
2391                 err = apr_sockaddr_info_get(&(worker->cp->addr),
2392                                             conn->hostname, APR_UNSPEC,
2393                                             conn->port, 0,
2394                                             worker->cp->pool);
2395                 conn->addr = worker->cp->addr;
2396                 if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
2397                     ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock");
2398                 }
2399             }
2400             else {
2401                 conn->addr = worker->cp->addr;
2402             }
2403         }
2404     }
2405     /* Close a possible existing socket if we are told to do so */
2406     if (conn->close) {
2407         socket_cleanup(conn);
2408         conn->close = 0;
2409     }
2410
2411     if (err != APR_SUCCESS) {
2412         return ap_proxyerror(r, HTTP_GATEWAY_TIME_OUT,
2413                              apr_pstrcat(p, "DNS lookup failure for: ",
2414                                          conn->hostname, NULL));
2415     }
2416
2417     /* Get the server port for the Via headers */
2418     server_port = ap_get_server_port(r);
2419     AP_DEBUG_ASSERT(server_portstr_size > 0);
2420     if (ap_is_default_port(server_port, r)) {
2421         server_portstr[0] = '\0';
2422     }
2423     else {
2424         apr_snprintf(server_portstr, server_portstr_size, ":%d",
2425                      server_port);
2426     }
2427
2428     /* check if ProxyBlock directive on this host */
2429     if (OK != ap_proxy_checkproxyblock(r, conf, uri->hostname, 
2430                                        proxyname ? NULL : conn->addr)) {
2431         return ap_proxyerror(r, HTTP_FORBIDDEN,
2432                              "Connect to remote machine blocked");
2433     }
2434     /*
2435      * When SSL is configured, determine the hostname (SNI) for the request
2436      * and save it in conn->ssl_hostname. Close any reused connection whose
2437      * SNI differs.
2438      */
2439     if (conn->is_ssl) {
2440         proxy_dir_conf *dconf;
2441         const char *ssl_hostname;
2442         /*
2443          * In the case of ProxyPreserveHost on use the hostname of
2444          * the request if present otherwise use the one from the
2445          * backend request URI.
2446          */
2447         dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
2448         if (dconf->preserve_host) {
2449             ssl_hostname = r->hostname;
2450         }
2451         else if (conn->forward
2452                  && ((forward_info *)(conn->forward))->use_http_connect) {
2453             ssl_hostname = ((forward_info *)conn->forward)->target_host;
2454         }
2455         else {
2456             ssl_hostname = conn->hostname;
2457         }
2458         /*
2459          * Close if a SNI is in use but this request requires no or
2460          * a different one, or no SNI is in use but one is required.
2461          */
2462         if ((conn->ssl_hostname && (!ssl_hostname ||
2463                                     strcasecmp(conn->ssl_hostname,
2464                                                ssl_hostname) != 0)) ||
2465                 (!conn->ssl_hostname && ssl_hostname && conn->sock)) {
2466             socket_cleanup(conn);
2467         }
2468         if (conn->ssl_hostname == NULL) {
2469             conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname);
2470         }
2471     }
2472     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00947)
2473                  "connected %s to %s:%d", *url, conn->hostname, conn->port);
2474     return OK;
2475 }
2476
2477 #define USE_ALTERNATE_IS_CONNECTED 1
2478
2479 #if !defined(APR_MSG_PEEK) && defined(MSG_PEEK)
2480 #define APR_MSG_PEEK MSG_PEEK
2481 #endif
2482
2483 #if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK)
2484 PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket)
2485 {
2486     apr_pollfd_t pfds[1];
2487     apr_status_t status;
2488     apr_int32_t  nfds;
2489
2490     pfds[0].reqevents = APR_POLLIN;
2491     pfds[0].desc_type = APR_POLL_SOCKET;
2492     pfds[0].desc.s = socket;
2493
2494     do {
2495         status = apr_poll(&pfds[0], 1, &nfds, 0);
2496     } while (APR_STATUS_IS_EINTR(status));
2497
2498     if (status == APR_SUCCESS && nfds == 1 &&
2499         pfds[0].rtnevents == APR_POLLIN) {
2500         apr_sockaddr_t unused;
2501         apr_size_t len = 1;
2502         char buf[1];
2503         /* The socket might be closed in which case
2504          * the poll will return POLLIN.
2505          * If there is no data available the socket
2506          * is closed.
2507          */
2508         status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK,
2509                                      &buf[0], &len);
2510         if (status == APR_SUCCESS && len)
2511             return 1;
2512         else
2513             return 0;
2514     }
2515     else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2516         return 1;
2517     }
2518     return 0;
2519
2520 }
2521 #else
2522 PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket)
2523
2524 {
2525     apr_size_t buffer_len = 1;
2526     char test_buffer[1];
2527     apr_status_t socket_status;
2528     apr_interval_time_t current_timeout;
2529
2530     /* save timeout */
2531     apr_socket_timeout_get(sock, &current_timeout);
2532     /* set no timeout */
2533     apr_socket_timeout_set(sock, 0);
2534     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
2535     /* put back old timeout */
2536     apr_socket_timeout_set(sock, current_timeout);
2537     if (APR_STATUS_IS_EOF(socket_status)
2538         || APR_STATUS_IS_ECONNRESET(socket_status)) {
2539         return 0;
2540     }
2541     else {
2542         return 1;
2543     }
2544 }
2545 #endif /* USE_ALTERNATE_IS_CONNECTED */
2546
2547
2548 /*
2549  * Send a HTTP CONNECT request to a forward proxy.
2550  * The proxy is given by "backend", the target server
2551  * is contained in the "forward" member of "backend".
2552  */
2553 static apr_status_t send_http_connect(proxy_conn_rec *backend,
2554                                       server_rec *s)
2555 {
2556     int status;
2557     apr_size_t nbytes;
2558     apr_size_t left;
2559     int complete = 0;
2560     char buffer[HUGE_STRING_LEN];
2561     char drain_buffer[HUGE_STRING_LEN];
2562     forward_info *forward = (forward_info *)backend->forward;
2563     int len = 0;
2564
2565     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00948)
2566                  "CONNECT: sending the CONNECT request for %s:%d "
2567                  "to the remote proxy %pI (%s)",
2568                  forward->target_host, forward->target_port,
2569                  backend->addr, backend->hostname);
2570     /* Create the CONNECT request */
2571     nbytes = apr_snprintf(buffer, sizeof(buffer),
2572                           "CONNECT %s:%d HTTP/1.0" CRLF,
2573                           forward->target_host, forward->target_port);
2574     /* Add proxy authorization from the initial request if necessary */
2575     if (forward->proxy_auth != NULL) {
2576         nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
2577                                "Proxy-Authorization: %s" CRLF,
2578                                forward->proxy_auth);
2579     }
2580     /* Set a reasonable agent and send everything */
2581     nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
2582                            "Proxy-agent: %s" CRLF CRLF,
2583                            ap_get_server_banner());
2584     ap_xlate_proto_to_ascii(buffer, nbytes);
2585     apr_socket_send(backend->sock, buffer, &nbytes);
2586
2587     /* Receive the whole CONNECT response */
2588     left = sizeof(buffer) - 1;
2589     /* Read until we find the end of the headers or run out of buffer */
2590     do {
2591         nbytes = left;
2592         status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
2593         len += nbytes;
2594         left -= nbytes;
2595         buffer[len] = '\0';
2596         if (strstr(buffer + len - nbytes, CRLF_ASCII CRLF_ASCII) != NULL) {
2597             ap_xlate_proto_from_ascii(buffer, len);
2598             complete = 1;
2599             break;
2600         }
2601     } while (status == APR_SUCCESS && left > 0);
2602     /* Drain what's left */
2603     if (!complete) {
2604         nbytes = sizeof(drain_buffer) - 1;
2605         while (status == APR_SUCCESS && nbytes) {
2606             status = apr_socket_recv(backend->sock, drain_buffer, &nbytes);
2607             drain_buffer[nbytes] = '\0';
2608             nbytes = sizeof(drain_buffer) - 1;
2609             if (strstr(drain_buffer, CRLF_ASCII CRLF_ASCII) != NULL) {
2610                 break;
2611             }
2612         }
2613     }
2614
2615     /* Check for HTTP_OK response status */
2616     if (status == APR_SUCCESS) {
2617         unsigned int major, minor;
2618         /* Only scan for three character status code */
2619         char code_str[4];
2620
2621         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00949)
2622                      "send_http_connect: response from the forward proxy: %s",
2623                      buffer);
2624
2625         /* Extract the returned code */
2626         if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
2627             status = atoi(code_str);
2628             if (status == HTTP_OK) {
2629                 status = APR_SUCCESS;
2630             }
2631             else {
2632                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00950)
2633                              "send_http_connect: the forward proxy returned code is '%s'",
2634                              code_str);
2635                 status = APR_INCOMPLETE;
2636             }
2637         }
2638     }
2639
2640     return(status);
2641 }
2642
2643
2644 #if APR_HAVE_SYS_UN_H
2645 /* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
2646 PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock,
2647                                                  const char *uds_path,
2648                                                  apr_pool_t *p)
2649 {
2650     apr_status_t rv;
2651     apr_os_sock_t rawsock;
2652     apr_interval_time_t t;
2653     struct sockaddr_un *sa;
2654     apr_socklen_t addrlen, pathlen;
2655
2656     rv = apr_os_sock_get(&rawsock, sock);
2657     if (rv != APR_SUCCESS) {
2658         return rv;
2659     }
2660
2661     rv = apr_socket_timeout_get(sock, &t);
2662     if (rv != APR_SUCCESS) {
2663         return rv;
2664     }
2665
2666     pathlen = strlen(uds_path);
2667     /* copy the UDS path (including NUL) to the sockaddr_un */
2668     addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) + pathlen;
2669     sa = (struct sockaddr_un *)apr_palloc(p, addrlen + 1);
2670     memcpy(sa->sun_path, uds_path, pathlen + 1);
2671     sa->sun_family = AF_UNIX;
2672
2673     do {
2674         rv = connect(rawsock, (struct sockaddr*)sa, addrlen);
2675     } while (rv == -1 && (rv = errno) == EINTR);
2676
2677     if (rv && rv != EISCONN) {
2678         if ((rv == EINPROGRESS || rv == EALREADY) && (t > 0))  {
2679 #if APR_MAJOR_VERSION < 2
2680             rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
2681 #else
2682             rv = apr_socket_wait(sock, APR_WAIT_WRITE);
2683 #endif
2684         }
2685         if (rv != APR_SUCCESS) {
2686             return rv;
2687         }
2688     }
2689
2690     return APR_SUCCESS;
2691 }
2692 #endif
2693
2694 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
2695                                             proxy_conn_rec *conn,
2696                                             proxy_worker *worker,
2697                                             server_rec *s)
2698 {
2699     apr_status_t rv;
2700     int connected = 0;
2701     int loglevel;
2702     apr_sockaddr_t *backend_addr = conn->addr;
2703     /* the local address to use for the outgoing connection */
2704     apr_sockaddr_t *local_addr;
2705     apr_socket_t *newsock;
2706     void *sconf = s->module_config;
2707     proxy_server_conf *conf =
2708         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
2709
2710     if (conn->sock) {
2711         if (!(connected = ap_proxy_is_socket_connected(conn->sock))) {
2712             socket_cleanup(conn);
2713             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951)
2714                          "%s: backend socket is disconnected.",
2715                          proxy_function);
2716         }
2717     }
2718     while ((backend_addr || conn->uds_path) && !connected) {
2719 #if APR_HAVE_SYS_UN_H
2720         if (conn->uds_path)
2721         {
2722             rv = apr_socket_create(&newsock, AF_UNIX, SOCK_STREAM, 0,
2723                                    conn->scpool);
2724             if (rv != APR_SUCCESS) {
2725                 loglevel = APLOG_ERR;
2726                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453)
2727                              "%s: error creating Unix domain socket for "
2728                              "target %s",
2729                              proxy_function,
2730                              worker->s->hostname);
2731                 break;
2732             }
2733             conn->connection = NULL;
2734
2735             rv = ap_proxy_connect_uds(newsock, conn->uds_path, conn->scpool);
2736             if (rv != APR_SUCCESS) {
2737                 apr_socket_close(newsock);
2738                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02454)
2739                              "%s: attempt to connect to Unix domain socket "
2740                              "%s (%s) failed",
2741                              proxy_function,
2742                              conn->uds_path,
2743                              worker->s->hostname);
2744                 break;
2745             }
2746
2747             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02823)
2748                          "%s: connection established with Unix domain socket "
2749                          "%s (%s)",
2750                          proxy_function,
2751                          conn->uds_path,
2752                          worker->s->hostname);
2753         }
2754         else
2755 #endif
2756         {
2757             if ((rv = apr_socket_create(&newsock, backend_addr->family,
2758                                         SOCK_STREAM, APR_PROTO_TCP,
2759                                         conn->scpool)) != APR_SUCCESS) {
2760                 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2761                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952)
2762                              "%s: error creating fam %d socket for "
2763                              "target %s",
2764                              proxy_function,
2765                              backend_addr->family,
2766                              worker->s->hostname);
2767                 /*
2768                  * this could be an IPv6 address from the DNS but the
2769                  * local machine won't give us an IPv6 socket; hopefully the
2770                  * DNS returned an additional address to try
2771                  */
2772                 backend_addr = backend_addr->next;
2773                 continue;
2774             }
2775             conn->connection = NULL;
2776
2777             if (worker->s->recv_buffer_size > 0 &&
2778                 (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
2779                                          worker->s->recv_buffer_size))) {
2780                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953)
2781                              "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2782                              "ProxyReceiveBufferSize, using default");
2783             }
2784
2785             rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1);
2786             if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2787                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954)
2788                              "apr_socket_opt_set(APR_TCP_NODELAY): "
2789                              "Failed to set");
2790             }
2791
2792             /* Set a timeout for connecting to the backend on the socket */
2793             if (worker->s->conn_timeout_set) {
2794                 apr_socket_timeout_set(newsock, worker->s->conn_timeout);
2795             }
2796             else if (worker->s->timeout_set) {
2797                 apr_socket_timeout_set(newsock, worker->s->timeout);
2798             }
2799             else if (conf->timeout_set) {
2800                 apr_socket_timeout_set(newsock, conf->timeout);
2801             }
2802             else {
2803                 apr_socket_timeout_set(newsock, s->timeout);
2804             }
2805             /* Set a keepalive option */
2806             if (worker->s->keepalive) {
2807                 if ((rv = apr_socket_opt_set(newsock,
2808                                              APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
2809                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955)
2810                                  "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
2811                                  " Keepalive");
2812                 }
2813             }
2814             ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
2815                          "%s: fam %d socket created to connect to %s",
2816                          proxy_function, backend_addr->family, worker->s->hostname);
2817
2818             if (conf->source_address_set) {
2819                 local_addr = apr_pmemdup(conn->scpool, conf->source_address,
2820                                          sizeof(apr_sockaddr_t));
2821                 local_addr->pool = conn->scpool;
2822                 rv = apr_socket_bind(newsock, local_addr);
2823                 if (rv != APR_SUCCESS) {
2824                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956)
2825                                  "%s: failed to bind socket to local address",
2826                                  proxy_function);
2827                 }
2828             }
2829
2830             /* make the connection out of the socket */
2831             rv = apr_socket_connect(newsock, backend_addr);
2832
2833             /* if an error occurred, loop round and try again */
2834             if (rv != APR_SUCCESS) {
2835                 apr_socket_close(newsock);
2836                 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2837                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957)
2838                              "%s: attempt to connect to %pI (%s) failed",
2839                              proxy_function,
2840                              backend_addr,
2841                              worker->s->hostname);
2842                 backend_addr = backend_addr->next;
2843                 continue;
2844             }
2845
2846             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02824)
2847                          "%s: connection established with %pI (%s)",
2848                          proxy_function,
2849                          backend_addr,
2850                          worker->s->hostname);
2851         }
2852
2853         /* Set a timeout on the socket */
2854         if (worker->s->timeout_set) {
2855             apr_socket_timeout_set(newsock, worker->s->timeout);
2856         }
2857         else if (conf->timeout_set) {
2858             apr_socket_timeout_set(newsock, conf->timeout);
2859         }
2860         else {
2861              apr_socket_timeout_set(newsock, s->timeout);
2862         }
2863
2864         conn->sock = newsock;
2865
2866         if (!conn->uds_path && conn->forward) {
2867             forward_info *forward = (forward_info *)conn->forward;
2868             /*
2869              * For HTTP CONNECT we need to prepend CONNECT request before
2870              * sending our actual HTTPS requests.
2871              */
2872             if (forward->use_http_connect) {
2873                 rv = send_http_connect(conn, s);
2874                 /* If an error occurred, loop round and try again */
2875                 if (rv != APR_SUCCESS) {
2876                     conn->sock = NULL;
2877                     apr_socket_close(newsock);
2878                     loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2879                     ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958)
2880                                  "%s: attempt to connect to %s:%d "
2881                                  "via http CONNECT through %pI (%s) failed",
2882                                  proxy_function,
2883                                  forward->target_host, forward->target_port,
2884                                  backend_addr, worker->s->hostname);
2885                     backend_addr = backend_addr->next;
2886                     continue;
2887                 }
2888             }
2889         }
2890
2891         connected    = 1;
2892     }
2893     if (PROXY_WORKER_IS_USABLE(worker)) {
2894         /*
2895          * Put the entire worker to error state if
2896          * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
2897          * Although some connections may be alive
2898          * no further connections to the worker could be made
2899          */
2900         if (!connected) {
2901             if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
2902                 worker->s->error_time = apr_time_now();
2903                 worker->s->status |= PROXY_WORKER_IN_ERROR;
2904                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959)
2905                     "ap_proxy_connect_backend disabling worker for (%s) for %"
2906                     APR_TIME_T_FMT "s",
2907                     worker->s->hostname, apr_time_sec(worker->s->retry));
2908             }
2909         }
2910         else {
2911             if (worker->s->retries) {
2912                 /*
2913                  * A worker came back. So here is where we need to
2914                  * either reset all params to initial conditions or
2915                  * apply some sort of aging
2916                  */
2917             }
2918             worker->s->error_time = 0;
2919             worker->s->retries = 0;
2920         }
2921         return connected ? OK : DECLINED;
2922     }
2923     else {
2924         /*
2925          * The worker is in error likely done by a different thread / process
2926          * e.g. for a timeout or bad status. We should respect this and should
2927          * not continue with a connection via this worker even if we got one.
2928          */
2929         if (connected) {
2930             socket_cleanup(conn);
2931         }
2932         return DECLINED;
2933     }
2934 }
2935
2936 static apr_status_t connection_shutdown(void *theconn)
2937 {
2938     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
2939     conn_rec *c = conn->connection;
2940     if (c) {
2941         if (!c->aborted) {
2942             apr_interval_time_t saved_timeout = 0;
2943             apr_socket_timeout_get(conn->sock, &saved_timeout);
2944             if (saved_timeout) {
2945                 apr_socket_timeout_set(conn->sock, 0);
2946             }
2947
2948             (void)ap_shutdown_conn(c, 0);
2949             c->aborted = 1;
2950
2951             if (saved_timeout) {
2952                 apr_socket_timeout_set(conn->sock, saved_timeout);
2953             }
2954         }
2955
2956         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02642)
2957                       "proxy: connection shutdown");
2958     }
2959     return APR_SUCCESS;
2960 }
2961
2962
2963 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
2964                                               proxy_conn_rec *conn,
2965                                               conn_rec *c,
2966                                               server_rec *s)
2967 {
2968     apr_sockaddr_t *backend_addr = conn->addr;
2969     int rc;
2970     apr_interval_time_t current_timeout;
2971     apr_bucket_alloc_t *bucket_alloc;
2972
2973     if (conn->connection) {
2974         return OK;
2975     }
2976
2977     bucket_alloc = apr_bucket_alloc_create(conn->scpool);
2978     /*
2979      * The socket is now open, create a new backend server connection
2980      */
2981     conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock,
2982                                                 0, NULL,
2983                                                 bucket_alloc);
2984
2985     if (!conn->connection) {
2986         /*
2987          * the peer reset the connection already; ap_run_create_connection()
2988          * closed the socket
2989          */
2990         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
2991                      s, APLOGNO(00960) "%s: an error occurred creating a "
2992                      "new connection to %pI (%s)", proxy_function,
2993                      backend_addr, conn->hostname);
2994         /* XXX: Will be closed when proxy_conn is closed */
2995         socket_cleanup(conn);
2996         return HTTP_INTERNAL_SERVER_ERROR;
2997     }
2998
2999     /* For ssl connection to backend */
3000     if (conn->is_ssl) {
3001         if (!ap_proxy_ssl_enable(conn->connection)) {
3002             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
3003                          s, APLOGNO(00961) "%s: failed to enable ssl support "
3004                          "for %pI (%s)", proxy_function,
3005                          backend_addr, conn->hostname);
3006             return HTTP_INTERNAL_SERVER_ERROR;
3007         }
3008     }
3009     else {
3010         /* TODO: See if this will break FTP */
3011         ap_proxy_ssl_disable(conn->connection);
3012     }
3013
3014     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00962)
3015                  "%s: connection complete to %pI (%s)",
3016                  proxy_function, backend_addr, conn->hostname);
3017
3018     /*
3019      * save the timeout of the socket because core_pre_connection
3020      * will set it to base_server->timeout
3021      * (core TimeOut directive).
3022      */
3023     apr_socket_timeout_get(conn->sock, &current_timeout);
3024     /* set up the connection filters */
3025     rc = ap_run_pre_connection(conn->connection, conn->sock);
3026     if (rc != OK && rc != DONE) {
3027         conn->connection->aborted = 1;
3028         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00963)
3029                      "%s: pre_connection setup failed (%d)",
3030                      proxy_function, rc);
3031         return rc;
3032     }
3033     apr_socket_timeout_set(conn->sock, current_timeout);
3034
3035     /* Shutdown the connection before closing it (eg. SSL connections
3036      * need to be close-notify-ed).
3037      */
3038     apr_pool_pre_cleanup_register(conn->scpool, conn, connection_shutdown);
3039
3040     return OK;
3041 }
3042
3043 int ap_proxy_lb_workers(void)
3044 {
3045     /*
3046      * Since we can't resize the scoreboard when reconfiguring, we
3047      * have to impose a limit on the number of workers, we are
3048      * able to reconfigure to.
3049      */
3050     if (!lb_workers_limit)
3051         lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
3052     return lb_workers_limit;
3053 }
3054
3055 /* deprecated - to be removed in v2.6 */
3056 PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
3057                                            apr_bucket_brigade *brigade)
3058 {
3059     apr_bucket *e;
3060     conn_rec *c = r->connection;
3061
3062     r->no_cache = 1;
3063     /*
3064      * If this is a subrequest, then prevent also caching of the main
3065      * request.
3066      */
3067     if (r->main)
3068         r->main->no_cache = 1;
3069     e = ap_bucket_error_create(HTTP_GATEWAY_TIME_OUT, NULL, c->pool,
3070                                c->bucket_alloc);
3071     APR_BRIGADE_INSERT_TAIL(brigade, e);
3072     e = apr_bucket_eos_create(c->bucket_alloc);
3073     APR_BRIGADE_INSERT_TAIL(brigade, e);
3074 }
3075
3076 /*
3077  * Provide a string hashing function for the proxy.
3078  * We offer 2 methods: one is the APR model but we
3079  * also provide our own, based on either FNV or SDBM.
3080  * The reason is in case we want to use both to ensure no
3081  * collisions.
3082  */
3083 PROXY_DECLARE(unsigned int)
3084 ap_proxy_hashfunc(const char *str, proxy_hash_t method)
3085 {
3086     if (method == PROXY_HASHFUNC_APR) {
3087         apr_ssize_t slen = strlen(str);
3088         return apr_hashfunc_default(str, &slen);
3089     }
3090     else if (method == PROXY_HASHFUNC_FNV) {
3091         /* FNV model */
3092         unsigned int hash;
3093         const unsigned int fnv_prime = 0x811C9DC5;
3094         for (hash = 0; *str; str++) {
3095             hash *= fnv_prime;
3096             hash ^= (*str);
3097         }
3098         return hash;
3099     }
3100     else { /* method == PROXY_HASHFUNC_DEFAULT */
3101         /* SDBM model */
3102         unsigned int hash;
3103         for (hash = 0; *str; str++) {
3104             hash = (*str) + (hash << 6) + (hash << 16) - hash;
3105         }
3106         return hash;
3107     }
3108 }
3109
3110 PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker *w)
3111 {
3112     unsigned int *status = &w->s->status;
3113     char flag = toupper(c);
3114     proxy_wstat_t *pwt = proxy_wstat_tbl;
3115     while (pwt->bit) {
3116         if (flag == pwt->flag) {
3117             if (set)
3118                 *status |= pwt->bit;
3119             else
3120                 *status &= ~(pwt->bit);
3121             return APR_SUCCESS;
3122         }
3123         pwt++;
3124     }
3125     return APR_EINVAL;
3126 }
3127
3128 PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w)
3129 {
3130     char *ret = "";
3131     unsigned int status = w->s->status;
3132     proxy_wstat_t *pwt = proxy_wstat_tbl;
3133     while (pwt->bit) {
3134         if (status & pwt->bit)
3135             ret = apr_pstrcat(p, ret, pwt->name, NULL);
3136         pwt++;
3137     }
3138     if (!*ret) {
3139         ret = "??? ";
3140     }
3141     if (PROXY_WORKER_IS_USABLE(w))
3142         ret = apr_pstrcat(p, ret, "Ok ", NULL);
3143     return ret;
3144 }
3145
3146 PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s,
3147                                                     proxy_server_conf *conf)
3148 {
3149     proxy_worker **workers;
3150     int i;
3151     int index;
3152     proxy_worker_shared *shm;
3153     proxy_balancer_method *lbmethod;
3154     ap_slotmem_provider_t *storage = b->storage;
3155
3156     if (b->s->wupdated <= b->wupdated)
3157         return APR_SUCCESS;
3158     /* balancer sync */
3159     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, b->s->lbpname, "0");
3160     if (lbmethod) {
3161         b->lbmethod = lbmethod;
3162     } else {
3163         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(02433)
3164                      "Cannot find LB Method: %s", b->s->lbpname);
3165         return APR_EINVAL;
3166     }
3167
3168     /* worker sync */
3169
3170     /*
3171      * Look thru the list of workers in shm
3172      * and see which one(s) we are lacking...
3173      * again, the cast to unsigned int is safe
3174      * since our upper limit is always max_workers
3175      * which is int.
3176      */
3177     for (index = 0; index < b->max_workers; index++) {
3178         int found;
3179         apr_status_t rv;
3180         if ((rv = storage->dptr(b->wslot, (unsigned int)index, (void *)&shm)) != APR_SUCCESS) {
3181             ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00965) "worker slotmem_dptr failed");
3182             return APR_EGENERAL;
3183         }
3184         /* account for possible "holes" in the slotmem
3185          * (eg: slots 0-2 are used, but 3 isn't, but 4-5 is)
3186          */
3187         if (!shm->hash.def || !shm->hash.fnv)
3188             continue;
3189         found = 0;
3190         workers = (proxy_worker **)b->workers->elts;
3191         for (i = 0; i < b->workers->nelts; i++, workers++) {
3192             proxy_worker *worker = *workers;
3193             if (worker->hash.def == shm->hash.def && worker->hash.fnv == shm->hash.fnv) {
3194                 found = 1;
3195                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02402)
3196                              "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
3197                              ap_proxy_worker_name(conf->pool, worker));
3198                 break;
3199             }
3200         }
3201         if (!found) {
3202             proxy_worker **runtime;
3203             apr_global_mutex_lock(proxy_mutex);
3204             runtime = apr_array_push(b->workers);
3205             *runtime = apr_palloc(conf->pool, sizeof(proxy_worker));
3206             apr_global_mutex_unlock(proxy_mutex);
3207             (*runtime)->hash = shm->hash;
3208             (*runtime)->context = NULL;
3209             (*runtime)->cp = NULL;
3210             (*runtime)->balancer = b;
3211             (*runtime)->s = shm;
3212             (*runtime)->tmutex = NULL;
3213             rv = ap_proxy_initialize_worker(*runtime, s, conf->pool);
3214             if (rv != APR_SUCCESS) {
3215                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker");
3216                 return rv;
3217             }
3218             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02403)
3219                          "grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
3220                          (*runtime)->s->name);
3221         }
3222     }
3223     if (b->s->need_reset) {
3224         if (b->lbmethod && b->lbmethod->reset)
3225             b->lbmethod->reset(b, s);
3226         b->s->need_reset = 0;
3227     }
3228     b->wupdated = b->s->wupdated;
3229     return APR_SUCCESS;
3230 }
3231
3232 PROXY_DECLARE(proxy_worker_shared *) ap_proxy_find_workershm(ap_slotmem_provider_t *storage,
3233                                                                ap_slotmem_instance_t *slot,
3234                                                                proxy_worker *worker,
3235                                                                unsigned int *index)
3236 {
3237     proxy_worker_shared *shm;
3238     unsigned int i, limit;
3239     limit = storage->num_slots(slot);
3240     for (i = 0; i < limit; i++) {
3241         if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
3242             return NULL;
3243         }
3244         if ((worker->s->hash.def == shm->hash.def) &&
3245             (worker->s->hash.fnv == shm->hash.fnv)) {
3246             *index = i;
3247             return shm;
3248         }
3249     }
3250     return NULL;
3251 }
3252
3253 PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_provider_t *storage,
3254                                                                  ap_slotmem_instance_t *slot,
3255                                                                  proxy_balancer *balancer,
3256                                                                  unsigned int *index)
3257 {
3258     proxy_balancer_shared *shm;
3259     unsigned int i, limit;
3260     limit = storage->num_slots(slot);
3261     for (i = 0; i < limit; i++) {
3262         if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
3263             return NULL;
3264         }
3265         if ((balancer->s->hash.def == shm->hash.def) &&
3266             (balancer->s->hash.fnv == shm->hash.fnv)) {
3267             *index = i;
3268             return shm;
3269         }
3270     }
3271     return NULL;
3272 }
3273
3274 typedef struct header_connection {
3275     apr_pool_t *pool;
3276     apr_array_header_t *array;
3277     const char *error;
3278     int is_req;
3279 } header_connection;
3280
3281 static int find_conn_headers(void *data, const char *key, const char *val)
3282 {
3283     header_connection *x = data;
3284     x->error = ap_parse_token_list_strict(x->pool, val, &x->array, !x->is_req);
3285     return !x->error;
3286 }
3287
3288 /**
3289  * Remove all headers referred to by the Connection header.
3290  * Returns -1 on error. Otherwise, returns 1 if 'Close' was seen in
3291  * the Connection header tokens, and 0 if not.
3292  */
3293 static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers)
3294 {
3295     int closed = 0;
3296     header_connection x;
3297
3298     x.pool = r->pool;
3299     x.array = NULL;
3300     x.error = NULL;
3301     x.is_req = (headers == r->headers_in);
3302
3303     apr_table_unset(headers, "Proxy-Connection");
3304
3305     apr_table_do(find_conn_headers, &x, headers, "Connection", NULL);
3306     apr_table_unset(headers, "Connection");
3307
3308     if (x.error) {
3309         ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02806)
3310                 "Error parsing Connection header: %s", x.error);
3311         return -1;
3312     }
3313
3314     if (x.array) {
3315         int i;
3316         for (i = 0; i < x.array->nelts; i++) {
3317             const char *name = APR_ARRAY_IDX(x.array, i, const char *);
3318             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02807)
3319                           "Removing header '%s' listed in Connection header",
3320                           name);
3321             if (!ap_casecmpstr(name, "close")) {
3322                 closed = 1;
3323             }
3324             apr_table_unset(headers, name);
3325         }
3326     }
3327
3328     return closed;
3329 }
3330
3331 PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
3332                                             apr_bucket_brigade *header_brigade,
3333                                             request_rec *r,
3334                                             proxy_conn_rec *p_conn,
3335                                             proxy_worker *worker,
3336                                             proxy_server_conf *conf,
3337                                             apr_uri_t *uri,
3338                                             char *url, char *server_portstr,
3339                                             char **old_cl_val,
3340                                             char **old_te_val)
3341 {
3342     conn_rec *c = r->connection;
3343     int counter;
3344     char *buf;
3345     const apr_array_header_t *headers_in_array;
3346     const apr_table_entry_t *headers_in;
3347     apr_table_t *saved_headers_in;
3348     apr_bucket *e;
3349     int do_100_continue;
3350     conn_rec *origin = p_conn->connection;
3351     const char *fpr1;
3352     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
3353
3354     /*
3355      * HTTP "Ping" test? Easiest is 100-Continue. However:
3356      * To be compliant, we only use 100-Continue for requests with bodies.
3357      * We also make sure we won't be talking HTTP/1.0 as well.
3358      */
3359     fpr1 = apr_table_get(r->subprocess_env, "force-proxy-request-1.0");
3360     do_100_continue = PROXY_DO_100_CONTINUE(worker, r);
3361
3362     if (fpr1) {
3363         /*
3364          * According to RFC 2616 8.2.3 we are not allowed to forward an
3365          * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
3366          * a HTTP_EXPECTATION_FAILED
3367          */
3368         if (r->expecting_100) {
3369             return HTTP_EXPECTATION_FAILED;
3370         }
3371         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
3372         p_conn->close = 1;
3373     } else {
3374         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
3375     }
3376     if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
3377         if (origin) {
3378             origin->keepalive = AP_CONN_CLOSE;
3379         }
3380         p_conn->close = 1;
3381     }
3382     ap_xlate_proto_to_ascii(buf, strlen(buf));
3383     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3384     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3385     if (dconf->preserve_host == 0) {
3386         if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
3387             if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
3388                 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:",
3389                                   uri->port_str, CRLF, NULL);
3390             } else {
3391                 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL);
3392             }
3393         } else {
3394             if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
3395                 buf = apr_pstrcat(p, "Host: ", uri->hostname, ":",
3396                                   uri->port_str, CRLF, NULL);
3397             } else {
3398                 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
3399             }
3400         }
3401     }
3402     else {
3403         /* don't want to use r->hostname, as the incoming header might have a
3404          * port attached
3405          */
3406         const char* hostname = apr_table_get(r->headers_in,"Host");
3407         if (!hostname) {
3408             hostname =  r->server->server_hostname;
3409             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092)
3410                           "no HTTP 0.9 request (with no host line) "
3411                           "on incoming request and preserve host set "
3412                           "forcing hostname to be %s for uri %s",
3413                           hostname, r->uri);
3414         }
3415         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
3416     }
3417     ap_xlate_proto_to_ascii(buf, strlen(buf));
3418     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3419     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3420
3421     /*
3422      * Save the original headers in here and restore them when leaving, since
3423      * we will apply proxy purpose only modifications (eg. clearing hop-by-hop
3424      * headers, add Via or X-Forwarded-* or Expect...), whereas the originals
3425      * will be needed later to prepare the correct response and logging.
3426      *
3427      * Note: We need to take r->pool for apr_table_copy as the key / value
3428      * pairs in r->headers_in have been created out of r->pool and
3429      * p might be (and actually is) a longer living pool.
3430      * This would trigger the bad pool ancestry abort in apr_table_copy if
3431      * apr is compiled with APR_POOL_DEBUG.
3432      */
3433     saved_headers_in = r->headers_in;
3434     r->headers_in = apr_table_copy(r->pool, saved_headers_in);
3435
3436     /* handle Via */
3437     if (conf->viaopt == via_block) {
3438         /* Block all outgoing Via: headers */
3439         apr_table_unset(r->headers_in, "Via");
3440     } else if (conf->viaopt != via_off) {
3441         const char *server_name = ap_get_server_name(r);
3442         /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
3443          * then the server name returned by ap_get_server_name() is the
3444          * origin server name (which does make too much sense with Via: headers)
3445          * so we use the proxy vhost's name instead.
3446          */
3447         if (server_name == r->hostname)
3448             server_name = r->server->server_hostname;
3449         /* Create a "Via:" request header entry and merge it */
3450         /* Generate outgoing Via: header with/without server comment: */
3451         apr_table_mergen(r->headers_in, "Via",
3452                          (conf->viaopt == via_full)
3453                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
3454                                         HTTP_VERSION_MAJOR(r->proto_num),
3455                                         HTTP_VERSION_MINOR(r->proto_num),
3456                                         server_name, server_portstr,
3457                                         AP_SERVER_BASEVERSION)
3458                          : apr_psprintf(p, "%d.%d %s%s",
3459                                         HTTP_VERSION_MAJOR(r->proto_num),
3460                                         HTTP_VERSION_MINOR(r->proto_num),
3461                                         server_name, server_portstr)
3462                          );
3463     }
3464
3465     /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
3466      * to backend
3467      */
3468     if (do_100_continue) {
3469         const char *val;
3470
3471         if (!r->expecting_100) {
3472             /* Don't forward any "100 Continue" response if the client is
3473              * not expecting it.
3474              */
3475             apr_table_setn(r->subprocess_env, "proxy-interim-response",
3476                                               "Suppress");
3477         }
3478
3479         /* Add the Expect header if not already there. */
3480         if (((val = apr_table_get(r->headers_in, "Expect")) == NULL)
3481                 || (ap_casecmpstr(val, "100-Continue") != 0 /* fast path */
3482                     && !ap_find_token(r->pool, val, "100-Continue"))) {
3483             apr_table_mergen(r->headers_in, "Expect", "100-Continue");
3484         }
3485     }
3486
3487     /* X-Forwarded-*: handling
3488      *
3489      * XXX Privacy Note:
3490      * -----------------
3491      *
3492      * These request headers are only really useful when the mod_proxy
3493      * is used in a reverse proxy configuration, so that useful info
3494      * about the client can be passed through the reverse proxy and on
3495      * to the backend server, which may require the information to
3496      * function properly.
3497      *
3498      * In a forward proxy situation, these options are a potential
3499      * privacy violation, as information about clients behind the proxy
3500      * are revealed to arbitrary servers out there on the internet.
3501      *
3502      * The HTTP/1.1 Via: header is designed for passing client
3503      * information through proxies to a server, and should be used in
3504      * a forward proxy configuation instead of X-Forwarded-*. See the
3505      * ProxyVia option for details.
3506      */
3507     if (dconf->add_forwarded_headers) {
3508         if (PROXYREQ_REVERSE == r->proxyreq) {
3509             const char *buf;
3510
3511             /* Add X-Forwarded-For: so that the upstream has a chance to
3512              * determine, where the original request came from.
3513              */
3514             apr_table_mergen(r->headers_in, "X-Forwarded-For",
3515                              r->useragent_ip);
3516
3517             /* Add X-Forwarded-Host: so that upstream knows what the
3518              * original request hostname was.
3519              */
3520             if ((buf = apr_table_get(r->headers_in, "Host"))) {
3521                 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
3522             }
3523
3524             /* Add X-Forwarded-Server: so that upstream knows what the
3525              * name of this proxy server is (if there are more than one)
3526              * XXX: This duplicates Via: - do we strictly need it?
3527              */
3528             apr_table_mergen(r->headers_in, "X-Forwarded-Server",
3529                              r->server->server_hostname);
3530         }
3531     }
3532
3533     proxy_run_fixups(r);
3534     if (ap_proxy_clear_connection(r, r->headers_in) < 0) {
3535         return HTTP_BAD_REQUEST;
3536     }
3537
3538     /* send request headers */
3539     headers_in_array = apr_table_elts(r->headers_in);
3540     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
3541     for (counter = 0; counter < headers_in_array->nelts; counter++) {
3542         if (headers_in[counter].key == NULL
3543             || headers_in[counter].val == NULL
3544
3545             /* Already sent */
3546             || !ap_casecmpstr(headers_in[counter].key, "Host")
3547
3548             /* Clear out hop-by-hop request headers not to send
3549              * RFC2616 13.5.1 says we should strip these headers
3550              */
3551             || !ap_casecmpstr(headers_in[counter].key, "Keep-Alive")
3552             || !ap_casecmpstr(headers_in[counter].key, "TE")
3553             || !ap_casecmpstr(headers_in[counter].key, "Trailer")
3554             || !ap_casecmpstr(headers_in[counter].key, "Upgrade")
3555
3556             ) {
3557             continue;
3558         }
3559         /* Do we want to strip Proxy-Authorization ?
3560          * If we haven't used it, then NO
3561          * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
3562          * So let's make it configurable by env.
3563          */
3564         if (!ap_casecmpstr(headers_in[counter].key,"Proxy-Authorization")) {
3565             if (r->user != NULL) { /* we've authenticated */
3566                 if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
3567                     continue;
3568                 }
3569             }
3570         }
3571
3572         /* Skip Transfer-Encoding and Content-Length for now.
3573          */
3574         if (!ap_casecmpstr(headers_in[counter].key, "Transfer-Encoding")) {
3575             *old_te_val = headers_in[counter].val;
3576             continue;
3577         }
3578         if (!ap_casecmpstr(headers_in[counter].key, "Content-Length")) {
3579             *old_cl_val = headers_in[counter].val;
3580             continue;
3581         }
3582
3583         /* for sub-requests, ignore freshness/expiry headers */
3584         if (r->main) {
3585             if (   !ap_casecmpstr(headers_in[counter].key, "If-Match")
3586                 || !ap_casecmpstr(headers_in[counter].key, "If-Modified-Since")
3587                 || !ap_casecmpstr(headers_in[counter].key, "If-Range")
3588                 || !ap_casecmpstr(headers_in[counter].key, "If-Unmodified-Since")
3589                 || !ap_casecmpstr(headers_in[counter].key, "If-None-Match")) {
3590                 continue;
3591             }
3592         }
3593
3594         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
3595                           headers_in[counter].val, CRLF,
3596                           NULL);
3597         ap_xlate_proto_to_ascii(buf, strlen(buf));
3598         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3599         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3600     }
3601
3602     /* Restore the original headers in (see comment above),
3603      * we won't modify them anymore.
3604      */
3605     r->headers_in = saved_headers_in;
3606     return OK;
3607 }
3608
3609 PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
3610                                          request_rec *r, proxy_conn_rec *p_conn,
3611                                          conn_rec *origin, apr_bucket_brigade *bb,
3612                                          int flush)
3613 {
3614     apr_status_t status;
3615     apr_off_t transferred;
3616
3617     if (flush) {
3618         apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
3619         APR_BRIGADE_INSERT_TAIL(bb, e);
3620     }
3621     apr_brigade_length(bb, 0, &transferred);
3622     if (transferred != -1)
3623         p_conn->worker->s->transferred += transferred;
3624     status = ap_pass_brigade(origin->output_filters, bb);
3625     /* Cleanup the brigade now to avoid buckets lifetime
3626      * issues in case of error returned below. */
3627     apr_brigade_cleanup(bb);
3628     if (status != APR_SUCCESS) {
3629         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084)
3630                       "pass request body failed to %pI (%s)",
3631                       p_conn->addr, p_conn->hostname);
3632         if (origin->aborted) {
3633             const char *ssl_note;
3634
3635             if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
3636                  != NULL) && (strcmp(ssl_note, "err") == 0)) {
3637                 return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
3638                                      "Error during SSL Handshake with"
3639                                      " remote server");
3640             }
3641             return HTTP_GATEWAY_TIME_OUT;
3642         }
3643         else {
3644             return HTTP_BAD_REQUEST;
3645         }
3646     }
3647     return OK;
3648 }
3649
3650 /* Fill in unknown schemes from apr_uri_port_of_scheme() */
3651
3652 typedef struct proxy_schemes_t {
3653     const char *name;
3654     apr_port_t default_port;
3655 } proxy_schemes_t ;
3656
3657 static proxy_schemes_t pschemes[] =
3658 {
3659     {"fcgi",     8000},
3660     {"ajp",      AJP13_DEF_PORT},
3661     {"scgi",     SCGI_DEF_PORT},
3662     { NULL, 0xFFFF }     /* unknown port */
3663 };
3664
3665 PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme)
3666 {
3667     if (scheme) {
3668         apr_port_t port;
3669         if ((port = apr_uri_port_of_scheme(scheme)) != 0) {
3670             return port;
3671         } else {
3672             proxy_schemes_t *pscheme;
3673             for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) {
3674                 if (ap_casecmpstr(scheme, pscheme->name) == 0) {
3675                     return pscheme->default_port;
3676                 }
3677             }
3678         }
3679     }
3680     return 0;
3681 }
3682
3683 PROXY_DECLARE (const char *) ap_proxy_show_hcmethod(hcmethod_t method)
3684 {
3685     proxy_hcmethods_t *m = proxy_hcmethods;
3686     for (; m->name; m++) {
3687         if (m->method == method) {
3688             return m->name;
3689         }
3690     }
3691     return "???";
3692 }
3693
3694 PROXY_DECLARE(apr_status_t) ap_proxy_buckets_lifetime_transform(request_rec *r,
3695                                                       apr_bucket_brigade *from,
3696                                                       apr_bucket_brigade *to)
3697 {
3698     apr_bucket *e;
3699     apr_bucket *new;
3700     const char *data;
3701     apr_size_t bytes;
3702     apr_status_t rv = APR_SUCCESS;
3703     apr_bucket_alloc_t *bucket_alloc = to->bucket_alloc;
3704
3705     apr_brigade_cleanup(to);
3706     for (e = APR_BRIGADE_FIRST(from);
3707          e != APR_BRIGADE_SENTINEL(from);
3708          e = APR_BUCKET_NEXT(e)) {
3709         if (!APR_BUCKET_IS_METADATA(e)) {
3710             apr_bucket_read(e, &data, &bytes, APR_BLOCK_READ);
3711             new = apr_bucket_transient_create(data, bytes, bucket_alloc);
3712             APR_BRIGADE_INSERT_TAIL(to, new);
3713         }
3714         else if (APR_BUCKET_IS_FLUSH(e)) {
3715             new = apr_bucket_flush_create(bucket_alloc);
3716             APR_BRIGADE_INSERT_TAIL(to, new);
3717         }
3718         else if (APR_BUCKET_IS_EOS(e)) {
3719             new = apr_bucket_eos_create(bucket_alloc);
3720             APR_BRIGADE_INSERT_TAIL(to, new);
3721         }
3722         else {
3723             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03304)
3724                           "Unhandled bucket type of type %s in"
3725                           " ap_proxy_buckets_lifetime_transform", e->type->name);
3726             rv = APR_EGENERAL;
3727         }
3728     }
3729     return rv;
3730 }
3731
3732 void proxy_util_register_hooks(apr_pool_t *p)
3733 {
3734     APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker);
3735     APR_REGISTER_OPTIONAL_FN(ap_proxy_clear_connection);
3736 }