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