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