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