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