]> granicus.if.org Git - apache/blob - modules/proxy/proxy_util.c
2b8b73d4dccf1c539d5bb29d801c3673c0a75578
[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 (PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED)) {
1950             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(3305)
1951                          "%s: Won't retry worker (%s): stopped",
1952                          proxy_function, worker->s->hostname);
1953             return DECLINED;
1954         }
1955         if ((worker->s->status & PROXY_WORKER_IGNORE_ERRORS)
1956             || apr_time_now() > worker->s->error_time + worker->s->retry) {
1957             ++worker->s->retries;
1958             worker->s->status &= ~PROXY_WORKER_IN_ERROR;
1959             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00932)
1960                          "%s: worker for (%s) has been marked for retry",
1961                          proxy_function, worker->s->hostname);
1962             return OK;
1963         }
1964         else {
1965             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00933)
1966                          "%s: too soon to retry worker for (%s)",
1967                          proxy_function, worker->s->hostname);
1968             return DECLINED;
1969         }
1970     }
1971     else {
1972         return OK;
1973     }
1974 }
1975
1976 /*
1977  * In the case of the reverse proxy, we need to see if we
1978  * were passed a UDS url (eg: from mod_proxy) and adjust uds_path
1979  * as required.  
1980  */
1981 static void fix_uds_filename(request_rec *r, char **url) 
1982 {
1983     char *ptr, *ptr2;
1984     if (!r || !r->filename) return;
1985
1986     if (!strncmp(r->filename, "proxy:", 6) &&
1987             (ptr2 = ap_strcasestr(r->filename, "unix:")) &&
1988             (ptr = ap_strchr(ptr2, '|'))) {
1989         apr_uri_t urisock;
1990         apr_status_t rv;
1991         *ptr = '\0';
1992         rv = apr_uri_parse(r->pool, ptr2, &urisock);
1993         if (rv == APR_SUCCESS) {
1994             char *rurl = ptr+1;
1995             char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path);
1996             apr_table_setn(r->notes, "uds_path", sockpath);
1997             *url = apr_pstrdup(r->pool, rurl); /* so we get the scheme for the uds */
1998             /* r->filename starts w/ "proxy:", so add after that */
1999             memmove(r->filename+6, rurl, strlen(rurl)+1);
2000             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2001                     "*: rewrite of url due to UDS(%s): %s (%s)",
2002                     sockpath, *url, r->filename);
2003         }
2004         else {
2005             *ptr = '|';
2006         }
2007     }
2008 }
2009
2010 PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
2011                                         proxy_balancer **balancer,
2012                                         request_rec *r,
2013                                         proxy_server_conf *conf, char **url)
2014 {
2015     int access_status;
2016
2017     access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
2018     if (access_status == DECLINED && *balancer == NULL) {
2019         *worker = ap_proxy_get_worker(r->pool, NULL, conf, *url);
2020         if (*worker) {
2021             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2022                           "%s: found worker %s for %s",
2023                           (*worker)->s->scheme, (*worker)->s->name, *url);
2024             *balancer = NULL;
2025             fix_uds_filename(r, url);
2026             access_status = OK;
2027         }
2028         else if (r->proxyreq == PROXYREQ_PROXY) {
2029             if (conf->forward) {
2030                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2031                               "*: found forward proxy worker for %s", *url);
2032                 *balancer = NULL;
2033                 *worker = conf->forward;
2034                 access_status = OK;
2035                 /*
2036                  * The forward worker does not keep connections alive, so
2037                  * ensure that mod_proxy_http does the correct thing
2038                  * regarding the Connection header in the request.
2039                  */
2040                 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2041             }
2042         }
2043         else if (r->proxyreq == PROXYREQ_REVERSE) {
2044             if (conf->reverse) {
2045                 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2046                               "*: using default reverse proxy worker for %s (no keepalive)", *url);
2047                 *balancer = NULL;
2048                 *worker = conf->reverse;
2049                 access_status = OK;
2050                 /*
2051                  * The reverse worker does not keep connections alive, so
2052                  * ensure that mod_proxy_http does the correct thing
2053                  * regarding the Connection header in the request.
2054                  */
2055                 apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1");
2056                 fix_uds_filename(r, url);
2057             }
2058         }
2059     }
2060     else if (access_status == DECLINED && *balancer != NULL) {
2061         /* All the workers are busy */
2062         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00934)
2063                       "all workers are busy.  Unable to serve %s", *url);
2064         access_status = HTTP_SERVICE_UNAVAILABLE;
2065     }
2066     return access_status;
2067 }
2068
2069 PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
2070                                          proxy_balancer *balancer,
2071                                          request_rec *r,
2072                                          proxy_server_conf *conf)
2073 {
2074     int access_status = OK;
2075     if (balancer) {
2076         access_status = proxy_run_post_request(worker, balancer, r, conf);
2077         if (access_status == DECLINED) {
2078             access_status = OK; /* no post_request handler available */
2079             /* TODO: recycle direct worker */
2080         }
2081     }
2082
2083     return access_status;
2084 }
2085
2086 /* DEPRECATED */
2087 PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
2088                                                const char *proxy_function,
2089                                                apr_sockaddr_t *backend_addr,
2090                                                const char *backend_name,
2091                                                proxy_server_conf *conf,
2092                                                request_rec *r)
2093 {
2094     apr_status_t rv;
2095     int connected = 0;
2096     int loglevel;
2097
2098     while (backend_addr && !connected) {
2099         if ((rv = apr_socket_create(newsock, backend_addr->family,
2100                                     SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
2101             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2102             ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935)
2103                           "%s: error creating fam %d socket for target %s",
2104                           proxy_function, backend_addr->family, backend_name);
2105             /*
2106              * this could be an IPv6 address from the DNS but the
2107              * local machine won't give us an IPv6 socket; hopefully the
2108              * DNS returned an additional address to try
2109              */
2110             backend_addr = backend_addr->next;
2111             continue;
2112         }
2113
2114         if (conf->recv_buffer_size > 0 &&
2115             (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
2116                                      conf->recv_buffer_size))) {
2117             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00936)
2118                           "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2119                           "ProxyReceiveBufferSize, using default");
2120         }
2121
2122         rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1);
2123         if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2124             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00937)
2125                           "apr_socket_opt_set(APR_TCP_NODELAY): "
2126                           "Failed to set");
2127         }
2128
2129         /* Set a timeout on the socket */
2130         if (conf->timeout_set) {
2131             apr_socket_timeout_set(*newsock, conf->timeout);
2132         }
2133         else {
2134             apr_socket_timeout_set(*newsock, r->server->timeout);
2135         }
2136
2137         ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
2138                       "%s: fam %d socket created to connect to %s",
2139                       proxy_function, backend_addr->family, backend_name);
2140
2141         if (conf->source_address) {
2142             apr_sockaddr_t *local_addr;
2143             /* Make a copy since apr_socket_bind() could change
2144              * conf->source_address, which we don't want.
2145              */
2146             local_addr = apr_pmemdup(r->pool, conf->source_address,
2147                                      sizeof(apr_sockaddr_t));
2148             local_addr->pool = r->pool;
2149             rv = apr_socket_bind(*newsock, local_addr);
2150             if (rv != APR_SUCCESS) {
2151                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00938)
2152                               "%s: failed to bind socket to local address",
2153                               proxy_function);
2154             }
2155         }
2156
2157         /* make the connection out of the socket */
2158         rv = apr_socket_connect(*newsock, backend_addr);
2159
2160         /* if an error occurred, loop round and try again */
2161         if (rv != APR_SUCCESS) {
2162             apr_socket_close(*newsock);
2163             loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2164             ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939)
2165                           "%s: attempt to connect to %pI (%s) failed",
2166                           proxy_function, backend_addr, backend_name);
2167             backend_addr = backend_addr->next;
2168             continue;
2169         }
2170         connected = 1;
2171     }
2172     return connected ? 0 : 1;
2173 }
2174
2175 PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
2176                                                proxy_conn_rec **conn,
2177                                                proxy_worker *worker,
2178                                                server_rec *s)
2179 {
2180     apr_status_t rv;
2181
2182     if (!PROXY_WORKER_IS_USABLE(worker)) {
2183         /* Retry the worker */
2184         ap_proxy_retry_worker(proxy_function, worker, s);
2185
2186         if (!PROXY_WORKER_IS_USABLE(worker)) {
2187             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00940)
2188                          "%s: disabled connection for (%s)",
2189                          proxy_function, worker->s->hostname);
2190             return HTTP_SERVICE_UNAVAILABLE;
2191         }
2192     }
2193
2194     if (worker->s->hmax && worker->cp->res) {
2195         rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
2196     }
2197     else {
2198         /* create the new connection if the previous was destroyed */
2199         if (!worker->cp->conn) {
2200             connection_constructor((void **)conn, worker, worker->cp->pool);
2201         }
2202         else {
2203             *conn = worker->cp->conn;
2204             worker->cp->conn = NULL;
2205         }
2206         rv = APR_SUCCESS;
2207     }
2208
2209     if (rv != APR_SUCCESS) {
2210         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00941)
2211                      "%s: failed to acquire connection for (%s)",
2212                      proxy_function, worker->s->hostname);
2213         return HTTP_SERVICE_UNAVAILABLE;
2214     }
2215     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942)
2216                  "%s: has acquired connection for (%s)",
2217                  proxy_function, worker->s->hostname);
2218
2219     (*conn)->worker = worker;
2220     (*conn)->close  = 0;
2221     (*conn)->inreslist = 0;
2222
2223     return OK;
2224 }
2225
2226 PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
2227                                                proxy_conn_rec *conn,
2228                                                server_rec *s)
2229 {
2230     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943)
2231                 "%s: has released connection for (%s)",
2232                 proxy_function, conn->worker->s->hostname);
2233     connection_cleanup(conn);
2234
2235     return OK;
2236 }
2237
2238 PROXY_DECLARE(int)
2239 ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
2240                               proxy_server_conf *conf,
2241                               proxy_worker *worker,
2242                               proxy_conn_rec *conn,
2243                               apr_uri_t *uri,
2244                               char **url,
2245                               const char *proxyname,
2246                               apr_port_t proxyport,
2247                               char *server_portstr,
2248                               int server_portstr_size)
2249 {
2250     int server_port;
2251     apr_status_t err = APR_SUCCESS;
2252     apr_status_t uerr = APR_SUCCESS;
2253     const char *uds_path;
2254
2255     /*
2256      * Break up the URL to determine the host to connect to
2257      */
2258
2259     /* we break the URL into host, port, uri */
2260     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
2261         return ap_proxyerror(r, HTTP_BAD_REQUEST,
2262                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
2263                                          NULL));
2264     }
2265     if (!uri->port) {
2266         uri->port = ap_proxy_port_of_scheme(uri->scheme);
2267     }
2268
2269     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00944)
2270                  "connecting %s to %s:%d", *url, uri->hostname, uri->port);
2271
2272     /*
2273      * allocate these out of the specified connection pool
2274      * The scheme handler decides if this is permanent or
2275      * short living pool.
2276      */
2277     /* Unless we are connecting the backend via a (forward Proxy)Remote, we
2278      * have to use the original form of the URI (non absolute), but this is
2279      * also the case via a remote proxy using the CONNECT method since the
2280      * original request (and URI) is to be embedded in the body.
2281      */
2282     if (!proxyname || conn->is_ssl) {
2283         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
2284                            uri->query ? uri->query : "",
2285                            uri->fragment ? "#" : "",
2286                            uri->fragment ? uri->fragment : "", NULL);
2287     }
2288     /*
2289      * Figure out if our passed in proxy_conn_rec has a usable
2290      * address cached.
2291      *
2292      * TODO: Handle this much better... 
2293      *
2294      * XXX: If generic workers are ever address-reusable, we need 
2295      *      to check host and port on the conn and be careful about
2296      *      spilling the cached addr from the worker.
2297      */
2298     uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path"));
2299     if (uds_path) {
2300         if (conn->uds_path == NULL) {
2301             /* use (*conn)->pool instead of worker->cp->pool to match lifetime */
2302             conn->uds_path = apr_pstrdup(conn->pool, uds_path);
2303         }
2304         if (conn->uds_path) {
2305             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02545)
2306                          "%s: has determined UDS as %s",
2307                          uri->scheme, conn->uds_path);
2308         }
2309         else {
2310             /* should never happen */
2311             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02546)
2312                          "%s: cannot determine UDS (%s)",
2313                          uri->scheme, uds_path);
2314
2315         }
2316         /*
2317          * In UDS cases, some structs are NULL. Protect from de-refs
2318          * and provide info for logging at the same time.
2319          */
2320         if (!conn->addr) {
2321             apr_sockaddr_t *sa;
2322             apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, conn->pool);
2323             conn->addr = sa;
2324         }
2325         conn->hostname = "httpd-UDS";
2326         conn->port = 0;
2327     }
2328     else {
2329         int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse;
2330         if (!conn->hostname || !will_reuse) {
2331             if (proxyname) {
2332                 conn->hostname = apr_pstrdup(conn->pool, proxyname);
2333                 conn->port = proxyport;
2334                 /*
2335                  * If we have a forward proxy and the protocol is HTTPS,
2336                  * then we need to prepend a HTTP CONNECT request before
2337                  * sending our actual HTTPS requests.
2338                  * Save our real backend data for using it later during HTTP CONNECT.
2339                  */
2340                 if (conn->is_ssl) {
2341                     const char *proxy_auth;
2342
2343                     forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info));
2344                     conn->forward = forward;
2345                     forward->use_http_connect = 1;
2346                     forward->target_host = apr_pstrdup(conn->pool, uri->hostname);
2347                     forward->target_port = uri->port;
2348                     /* Do we want to pass Proxy-Authorization along?
2349                      * If we haven't used it, then YES
2350                      * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
2351                      * So let's make it configurable by env.
2352                      * The logic here is the same used in mod_proxy_http.
2353                      */
2354                     proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
2355                     if (proxy_auth != NULL &&
2356                         proxy_auth[0] != '\0' &&
2357                         r->user == NULL && /* we haven't yet authenticated */
2358                         apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
2359                         forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth);
2360                     }
2361                 }
2362             }
2363             else {
2364                 conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
2365                 conn->port = uri->port;
2366             }
2367             if (!will_reuse) {
2368                 /*
2369                  * Only do a lookup if we should not reuse the backend address.
2370                  * Otherwise we will look it up once for the worker.
2371                  */
2372                 err = apr_sockaddr_info_get(&(conn->addr),
2373                                             conn->hostname, APR_UNSPEC,
2374                                             conn->port, 0,
2375                                             conn->pool);
2376             }
2377             socket_cleanup(conn);
2378             conn->close = 0;
2379         }
2380         if (will_reuse) {
2381             /*
2382              * Looking up the backend address for the worker only makes sense if
2383              * we can reuse the address.
2384              */
2385             if (!worker->cp->addr) {
2386                 if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
2387                     ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock");
2388                     return HTTP_INTERNAL_SERVER_ERROR;
2389                 }
2390
2391                 /*
2392                  * Worker can have the single constant backend adress.
2393                  * The single DNS lookup is used once per worker.
2394                  * If dynamic change is needed then set the addr to NULL
2395                  * inside dynamic config to force the lookup.
2396                  */
2397                 err = apr_sockaddr_info_get(&(worker->cp->addr),
2398                                             conn->hostname, APR_UNSPEC,
2399                                             conn->port, 0,
2400                                             worker->cp->pool);
2401                 conn->addr = worker->cp->addr;
2402                 if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
2403                     ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock");
2404                 }
2405             }
2406             else {
2407                 conn->addr = worker->cp->addr;
2408             }
2409         }
2410     }
2411     /* Close a possible existing socket if we are told to do so */
2412     if (conn->close) {
2413         socket_cleanup(conn);
2414         conn->close = 0;
2415     }
2416
2417     if (err != APR_SUCCESS) {
2418         return ap_proxyerror(r, HTTP_GATEWAY_TIME_OUT,
2419                              apr_pstrcat(p, "DNS lookup failure for: ",
2420                                          conn->hostname, NULL));
2421     }
2422
2423     /* Get the server port for the Via headers */
2424     server_port = ap_get_server_port(r);
2425     AP_DEBUG_ASSERT(server_portstr_size > 0);
2426     if (ap_is_default_port(server_port, r)) {
2427         server_portstr[0] = '\0';
2428     }
2429     else {
2430         apr_snprintf(server_portstr, server_portstr_size, ":%d",
2431                      server_port);
2432     }
2433
2434     /* check if ProxyBlock directive on this host */
2435     if (OK != ap_proxy_checkproxyblock(r, conf, uri->hostname, 
2436                                        proxyname ? NULL : conn->addr)) {
2437         return ap_proxyerror(r, HTTP_FORBIDDEN,
2438                              "Connect to remote machine blocked");
2439     }
2440     /*
2441      * When SSL is configured, determine the hostname (SNI) for the request
2442      * and save it in conn->ssl_hostname. Close any reused connection whose
2443      * SNI differs.
2444      */
2445     if (conn->is_ssl) {
2446         proxy_dir_conf *dconf;
2447         const char *ssl_hostname;
2448         /*
2449          * In the case of ProxyPreserveHost on use the hostname of
2450          * the request if present otherwise use the one from the
2451          * backend request URI.
2452          */
2453         dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
2454         if (dconf->preserve_host) {
2455             ssl_hostname = r->hostname;
2456         }
2457         else if (conn->forward
2458                  && ((forward_info *)(conn->forward))->use_http_connect) {
2459             ssl_hostname = ((forward_info *)conn->forward)->target_host;
2460         }
2461         else {
2462             ssl_hostname = conn->hostname;
2463         }
2464         /*
2465          * Close if a SNI is in use but this request requires no or
2466          * a different one, or no SNI is in use but one is required.
2467          */
2468         if ((conn->ssl_hostname && (!ssl_hostname ||
2469                                     strcasecmp(conn->ssl_hostname,
2470                                                ssl_hostname) != 0)) ||
2471                 (!conn->ssl_hostname && ssl_hostname && conn->sock)) {
2472             socket_cleanup(conn);
2473         }
2474         if (conn->ssl_hostname == NULL) {
2475             conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname);
2476         }
2477     }
2478     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00947)
2479                  "connected %s to %s:%d", *url, conn->hostname, conn->port);
2480     return OK;
2481 }
2482
2483 #define USE_ALTERNATE_IS_CONNECTED 1
2484
2485 #if !defined(APR_MSG_PEEK) && defined(MSG_PEEK)
2486 #define APR_MSG_PEEK MSG_PEEK
2487 #endif
2488
2489 #if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK)
2490 PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket)
2491 {
2492     apr_pollfd_t pfds[1];
2493     apr_status_t status;
2494     apr_int32_t  nfds;
2495
2496     pfds[0].reqevents = APR_POLLIN;
2497     pfds[0].desc_type = APR_POLL_SOCKET;
2498     pfds[0].desc.s = socket;
2499
2500     do {
2501         status = apr_poll(&pfds[0], 1, &nfds, 0);
2502     } while (APR_STATUS_IS_EINTR(status));
2503
2504     if (status == APR_SUCCESS && nfds == 1 &&
2505         pfds[0].rtnevents == APR_POLLIN) {
2506         apr_sockaddr_t unused;
2507         apr_size_t len = 1;
2508         char buf[1];
2509         /* The socket might be closed in which case
2510          * the poll will return POLLIN.
2511          * If there is no data available the socket
2512          * is closed.
2513          */
2514         status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK,
2515                                      &buf[0], &len);
2516         if (status == APR_SUCCESS && len)
2517             return 1;
2518         else
2519             return 0;
2520     }
2521     else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2522         return 1;
2523     }
2524     return 0;
2525
2526 }
2527 #else
2528 PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket)
2529
2530 {
2531     apr_size_t buffer_len = 1;
2532     char test_buffer[1];
2533     apr_status_t socket_status;
2534     apr_interval_time_t current_timeout;
2535
2536     /* save timeout */
2537     apr_socket_timeout_get(sock, &current_timeout);
2538     /* set no timeout */
2539     apr_socket_timeout_set(sock, 0);
2540     socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
2541     /* put back old timeout */
2542     apr_socket_timeout_set(sock, current_timeout);
2543     if (APR_STATUS_IS_EOF(socket_status)
2544         || APR_STATUS_IS_ECONNRESET(socket_status)) {
2545         return 0;
2546     }
2547     else {
2548         return 1;
2549     }
2550 }
2551 #endif /* USE_ALTERNATE_IS_CONNECTED */
2552
2553
2554 /*
2555  * Send a HTTP CONNECT request to a forward proxy.
2556  * The proxy is given by "backend", the target server
2557  * is contained in the "forward" member of "backend".
2558  */
2559 static apr_status_t send_http_connect(proxy_conn_rec *backend,
2560                                       server_rec *s)
2561 {
2562     int status;
2563     apr_size_t nbytes;
2564     apr_size_t left;
2565     int complete = 0;
2566     char buffer[HUGE_STRING_LEN];
2567     char drain_buffer[HUGE_STRING_LEN];
2568     forward_info *forward = (forward_info *)backend->forward;
2569     int len = 0;
2570
2571     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00948)
2572                  "CONNECT: sending the CONNECT request for %s:%d "
2573                  "to the remote proxy %pI (%s)",
2574                  forward->target_host, forward->target_port,
2575                  backend->addr, backend->hostname);
2576     /* Create the CONNECT request */
2577     nbytes = apr_snprintf(buffer, sizeof(buffer),
2578                           "CONNECT %s:%d HTTP/1.0" CRLF,
2579                           forward->target_host, forward->target_port);
2580     /* Add proxy authorization from the initial request if necessary */
2581     if (forward->proxy_auth != NULL) {
2582         nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
2583                                "Proxy-Authorization: %s" CRLF,
2584                                forward->proxy_auth);
2585     }
2586     /* Set a reasonable agent and send everything */
2587     nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
2588                            "Proxy-agent: %s" CRLF CRLF,
2589                            ap_get_server_banner());
2590     ap_xlate_proto_to_ascii(buffer, nbytes);
2591     apr_socket_send(backend->sock, buffer, &nbytes);
2592
2593     /* Receive the whole CONNECT response */
2594     left = sizeof(buffer) - 1;
2595     /* Read until we find the end of the headers or run out of buffer */
2596     do {
2597         nbytes = left;
2598         status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
2599         len += nbytes;
2600         left -= nbytes;
2601         buffer[len] = '\0';
2602         if (strstr(buffer + len - nbytes, CRLF_ASCII CRLF_ASCII) != NULL) {
2603             ap_xlate_proto_from_ascii(buffer, len);
2604             complete = 1;
2605             break;
2606         }
2607     } while (status == APR_SUCCESS && left > 0);
2608     /* Drain what's left */
2609     if (!complete) {
2610         nbytes = sizeof(drain_buffer) - 1;
2611         while (status == APR_SUCCESS && nbytes) {
2612             status = apr_socket_recv(backend->sock, drain_buffer, &nbytes);
2613             drain_buffer[nbytes] = '\0';
2614             nbytes = sizeof(drain_buffer) - 1;
2615             if (strstr(drain_buffer, CRLF_ASCII CRLF_ASCII) != NULL) {
2616                 break;
2617             }
2618         }
2619     }
2620
2621     /* Check for HTTP_OK response status */
2622     if (status == APR_SUCCESS) {
2623         unsigned int major, minor;
2624         /* Only scan for three character status code */
2625         char code_str[4];
2626
2627         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00949)
2628                      "send_http_connect: response from the forward proxy: %s",
2629                      buffer);
2630
2631         /* Extract the returned code */
2632         if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
2633             status = atoi(code_str);
2634             if (status == HTTP_OK) {
2635                 status = APR_SUCCESS;
2636             }
2637             else {
2638                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00950)
2639                              "send_http_connect: the forward proxy returned code is '%s'",
2640                              code_str);
2641                 status = APR_INCOMPLETE;
2642             }
2643         }
2644     }
2645
2646     return(status);
2647 }
2648
2649
2650 /* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
2651 PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock,
2652                                                  const char *uds_path,
2653                                                  apr_pool_t *p)
2654 {
2655 #if APR_HAVE_SYS_UN_H
2656     apr_status_t rv;
2657     apr_os_sock_t rawsock;
2658     apr_interval_time_t t;
2659     struct sockaddr_un *sa;
2660     apr_socklen_t addrlen, pathlen;
2661
2662     rv = apr_os_sock_get(&rawsock, sock);
2663     if (rv != APR_SUCCESS) {
2664         return rv;
2665     }
2666
2667     rv = apr_socket_timeout_get(sock, &t);
2668     if (rv != APR_SUCCESS) {
2669         return rv;
2670     }
2671
2672     pathlen = strlen(uds_path);
2673     /* copy the UDS path (including NUL) to the sockaddr_un */
2674     addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) + pathlen;
2675     sa = (struct sockaddr_un *)apr_palloc(p, addrlen + 1);
2676     memcpy(sa->sun_path, uds_path, pathlen + 1);
2677     sa->sun_family = AF_UNIX;
2678
2679     do {
2680         rv = connect(rawsock, (struct sockaddr*)sa, addrlen);
2681     } while (rv == -1 && (rv = errno) == EINTR);
2682
2683     if (rv && rv != EISCONN) {
2684         if ((rv == EINPROGRESS || rv == EALREADY) && (t > 0))  {
2685 #if APR_MAJOR_VERSION < 2
2686             rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
2687 #else
2688             rv = apr_socket_wait(sock, APR_WAIT_WRITE);
2689 #endif
2690         }
2691         if (rv != APR_SUCCESS) {
2692             return rv;
2693         }
2694     }
2695
2696     return APR_SUCCESS;
2697 #else
2698     return APR_ENOTIMPL;
2699 #endif
2700 }
2701
2702 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
2703                                             proxy_conn_rec *conn,
2704                                             proxy_worker *worker,
2705                                             server_rec *s)
2706 {
2707     apr_status_t rv;
2708     int connected = 0;
2709     int loglevel;
2710     apr_sockaddr_t *backend_addr = conn->addr;
2711     /* the local address to use for the outgoing connection */
2712     apr_sockaddr_t *local_addr;
2713     apr_socket_t *newsock;
2714     void *sconf = s->module_config;
2715     proxy_server_conf *conf =
2716         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
2717
2718     if (conn->sock) {
2719         if (!(connected = ap_proxy_is_socket_connected(conn->sock))) {
2720             socket_cleanup(conn);
2721             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951)
2722                          "%s: backend socket is disconnected.",
2723                          proxy_function);
2724         }
2725     }
2726     while ((backend_addr || conn->uds_path) && !connected) {
2727 #if APR_HAVE_SYS_UN_H
2728         if (conn->uds_path)
2729         {
2730             rv = apr_socket_create(&newsock, AF_UNIX, SOCK_STREAM, 0,
2731                                    conn->scpool);
2732             if (rv != APR_SUCCESS) {
2733                 loglevel = APLOG_ERR;
2734                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453)
2735                              "%s: error creating Unix domain socket for "
2736                              "target %s",
2737                              proxy_function,
2738                              worker->s->hostname);
2739                 break;
2740             }
2741             conn->connection = NULL;
2742
2743             rv = ap_proxy_connect_uds(newsock, conn->uds_path, conn->scpool);
2744             if (rv != APR_SUCCESS) {
2745                 apr_socket_close(newsock);
2746                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02454)
2747                              "%s: attempt to connect to Unix domain socket "
2748                              "%s (%s) failed",
2749                              proxy_function,
2750                              conn->uds_path,
2751                              worker->s->hostname);
2752                 break;
2753             }
2754
2755             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02823)
2756                          "%s: connection established with Unix domain socket "
2757                          "%s (%s)",
2758                          proxy_function,
2759                          conn->uds_path,
2760                          worker->s->hostname);
2761         }
2762         else
2763 #endif
2764         {
2765             if ((rv = apr_socket_create(&newsock, backend_addr->family,
2766                                         SOCK_STREAM, APR_PROTO_TCP,
2767                                         conn->scpool)) != APR_SUCCESS) {
2768                 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2769                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952)
2770                              "%s: error creating fam %d socket for "
2771                              "target %s",
2772                              proxy_function,
2773                              backend_addr->family,
2774                              worker->s->hostname);
2775                 /*
2776                  * this could be an IPv6 address from the DNS but the
2777                  * local machine won't give us an IPv6 socket; hopefully the
2778                  * DNS returned an additional address to try
2779                  */
2780                 backend_addr = backend_addr->next;
2781                 continue;
2782             }
2783             conn->connection = NULL;
2784
2785             if (worker->s->recv_buffer_size > 0 &&
2786                 (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
2787                                          worker->s->recv_buffer_size))) {
2788                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953)
2789                              "apr_socket_opt_set(SO_RCVBUF): Failed to set "
2790                              "ProxyReceiveBufferSize, using default");
2791             }
2792
2793             rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1);
2794             if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
2795                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954)
2796                              "apr_socket_opt_set(APR_TCP_NODELAY): "
2797                              "Failed to set");
2798             }
2799
2800             /* Set a timeout for connecting to the backend on the socket */
2801             if (worker->s->conn_timeout_set) {
2802                 apr_socket_timeout_set(newsock, worker->s->conn_timeout);
2803             }
2804             else if (worker->s->timeout_set) {
2805                 apr_socket_timeout_set(newsock, worker->s->timeout);
2806             }
2807             else if (conf->timeout_set) {
2808                 apr_socket_timeout_set(newsock, conf->timeout);
2809             }
2810             else {
2811                 apr_socket_timeout_set(newsock, s->timeout);
2812             }
2813             /* Set a keepalive option */
2814             if (worker->s->keepalive) {
2815                 if ((rv = apr_socket_opt_set(newsock,
2816                                              APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
2817                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955)
2818                                  "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
2819                                  " Keepalive");
2820                 }
2821             }
2822             ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
2823                          "%s: fam %d socket created to connect to %s",
2824                          proxy_function, backend_addr->family, worker->s->hostname);
2825
2826             if (conf->source_address_set) {
2827                 local_addr = apr_pmemdup(conn->scpool, conf->source_address,
2828                                          sizeof(apr_sockaddr_t));
2829                 local_addr->pool = conn->scpool;
2830                 rv = apr_socket_bind(newsock, local_addr);
2831                 if (rv != APR_SUCCESS) {
2832                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956)
2833                                  "%s: failed to bind socket to local address",
2834                                  proxy_function);
2835                 }
2836             }
2837
2838             /* make the connection out of the socket */
2839             rv = apr_socket_connect(newsock, backend_addr);
2840
2841             /* if an error occurred, loop round and try again */
2842             if (rv != APR_SUCCESS) {
2843                 apr_socket_close(newsock);
2844                 loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2845                 ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957)
2846                              "%s: attempt to connect to %pI (%s) failed",
2847                              proxy_function,
2848                              backend_addr,
2849                              worker->s->hostname);
2850                 backend_addr = backend_addr->next;
2851                 continue;
2852             }
2853
2854             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02824)
2855                          "%s: connection established with %pI (%s)",
2856                          proxy_function,
2857                          backend_addr,
2858                          worker->s->hostname);
2859         }
2860
2861         /* Set a timeout on the socket */
2862         if (worker->s->timeout_set) {
2863             apr_socket_timeout_set(newsock, worker->s->timeout);
2864         }
2865         else if (conf->timeout_set) {
2866             apr_socket_timeout_set(newsock, conf->timeout);
2867         }
2868         else {
2869              apr_socket_timeout_set(newsock, s->timeout);
2870         }
2871
2872         conn->sock = newsock;
2873
2874         if (!conn->uds_path && conn->forward) {
2875             forward_info *forward = (forward_info *)conn->forward;
2876             /*
2877              * For HTTP CONNECT we need to prepend CONNECT request before
2878              * sending our actual HTTPS requests.
2879              */
2880             if (forward->use_http_connect) {
2881                 rv = send_http_connect(conn, s);
2882                 /* If an error occurred, loop round and try again */
2883                 if (rv != APR_SUCCESS) {
2884                     conn->sock = NULL;
2885                     apr_socket_close(newsock);
2886                     loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
2887                     ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958)
2888                                  "%s: attempt to connect to %s:%d "
2889                                  "via http CONNECT through %pI (%s) failed",
2890                                  proxy_function,
2891                                  forward->target_host, forward->target_port,
2892                                  backend_addr, worker->s->hostname);
2893                     backend_addr = backend_addr->next;
2894                     continue;
2895                 }
2896             }
2897         }
2898
2899         connected    = 1;
2900     }
2901     if (PROXY_WORKER_IS_USABLE(worker)) {
2902         /*
2903          * Put the entire worker to error state if
2904          * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
2905          * Although some connections may be alive
2906          * no further connections to the worker could be made
2907          */
2908         if (!connected) {
2909             if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
2910                 worker->s->error_time = apr_time_now();
2911                 worker->s->status |= PROXY_WORKER_IN_ERROR;
2912                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959)
2913                     "ap_proxy_connect_backend disabling worker for (%s) for %"
2914                     APR_TIME_T_FMT "s",
2915                     worker->s->hostname, apr_time_sec(worker->s->retry));
2916             }
2917         }
2918         else {
2919             if (worker->s->retries) {
2920                 /*
2921                  * A worker came back. So here is where we need to
2922                  * either reset all params to initial conditions or
2923                  * apply some sort of aging
2924                  */
2925             }
2926             worker->s->error_time = 0;
2927             worker->s->retries = 0;
2928         }
2929         return connected ? OK : DECLINED;
2930     }
2931     else {
2932         /*
2933          * The worker is in error likely done by a different thread / process
2934          * e.g. for a timeout or bad status. We should respect this and should
2935          * not continue with a connection via this worker even if we got one.
2936          */
2937         if (connected) {
2938             socket_cleanup(conn);
2939         }
2940         return DECLINED;
2941     }
2942 }
2943
2944 static apr_status_t connection_shutdown(void *theconn)
2945 {
2946     proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
2947     conn_rec *c = conn->connection;
2948     if (c) {
2949         if (!c->aborted) {
2950             apr_interval_time_t saved_timeout = 0;
2951             apr_socket_timeout_get(conn->sock, &saved_timeout);
2952             if (saved_timeout) {
2953                 apr_socket_timeout_set(conn->sock, 0);
2954             }
2955
2956             (void)ap_shutdown_conn(c, 0);
2957             c->aborted = 1;
2958
2959             if (saved_timeout) {
2960                 apr_socket_timeout_set(conn->sock, saved_timeout);
2961             }
2962         }
2963
2964         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02642)
2965                       "proxy: connection shutdown");
2966     }
2967     return APR_SUCCESS;
2968 }
2969
2970
2971 PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
2972                                               proxy_conn_rec *conn,
2973                                               conn_rec *c,
2974                                               server_rec *s)
2975 {
2976     apr_sockaddr_t *backend_addr = conn->addr;
2977     int rc;
2978     apr_interval_time_t current_timeout;
2979     apr_bucket_alloc_t *bucket_alloc;
2980
2981     if (conn->connection) {
2982         return OK;
2983     }
2984
2985     bucket_alloc = apr_bucket_alloc_create(conn->scpool);
2986     /*
2987      * The socket is now open, create a new backend server connection
2988      */
2989     conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock,
2990                                                 0, NULL,
2991                                                 bucket_alloc);
2992
2993     if (!conn->connection) {
2994         /*
2995          * the peer reset the connection already; ap_run_create_connection()
2996          * closed the socket
2997          */
2998         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
2999                      s, APLOGNO(00960) "%s: an error occurred creating a "
3000                      "new connection to %pI (%s)", proxy_function,
3001                      backend_addr, conn->hostname);
3002         /* XXX: Will be closed when proxy_conn is closed */
3003         socket_cleanup(conn);
3004         return HTTP_INTERNAL_SERVER_ERROR;
3005     }
3006
3007     /* For ssl connection to backend */
3008     if (conn->is_ssl) {
3009         if (!ap_proxy_ssl_enable(conn->connection)) {
3010             ap_log_error(APLOG_MARK, APLOG_ERR, 0,
3011                          s, APLOGNO(00961) "%s: failed to enable ssl support "
3012                          "for %pI (%s)", proxy_function,
3013                          backend_addr, conn->hostname);
3014             return HTTP_INTERNAL_SERVER_ERROR;
3015         }
3016     }
3017     else {
3018         /* TODO: See if this will break FTP */
3019         ap_proxy_ssl_disable(conn->connection);
3020     }
3021
3022     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00962)
3023                  "%s: connection complete to %pI (%s)",
3024                  proxy_function, backend_addr, conn->hostname);
3025
3026     /*
3027      * save the timeout of the socket because core_pre_connection
3028      * will set it to base_server->timeout
3029      * (core TimeOut directive).
3030      */
3031     apr_socket_timeout_get(conn->sock, &current_timeout);
3032     /* set up the connection filters */
3033     rc = ap_run_pre_connection(conn->connection, conn->sock);
3034     if (rc != OK && rc != DONE) {
3035         conn->connection->aborted = 1;
3036         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00963)
3037                      "%s: pre_connection setup failed (%d)",
3038                      proxy_function, rc);
3039         return rc;
3040     }
3041     apr_socket_timeout_set(conn->sock, current_timeout);
3042
3043     /* Shutdown the connection before closing it (eg. SSL connections
3044      * need to be close-notify-ed).
3045      */
3046     apr_pool_pre_cleanup_register(conn->scpool, conn, connection_shutdown);
3047
3048     return OK;
3049 }
3050
3051 int ap_proxy_lb_workers(void)
3052 {
3053     /*
3054      * Since we can't resize the scoreboard when reconfiguring, we
3055      * have to impose a limit on the number of workers, we are
3056      * able to reconfigure to.
3057      */
3058     if (!lb_workers_limit)
3059         lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
3060     return lb_workers_limit;
3061 }
3062
3063 /* deprecated - to be removed in v2.6 */
3064 PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
3065                                            apr_bucket_brigade *brigade)
3066 {
3067     apr_bucket *e;
3068     conn_rec *c = r->connection;
3069
3070     r->no_cache = 1;
3071     /*
3072      * If this is a subrequest, then prevent also caching of the main
3073      * request.
3074      */
3075     if (r->main)
3076         r->main->no_cache = 1;
3077     e = ap_bucket_error_create(HTTP_GATEWAY_TIME_OUT, NULL, c->pool,
3078                                c->bucket_alloc);
3079     APR_BRIGADE_INSERT_TAIL(brigade, e);
3080     e = apr_bucket_eos_create(c->bucket_alloc);
3081     APR_BRIGADE_INSERT_TAIL(brigade, e);
3082 }
3083
3084 /*
3085  * Provide a string hashing function for the proxy.
3086  * We offer 2 methods: one is the APR model but we
3087  * also provide our own, based on either FNV or SDBM.
3088  * The reason is in case we want to use both to ensure no
3089  * collisions.
3090  */
3091 PROXY_DECLARE(unsigned int)
3092 ap_proxy_hashfunc(const char *str, proxy_hash_t method)
3093 {
3094     if (method == PROXY_HASHFUNC_APR) {
3095         apr_ssize_t slen = strlen(str);
3096         return apr_hashfunc_default(str, &slen);
3097     }
3098     else if (method == PROXY_HASHFUNC_FNV) {
3099         /* FNV model */
3100         unsigned int hash;
3101         const unsigned int fnv_prime = 0x811C9DC5;
3102         for (hash = 0; *str; str++) {
3103             hash *= fnv_prime;
3104             hash ^= (*str);
3105         }
3106         return hash;
3107     }
3108     else { /* method == PROXY_HASHFUNC_DEFAULT */
3109         /* SDBM model */
3110         unsigned int hash;
3111         for (hash = 0; *str; str++) {
3112             hash = (*str) + (hash << 6) + (hash << 16) - hash;
3113         }
3114         return hash;
3115     }
3116 }
3117
3118 PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker *w)
3119 {
3120     unsigned int *status = &w->s->status;
3121     char flag = toupper(c);
3122     proxy_wstat_t *pwt = proxy_wstat_tbl;
3123     while (pwt->bit) {
3124         if (flag == pwt->flag) {
3125             if (set)
3126                 *status |= pwt->bit;
3127             else
3128                 *status &= ~(pwt->bit);
3129             return APR_SUCCESS;
3130         }
3131         pwt++;
3132     }
3133     return APR_EINVAL;
3134 }
3135
3136 PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w)
3137 {
3138     char *ret = "";
3139     unsigned int status = w->s->status;
3140     proxy_wstat_t *pwt = proxy_wstat_tbl;
3141     while (pwt->bit) {
3142         if (status & pwt->bit)
3143             ret = apr_pstrcat(p, ret, pwt->name, NULL);
3144         pwt++;
3145     }
3146     if (!*ret) {
3147         ret = "??? ";
3148     }
3149     if (PROXY_WORKER_IS_USABLE(w))
3150         ret = apr_pstrcat(p, ret, "Ok ", NULL);
3151     return ret;
3152 }
3153
3154 PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s,
3155                                                     proxy_server_conf *conf)
3156 {
3157     proxy_worker **workers;
3158     int i;
3159     int index;
3160     proxy_worker_shared *shm;
3161     proxy_balancer_method *lbmethod;
3162     ap_slotmem_provider_t *storage = b->storage;
3163
3164     if (b->s->wupdated <= b->wupdated)
3165         return APR_SUCCESS;
3166     /* balancer sync */
3167     lbmethod = ap_lookup_provider(PROXY_LBMETHOD, b->s->lbpname, "0");
3168     if (lbmethod) {
3169         b->lbmethod = lbmethod;
3170     } else {
3171         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(02433)
3172                      "Cannot find LB Method: %s", b->s->lbpname);
3173         return APR_EINVAL;
3174     }
3175
3176     /* worker sync */
3177
3178     /*
3179      * Look thru the list of workers in shm
3180      * and see which one(s) we are lacking...
3181      * again, the cast to unsigned int is safe
3182      * since our upper limit is always max_workers
3183      * which is int.
3184      */
3185     for (index = 0; index < b->max_workers; index++) {
3186         int found;
3187         apr_status_t rv;
3188         if ((rv = storage->dptr(b->wslot, (unsigned int)index, (void *)&shm)) != APR_SUCCESS) {
3189             ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00965) "worker slotmem_dptr failed");
3190             return APR_EGENERAL;
3191         }
3192         /* account for possible "holes" in the slotmem
3193          * (eg: slots 0-2 are used, but 3 isn't, but 4-5 is)
3194          */
3195         if (!shm->hash.def || !shm->hash.fnv)
3196             continue;
3197         found = 0;
3198         workers = (proxy_worker **)b->workers->elts;
3199         for (i = 0; i < b->workers->nelts; i++, workers++) {
3200             proxy_worker *worker = *workers;
3201             if (worker->hash.def == shm->hash.def && worker->hash.fnv == shm->hash.fnv) {
3202                 found = 1;
3203                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02402)
3204                              "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
3205                              ap_proxy_worker_name(conf->pool, worker));
3206                 break;
3207             }
3208         }
3209         if (!found) {
3210             proxy_worker **runtime;
3211             apr_global_mutex_lock(proxy_mutex);
3212             runtime = apr_array_push(b->workers);
3213             *runtime = apr_palloc(conf->pool, sizeof(proxy_worker));
3214             apr_global_mutex_unlock(proxy_mutex);
3215             (*runtime)->hash = shm->hash;
3216             (*runtime)->context = NULL;
3217             (*runtime)->cp = NULL;
3218             (*runtime)->balancer = b;
3219             (*runtime)->s = shm;
3220             (*runtime)->tmutex = NULL;
3221             rv = ap_proxy_initialize_worker(*runtime, s, conf->pool);
3222             if (rv != APR_SUCCESS) {
3223                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker");
3224                 return rv;
3225             }
3226             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02403)
3227                          "grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm,
3228                          (*runtime)->s->name);
3229         }
3230     }
3231     if (b->s->need_reset) {
3232         if (b->lbmethod && b->lbmethod->reset)
3233             b->lbmethod->reset(b, s);
3234         b->s->need_reset = 0;
3235     }
3236     b->wupdated = b->s->wupdated;
3237     return APR_SUCCESS;
3238 }
3239
3240 PROXY_DECLARE(proxy_worker_shared *) ap_proxy_find_workershm(ap_slotmem_provider_t *storage,
3241                                                                ap_slotmem_instance_t *slot,
3242                                                                proxy_worker *worker,
3243                                                                unsigned int *index)
3244 {
3245     proxy_worker_shared *shm;
3246     unsigned int i, limit;
3247     limit = storage->num_slots(slot);
3248     for (i = 0; i < limit; i++) {
3249         if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
3250             return NULL;
3251         }
3252         if ((worker->s->hash.def == shm->hash.def) &&
3253             (worker->s->hash.fnv == shm->hash.fnv)) {
3254             *index = i;
3255             return shm;
3256         }
3257     }
3258     return NULL;
3259 }
3260
3261 PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_provider_t *storage,
3262                                                                  ap_slotmem_instance_t *slot,
3263                                                                  proxy_balancer *balancer,
3264                                                                  unsigned int *index)
3265 {
3266     proxy_balancer_shared *shm;
3267     unsigned int i, limit;
3268     limit = storage->num_slots(slot);
3269     for (i = 0; i < limit; i++) {
3270         if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) {
3271             return NULL;
3272         }
3273         if ((balancer->s->hash.def == shm->hash.def) &&
3274             (balancer->s->hash.fnv == shm->hash.fnv)) {
3275             *index = i;
3276             return shm;
3277         }
3278     }
3279     return NULL;
3280 }
3281
3282 typedef struct header_connection {
3283     apr_pool_t *pool;
3284     apr_array_header_t *array;
3285     const char *error;
3286     int is_req;
3287 } header_connection;
3288
3289 static int find_conn_headers(void *data, const char *key, const char *val)
3290 {
3291     header_connection *x = data;
3292     x->error = ap_parse_token_list_strict(x->pool, val, &x->array, !x->is_req);
3293     return !x->error;
3294 }
3295
3296 /**
3297  * Remove all headers referred to by the Connection header.
3298  * Returns -1 on error. Otherwise, returns 1 if 'Close' was seen in
3299  * the Connection header tokens, and 0 if not.
3300  */
3301 static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers)
3302 {
3303     int closed = 0;
3304     header_connection x;
3305
3306     x.pool = r->pool;
3307     x.array = NULL;
3308     x.error = NULL;
3309     x.is_req = (headers == r->headers_in);
3310
3311     apr_table_unset(headers, "Proxy-Connection");
3312
3313     apr_table_do(find_conn_headers, &x, headers, "Connection", NULL);
3314     apr_table_unset(headers, "Connection");
3315
3316     if (x.error) {
3317         ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02806)
3318                 "Error parsing Connection header: %s", x.error);
3319         return -1;
3320     }
3321
3322     if (x.array) {
3323         int i;
3324         for (i = 0; i < x.array->nelts; i++) {
3325             const char *name = APR_ARRAY_IDX(x.array, i, const char *);
3326             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02807)
3327                           "Removing header '%s' listed in Connection header",
3328                           name);
3329             if (!ap_casecmpstr(name, "close")) {
3330                 closed = 1;
3331             }
3332             apr_table_unset(headers, name);
3333         }
3334     }
3335
3336     return closed;
3337 }
3338
3339 PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p,
3340                                             apr_bucket_brigade *header_brigade,
3341                                             request_rec *r,
3342                                             proxy_conn_rec *p_conn,
3343                                             proxy_worker *worker,
3344                                             proxy_server_conf *conf,
3345                                             apr_uri_t *uri,
3346                                             char *url, char *server_portstr,
3347                                             char **old_cl_val,
3348                                             char **old_te_val)
3349 {
3350     conn_rec *c = r->connection;
3351     int counter;
3352     char *buf;
3353     const apr_array_header_t *headers_in_array;
3354     const apr_table_entry_t *headers_in;
3355     apr_table_t *saved_headers_in;
3356     apr_bucket *e;
3357     int do_100_continue;
3358     conn_rec *origin = p_conn->connection;
3359     const char *fpr1;
3360     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
3361
3362     /*
3363      * HTTP "Ping" test? Easiest is 100-Continue. However:
3364      * To be compliant, we only use 100-Continue for requests with bodies.
3365      * We also make sure we won't be talking HTTP/1.0 as well.
3366      */
3367     fpr1 = apr_table_get(r->subprocess_env, "force-proxy-request-1.0");
3368     do_100_continue = PROXY_DO_100_CONTINUE(worker, r);
3369
3370     if (fpr1) {
3371         /*
3372          * According to RFC 2616 8.2.3 we are not allowed to forward an
3373          * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
3374          * a HTTP_EXPECTATION_FAILED
3375          */
3376         if (r->expecting_100) {
3377             return HTTP_EXPECTATION_FAILED;
3378         }
3379         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
3380         p_conn->close = 1;
3381     } else {
3382         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
3383     }
3384     if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
3385         if (origin) {
3386             origin->keepalive = AP_CONN_CLOSE;
3387         }
3388         p_conn->close = 1;
3389     }
3390     ap_xlate_proto_to_ascii(buf, strlen(buf));
3391     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3392     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3393     if (dconf->preserve_host == 0) {
3394         if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */
3395             if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
3396                 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:",
3397                                   uri->port_str, CRLF, NULL);
3398             } else {
3399                 buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL);
3400             }
3401         } else {
3402             if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
3403                 buf = apr_pstrcat(p, "Host: ", uri->hostname, ":",
3404                                   uri->port_str, CRLF, NULL);
3405             } else {
3406                 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
3407             }
3408         }
3409     }
3410     else {
3411         /* don't want to use r->hostname, as the incoming header might have a
3412          * port attached
3413          */
3414         const char* hostname = apr_table_get(r->headers_in,"Host");
3415         if (!hostname) {
3416             hostname =  r->server->server_hostname;
3417             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092)
3418                           "no HTTP 0.9 request (with no host line) "
3419                           "on incoming request and preserve host set "
3420                           "forcing hostname to be %s for uri %s",
3421                           hostname, r->uri);
3422         }
3423         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
3424     }
3425     ap_xlate_proto_to_ascii(buf, strlen(buf));
3426     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3427     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3428
3429     /*
3430      * Save the original headers in here and restore them when leaving, since
3431      * we will apply proxy purpose only modifications (eg. clearing hop-by-hop
3432      * headers, add Via or X-Forwarded-* or Expect...), whereas the originals
3433      * will be needed later to prepare the correct response and logging.
3434      *
3435      * Note: We need to take r->pool for apr_table_copy as the key / value
3436      * pairs in r->headers_in have been created out of r->pool and
3437      * p might be (and actually is) a longer living pool.
3438      * This would trigger the bad pool ancestry abort in apr_table_copy if
3439      * apr is compiled with APR_POOL_DEBUG.
3440      */
3441     saved_headers_in = r->headers_in;
3442     r->headers_in = apr_table_copy(r->pool, saved_headers_in);
3443
3444     /* handle Via */
3445     if (conf->viaopt == via_block) {
3446         /* Block all outgoing Via: headers */
3447         apr_table_unset(r->headers_in, "Via");
3448     } else if (conf->viaopt != via_off) {
3449         const char *server_name = ap_get_server_name(r);
3450         /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
3451          * then the server name returned by ap_get_server_name() is the
3452          * origin server name (which does make too much sense with Via: headers)
3453          * so we use the proxy vhost's name instead.
3454          */
3455         if (server_name == r->hostname)
3456             server_name = r->server->server_hostname;
3457         /* Create a "Via:" request header entry and merge it */
3458         /* Generate outgoing Via: header with/without server comment: */
3459         apr_table_mergen(r->headers_in, "Via",
3460                          (conf->viaopt == via_full)
3461                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
3462                                         HTTP_VERSION_MAJOR(r->proto_num),
3463                                         HTTP_VERSION_MINOR(r->proto_num),
3464                                         server_name, server_portstr,
3465                                         AP_SERVER_BASEVERSION)
3466                          : apr_psprintf(p, "%d.%d %s%s",
3467                                         HTTP_VERSION_MAJOR(r->proto_num),
3468                                         HTTP_VERSION_MINOR(r->proto_num),
3469                                         server_name, server_portstr)
3470                          );
3471     }
3472
3473     /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
3474      * to backend
3475      */
3476     if (do_100_continue) {
3477         const char *val;
3478
3479         if (!r->expecting_100) {
3480             /* Don't forward any "100 Continue" response if the client is
3481              * not expecting it.
3482              */
3483             apr_table_setn(r->subprocess_env, "proxy-interim-response",
3484                                               "Suppress");
3485         }
3486
3487         /* Add the Expect header if not already there. */
3488         if (((val = apr_table_get(r->headers_in, "Expect")) == NULL)
3489                 || (ap_casecmpstr(val, "100-Continue") != 0 /* fast path */
3490                     && !ap_find_token(r->pool, val, "100-Continue"))) {
3491             apr_table_mergen(r->headers_in, "Expect", "100-Continue");
3492         }
3493     }
3494
3495     /* X-Forwarded-*: handling
3496      *
3497      * XXX Privacy Note:
3498      * -----------------
3499      *
3500      * These request headers are only really useful when the mod_proxy
3501      * is used in a reverse proxy configuration, so that useful info
3502      * about the client can be passed through the reverse proxy and on
3503      * to the backend server, which may require the information to
3504      * function properly.
3505      *
3506      * In a forward proxy situation, these options are a potential
3507      * privacy violation, as information about clients behind the proxy
3508      * are revealed to arbitrary servers out there on the internet.
3509      *
3510      * The HTTP/1.1 Via: header is designed for passing client
3511      * information through proxies to a server, and should be used in
3512      * a forward proxy configuation instead of X-Forwarded-*. See the
3513      * ProxyVia option for details.
3514      */
3515     if (dconf->add_forwarded_headers) {
3516         if (PROXYREQ_REVERSE == r->proxyreq) {
3517             const char *buf;
3518
3519             /* Add X-Forwarded-For: so that the upstream has a chance to
3520              * determine, where the original request came from.
3521              */
3522             apr_table_mergen(r->headers_in, "X-Forwarded-For",
3523                              r->useragent_ip);
3524
3525             /* Add X-Forwarded-Host: so that upstream knows what the
3526              * original request hostname was.
3527              */
3528             if ((buf = apr_table_get(r->headers_in, "Host"))) {
3529                 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
3530             }
3531
3532             /* Add X-Forwarded-Server: so that upstream knows what the
3533              * name of this proxy server is (if there are more than one)
3534              * XXX: This duplicates Via: - do we strictly need it?
3535              */
3536             apr_table_mergen(r->headers_in, "X-Forwarded-Server",
3537                              r->server->server_hostname);
3538         }
3539     }
3540
3541     proxy_run_fixups(r);
3542     if (ap_proxy_clear_connection(r, r->headers_in) < 0) {
3543         return HTTP_BAD_REQUEST;
3544     }
3545
3546     /* send request headers */
3547     headers_in_array = apr_table_elts(r->headers_in);
3548     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
3549     for (counter = 0; counter < headers_in_array->nelts; counter++) {
3550         if (headers_in[counter].key == NULL
3551             || headers_in[counter].val == NULL
3552
3553             /* Already sent */
3554             || !ap_casecmpstr(headers_in[counter].key, "Host")
3555
3556             /* Clear out hop-by-hop request headers not to send
3557              * RFC2616 13.5.1 says we should strip these headers
3558              */
3559             || !ap_casecmpstr(headers_in[counter].key, "Keep-Alive")
3560             || !ap_casecmpstr(headers_in[counter].key, "TE")
3561             || !ap_casecmpstr(headers_in[counter].key, "Trailer")
3562             || !ap_casecmpstr(headers_in[counter].key, "Upgrade")
3563
3564             ) {
3565             continue;
3566         }
3567         /* Do we want to strip Proxy-Authorization ?
3568          * If we haven't used it, then NO
3569          * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
3570          * So let's make it configurable by env.
3571          */
3572         if (!ap_casecmpstr(headers_in[counter].key,"Proxy-Authorization")) {
3573             if (r->user != NULL) { /* we've authenticated */
3574                 if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
3575                     continue;
3576                 }
3577             }
3578         }
3579
3580         /* Skip Transfer-Encoding and Content-Length for now.
3581          */
3582         if (!ap_casecmpstr(headers_in[counter].key, "Transfer-Encoding")) {
3583             *old_te_val = headers_in[counter].val;
3584             continue;
3585         }
3586         if (!ap_casecmpstr(headers_in[counter].key, "Content-Length")) {
3587             *old_cl_val = headers_in[counter].val;
3588             continue;
3589         }
3590
3591         /* for sub-requests, ignore freshness/expiry headers */
3592         if (r->main) {
3593             if (   !ap_casecmpstr(headers_in[counter].key, "If-Match")
3594                 || !ap_casecmpstr(headers_in[counter].key, "If-Modified-Since")
3595                 || !ap_casecmpstr(headers_in[counter].key, "If-Range")
3596                 || !ap_casecmpstr(headers_in[counter].key, "If-Unmodified-Since")
3597                 || !ap_casecmpstr(headers_in[counter].key, "If-None-Match")) {
3598                 continue;
3599             }
3600         }
3601
3602         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
3603                           headers_in[counter].val, CRLF,
3604                           NULL);
3605         ap_xlate_proto_to_ascii(buf, strlen(buf));
3606         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
3607         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
3608     }
3609
3610     /* Restore the original headers in (see comment above),
3611      * we won't modify them anymore.
3612      */
3613     r->headers_in = saved_headers_in;
3614     return OK;
3615 }
3616
3617 PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
3618                                          request_rec *r, proxy_conn_rec *p_conn,
3619                                          conn_rec *origin, apr_bucket_brigade *bb,
3620                                          int flush)
3621 {
3622     apr_status_t status;
3623     apr_off_t transferred;
3624
3625     if (flush) {
3626         apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
3627         APR_BRIGADE_INSERT_TAIL(bb, e);
3628     }
3629     apr_brigade_length(bb, 0, &transferred);
3630     if (transferred != -1)
3631         p_conn->worker->s->transferred += transferred;
3632     status = ap_pass_brigade(origin->output_filters, bb);
3633     /* Cleanup the brigade now to avoid buckets lifetime
3634      * issues in case of error returned below. */
3635     apr_brigade_cleanup(bb);
3636     if (status != APR_SUCCESS) {
3637         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084)
3638                       "pass request body failed to %pI (%s)",
3639                       p_conn->addr, p_conn->hostname);
3640         if (origin->aborted) {
3641             const char *ssl_note;
3642
3643             if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv"))
3644                  != NULL) && (strcmp(ssl_note, "err") == 0)) {
3645                 return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
3646                                      "Error during SSL Handshake with"
3647                                      " remote server");
3648             }
3649             return HTTP_GATEWAY_TIME_OUT;
3650         }
3651         else {
3652             return HTTP_BAD_REQUEST;
3653         }
3654     }
3655     return OK;
3656 }
3657
3658 /* Fill in unknown schemes from apr_uri_port_of_scheme() */
3659
3660 typedef struct proxy_schemes_t {
3661     const char *name;
3662     apr_port_t default_port;
3663 } proxy_schemes_t ;
3664
3665 static proxy_schemes_t pschemes[] =
3666 {
3667     {"fcgi",     8000},
3668     {"ajp",      AJP13_DEF_PORT},
3669     {"scgi",     SCGI_DEF_PORT},
3670     { NULL, 0xFFFF }     /* unknown port */
3671 };
3672
3673 PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme)
3674 {
3675     if (scheme) {
3676         apr_port_t port;
3677         if ((port = apr_uri_port_of_scheme(scheme)) != 0) {
3678             return port;
3679         } else {
3680             proxy_schemes_t *pscheme;
3681             for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) {
3682                 if (ap_casecmpstr(scheme, pscheme->name) == 0) {
3683                     return pscheme->default_port;
3684                 }
3685             }
3686         }
3687     }
3688     return 0;
3689 }
3690
3691 PROXY_DECLARE (const char *) ap_proxy_show_hcmethod(hcmethod_t method)
3692 {
3693     proxy_hcmethods_t *m = proxy_hcmethods;
3694     for (; m->name; m++) {
3695         if (m->method == method) {
3696             return m->name;
3697         }
3698     }
3699     return "???";
3700 }
3701
3702 PROXY_DECLARE(apr_status_t) ap_proxy_buckets_lifetime_transform(request_rec *r,
3703                                                       apr_bucket_brigade *from,
3704                                                       apr_bucket_brigade *to)
3705 {
3706     apr_bucket *e;
3707     apr_bucket *new;
3708     const char *data;
3709     apr_size_t bytes;
3710     apr_status_t rv = APR_SUCCESS;
3711     apr_bucket_alloc_t *bucket_alloc = to->bucket_alloc;
3712
3713     apr_brigade_cleanup(to);
3714     for (e = APR_BRIGADE_FIRST(from);
3715          e != APR_BRIGADE_SENTINEL(from);
3716          e = APR_BUCKET_NEXT(e)) {
3717         if (!APR_BUCKET_IS_METADATA(e)) {
3718             apr_bucket_read(e, &data, &bytes, APR_BLOCK_READ);
3719             new = apr_bucket_transient_create(data, bytes, bucket_alloc);
3720             APR_BRIGADE_INSERT_TAIL(to, new);
3721         }
3722         else if (APR_BUCKET_IS_FLUSH(e)) {
3723             new = apr_bucket_flush_create(bucket_alloc);
3724             APR_BRIGADE_INSERT_TAIL(to, new);
3725         }
3726         else if (APR_BUCKET_IS_EOS(e)) {
3727             new = apr_bucket_eos_create(bucket_alloc);
3728             APR_BRIGADE_INSERT_TAIL(to, new);
3729         }
3730         else {
3731             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03304)
3732                           "Unhandled bucket type of type %s in"
3733                           " ap_proxy_buckets_lifetime_transform", e->type->name);
3734             rv = APR_EGENERAL;
3735         }
3736     }
3737     return rv;
3738 }
3739
3740 PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections(
3741                                                        request_rec *r,
3742                                                        conn_rec *c_i,
3743                                                        conn_rec *c_o,
3744                                                        apr_bucket_brigade *bb_i,
3745                                                        apr_bucket_brigade *bb_o,
3746                                                        const char *name,
3747                                                        int *sent,
3748                                                        apr_off_t bsize,
3749                                                        int after)
3750 {
3751     apr_status_t rv;
3752 #ifdef DEBUGGING
3753     apr_off_t len;
3754 #endif
3755
3756     do {
3757         apr_brigade_cleanup(bb_i);
3758         rv = ap_get_brigade(c_i->input_filters, bb_i, AP_MODE_READBYTES,
3759                             APR_NONBLOCK_READ, bsize);
3760         if (rv == APR_SUCCESS) {
3761             if (c_o->aborted) {
3762                 return APR_EPIPE;
3763             }
3764             if (APR_BRIGADE_EMPTY(bb_i)) {
3765                 break;
3766             }
3767 #ifdef DEBUGGING
3768             len = -1;
3769             apr_brigade_length(bb_i, 0, &len);
3770             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
3771                           "ap_proxy_transfer_between_connections: "
3772                           "read %" APR_OFF_T_FMT
3773                           " bytes from %s", len, name);
3774 #endif
3775             if (sent) {
3776                 *sent = 1;
3777             }
3778             ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o);
3779             if (!after) {
3780                 apr_bucket *b;
3781
3782                 /*
3783                  * Do not use ap_fflush here since this would cause the flush
3784                  * bucket to be sent in a separate brigade afterwards which
3785                  * causes some filters to set aside the buckets from the first
3786                  * brigade and process them when the flush arrives in the second
3787                  * brigade. As set asides of our transformed buckets involve
3788                  * memory copying we try to avoid this. If we have the flush
3789                  * bucket in the first brigade they directly process the
3790                  * buckets without setting them aside.
3791                  */
3792                 b = apr_bucket_flush_create(bb_o->bucket_alloc);
3793                 APR_BRIGADE_INSERT_TAIL(bb_o, b);
3794             }
3795             rv = ap_pass_brigade(c_o->output_filters, bb_o);
3796             if (rv != APR_SUCCESS) {
3797                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
3798                               "ap_proxy_transfer_between_connections: "
3799                               "error on %s - ap_pass_brigade",
3800                               name);
3801             }
3802         } else if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
3803             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO()
3804                           "ap_proxy_transfer_between_connections: "
3805                           "error on %s - ap_get_brigade",
3806                           name);
3807         }
3808     } while (rv == APR_SUCCESS);
3809
3810     if (after) {
3811         ap_fflush(c_o->output_filters, bb_o);
3812     }
3813
3814     ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r,
3815                   "ap_proxy_transfer_between_connections complete");
3816
3817     if (APR_STATUS_IS_EAGAIN(rv)) {
3818         rv = APR_SUCCESS;
3819     }
3820
3821     return rv;
3822 }
3823
3824 void proxy_util_register_hooks(apr_pool_t *p)
3825 {
3826     APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker);
3827     APR_REGISTER_OPTIONAL_FN(ap_proxy_clear_connection);
3828 }