Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Apache/modules/proxy/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 10.5.2025 mit Größe 210 kB image not shown  

Quelle  proxy_util.c   Sprache: C

 
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* Utility routines for Apache proxy */
#include "mod_proxy.h"
#include "ap_mpm.h"
#include "scoreboard.h"
#include "apr_version.h"
#include "apr_strings.h"
#include "apr_hash.h"
#include "apr_atomic.h"
#include "http_core.h"
#include "proxy_util.h"
#include "ajp.h"
#include "scgi.h"

#include "mpm_common.h" /* for ap_max_mem_free */

#include "mod_http2.h" /* for http2_get_num_workers() */

#if APR_HAVE_UNISTD_H
#include <unistd.h>         /* for getpid() */
#endif

#if APR_HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#if (APR_MAJOR_VERSION < 2)
#include "apr_support.h"        /* for apr_wait_for_io_or_timeout() */
#endif

APLOG_USE_MODULE(proxy);

/*
 * Opaque structure containing infos for CONNECT-ing an origin server through a
 * remote (forward) proxy. Saved in the (opaque) proxy_conn_rec::forward pointer
 * field for backend connections kept alive, allowing for reuse when subsequent
 * requests should be routed through the same remote proxy.
 */

typedef struct {
    const char   *proxy_auth;      /* Proxy authorization */
    const char   *target_host;     /* Target/origin hostname */
    apr_port_t   target_port;      /* Target/origin port */
} remote_connect_info;

/*
 * Opaque structure containing a refcounted and TTL'ed address.
 */

typedef struct proxy_address {
    apr_sockaddr_t *addr;       /* Remote address info */
    const char *hostname;       /* Remote host name */
    apr_port_t hostport;        /* Remote host port */
    apr_uint32_t refcount;      /* Number of conns and/or worker using it */
    apr_uint32_t expiry;        /* Expiry timestamp (seconds to proxy_start_time) */
} proxy_address;

/* Global balancer counter */
int PROXY_DECLARE_DATA proxy_lb_workers = 0;
static int lb_workers_limit = 0;
const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_path;
const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_domain;

extern apr_global_mutex_t *proxy_mutex;

static const apr_time_t *proxy_start_time; /* epoch for expiring addresses */

static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s);
static proxy_worker *proxy_balancer_get_best_worker(proxy_balancer *balancer,
                                                    request_rec *r,
                                                    proxy_is_best_callback_fn_t *is_best,
                                                    void *baton);

APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
                                   (request_rec *r, request_rec *pr), (r, pr),
                                   OK, DECLINED)

PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src,
                                             apr_size_t dlen)
{
    char *thenil;
    apr_size_t thelen;

    /* special case handling */
    if (!dlen) {
        /* XXX: APR_ENOSPACE would be better */
        return APR_EGENERAL;
    }
    if (!src) {
        *dst = '\0';
        return APR_SUCCESS;
    }
    thenil = apr_cpystrn(dst, src, dlen);
    thelen = thenil - dst;
    if (src[thelen] == '\0') {
        return APR_SUCCESS;
    }
    return APR_EGENERAL;
}

/* already called in the knowledge that the characters are hex digits */
PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
{
    int i;

#if !APR_CHARSET_EBCDIC
    int ch = x[0];

    if (apr_isdigit(ch)) {
        i = ch - '0';
    }
    else if (apr_isupper(ch)) {
        i = ch - ('A' - 10);
    }
    else {
        i = ch - ('a' - 10);
    }
    i <<= 4;

    ch = x[1];
    if (apr_isdigit(ch)) {
        i += ch - '0';
    }
    else if (apr_isupper(ch)) {
        i += ch - ('A' - 10);
    }
    else {
        i += ch - ('a' - 10);
    }
    return i;
#else /*APR_CHARSET_EBCDIC*/
    /*
     * we assume that the hex value refers to an ASCII character
     * so convert to EBCDIC so that it makes sense locally;
     *
     * example:
     *
     * client specifies %20 in URL to refer to a space char;
     * at this point we're called with EBCDIC "20"; after turning
     * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
     * represents an ASCII char and convert 0x20 to EBCDIC, yielding
     * 0x40
     */

    char buf[1];

    if (1 == sscanf(x, "%2x", &i)) {
        buf[0] = i & 0xFF;
        ap_xlate_proto_from_ascii(buf, 1);
        return buf[0];
    }
    else {
        return 0;
    }
#endif /*APR_CHARSET_EBCDIC*/
}

PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
{
#if !APR_CHARSET_EBCDIC
    int i;

    x[0] = '%';
    i = (ch & 0xF0) >> 4;
    if (i >= 10) {
        x[1] = ('A' - 10) + i;
    }
    else {
        x[1] = '0' + i;
    }

    i = ch & 0x0F;
    if (i >= 10) {
        x[2] = ('A' - 10) + i;
    }
    else {
        x[2] = '0' + i;
    }
#else /*APR_CHARSET_EBCDIC*/
    static const char ntoa[] = { "0123456789ABCDEF" };
    char buf[1];

    ch &= 0xFF;

    buf[0] = ch;
    ap_xlate_proto_to_ascii(buf, 1);

    x[0] = '%';
    x[1] = ntoa[(buf[0] >> 4) & 0x0F];
    x[2] = ntoa[buf[0] & 0x0F];
    x[3] = '\0';
#endif /*APR_CHARSET_EBCDIC*/
}

/*
 * canonicalise a URL-encoded string
 */


/*
 * Convert a URL-encoded string to canonical form.
 * It decodes characters which need not be encoded,
 * and encodes those which must be encoded, and does not touch
 * those which must not be touched.
 */

PROXY_DECLARE(char *)ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len,
                                          enum enctype t, int flags,
                                          int proxyreq)
{
    int i, j, ch;
    char *y;
    char *allowed;  /* characters which should not be encoded */
    char *reserved; /* characters which much not be en/de-coded */
    int forcedec = flags & PROXY_CANONENC_FORCEDEC;
    int noencslashesenc = flags & PROXY_CANONENC_NOENCODEDSLASHENCODING;

/*
 * N.B. in addition to :@&=, this allows ';' in an http path
 * and '?' in an ftp path -- this may be revised
 *
 * Also, it makes a '+' character in a search string reserved, as
 * it may be form-encoded. (Although RFC 1738 doesn't allow this -
 * it only permits ; / ? : @ = & as reserved chars.)
 */

    if (t == enc_path) {
        allowed = "~$-_.+!*'(),;:@&=";
    }
    else if (t == enc_search) {
        allowed = "$-_.!*'(),;:@&=";
    }
    else if (t == enc_user) {
        allowed = "$-_.+!*'(),;@&=";
    }
    else if (t == enc_fpath) {
        allowed = "$-_.+!*'(),?:@&=";
    }
    else {            /* if (t == enc_parm) */
        allowed = "$-_.+!*'(),?/:@&=";
    }

    if (t == enc_path) {
        reserved = "/";
    }
    else if (t == enc_search) {
        reserved = "+";
    }
    else {
        reserved = "";
    }

    y = apr_palloc(p, 3 * len + 1);

    for (i = 0, j = 0; i < len; i++, j++) {
/* always handle '/' first */
        ch = x[i];
        if (strchr(reserved, ch)) {
            y[j] = ch;
            continue;
        }
/*
 * decode it if not already done. do not decode reverse proxied URLs
 * unless specifically forced
 */

        if ((forcedec || noencslashesenc
            || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
            if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) {
                return NULL;
            }
            ch = ap_proxy_hex2c(&x[i + 1]);
            if (ch != 0 && strchr(reserved, ch)) {  /* keep it encoded */
                y[j++] = x[i++];
                y[j++] = x[i++];
                y[j] = x[i];
                continue;
            }
            if (noencslashesenc && !forcedec && (proxyreq == PROXYREQ_REVERSE)) {
                /*
                 * In the reverse proxy case when we only want to keep encoded
                 * slashes untouched revert back to '%' which will cause
                 * '%' to be encoded in the following.
                 */

                ch = '%';
            }
            else {
                i += 2;
            }
        }
/* recode it, if necessary */
        if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
            ap_proxy_c2hex(ch, &y[j]);
            j += 2;
        }
        else {
            y[j] = ch;
        }
    }
    y[j] = '\0';
    return y;
}

/*
 * Convert a URL-encoded string to canonical form.
 * It decodes characters which need not be encoded,
 * and encodes those which must be encoded, and does not touch
 * those which must not be touched.
 */

PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len,
                                       enum enctype t, int forcedec,
                                       int proxyreq)
{
    int flags;

    flags = forcedec ? PROXY_CANONENC_FORCEDEC : 0;
    return ap_proxy_canonenc_ex(p, x, len, t, flags, proxyreq);
}

/*
 * Parses network-location.
 *    urlp           on input the URL; on output the path, after the leading /
 *    user           NULL if no user/password permitted
 *    password       holder for password
 *    host           holder for host
 *    port           port number; only set if one is supplied.
 *
 * Returns an error string.
 */

PROXY_DECLARE(char *)
     ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
            char **passwordp, char **hostp, apr_port_t *port)
{
    char *addr, *scope_id, *strp, *host, *url = *urlp;
    char *user = NULL, *password = NULL;
    apr_port_t tmp_port;
    apr_status_t rv;

    if (url[0] != '/' || url[1] != '/') {
        return "Malformed URL";
    }
    host = url + 2;
    url = strchr(host, '/');
    if (url == NULL) {
        url = "";
    }
    else {
        *(url++) = '\0';    /* skip separating '/' */
    }

    /* find _last_ '@' since it might occur in user/password part */
    strp = strrchr(host, '@');

    if (strp != NULL) {
        *strp = '\0';
        user = host;
        host = strp + 1;

/* find password */
        strp = strchr(user, ':');
        if (strp != NULL) {
            *strp = '\0';
            password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
            if (password == NULL) {
                return "Bad %-escape in URL (password)";
            }
        }

        user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
        if (user == NULL) {
            return "Bad %-escape in URL (username)";
        }
    }
    if (userp != NULL) {
        *userp = user;
    }
    if (passwordp != NULL) {
        *passwordp = password;
    }

    /*
     * Parse the host string to separate host portion from optional port.
     * Perform range checking on port.
     */

    rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
    if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
        return "Invalid host/port";
    }
    if (tmp_port != 0) { /* only update caller's port if port was specified */
        *port = tmp_port;
    }

    ap_str_tolower(addr); /* DNS names are case-insensitive */

    *urlp = url;
    *hostp = addr;

    return NULL;
}

static int proxyerror_core(request_rec *r, int statuscode, const char *message,
                           apr_status_t rv)
{
    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00898)
                  "%s returned by %s", message, r->uri);

    apr_table_setn(r->notes, "error-notes",
        apr_pstrcat(r->pool,
            "The proxy server could not handle the request

"
            "Reason: ", ap_escape_html(r->pool, message),
            "

"
,
            NULL));

    /* Allow "error-notes" string to be printed by ap_send_error_response() */
    apr_table_setn(r->notes, "verbose-error-to""*");

    r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
    return statuscode;
}

PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
{
    return proxyerror_core(r, statuscode, message, 0);
}

static const char *
     proxy_get_host_of_request(request_rec *r)
{
    char *url, *user = NULL, *password = NULL, *err, *host = NULL;
    apr_port_t port;

    if (r->hostname != NULL) {
        return r->hostname;
    }

    /* Set url to the first char after "scheme://" */
    if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') {
        return NULL;
    }

    url = apr_pstrdup(r->pool, &url[1]);    /* make it point to "//", which is what proxy_canon_netloc expects */

    err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);

    if (err != NULL) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00899) "%s", err);
    }

    r->hostname = host;

    return host;        /* ought to return the port, too */
}

/* Return TRUE if addr represents an IP address (or an IP network address) */
PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
{
    const char *addr = This->name;
    long ip_addr[4];
    int i, quads;
    long bits;

    /*
     * if the address is given with an explicit netmask, use that
     * Due to a deficiency in apr_inet_addr(), it is impossible to parse
     * "partial" addresses (with less than 4 quads) correctly, i.e.
     * 192.168.123 is parsed as 192.168.0.123, which is not what I want.
     * I therefore have to parse the IP address manually:
     * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0)
     * addr and mask were set by proxy_readmask()
     * return 1;
     */


    /*
     * Parse IP addr manually, optionally allowing
     * abbreviated net addresses like 192.168.
     */


    /* Iterate over up to 4 (dotted) quads. */
    for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
        char *tmp;

        if (*addr == '/' && quads > 0) {  /* netmask starts here. */
            break;
        }

        if (!apr_isdigit(*addr)) {
            return 0;       /* no digit at start of quad */
        }

        ip_addr[quads] = strtol(addr, &tmp, 0);

        if (tmp == addr) {  /* expected a digit, found something else */
            return 0;
        }

        if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
            /* invalid octet */
            return 0;
        }

        addr = tmp;

        if (*addr == '.' && quads != 3) {
            ++addr;     /* after the 4th quad, a dot would be illegal */
        }
    }

    for (This->addr.s_addr = 0, i = 0; i < quads; ++i) {
        This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
    }

    if (addr[0] == '/' && apr_isdigit(addr[1])) {   /* net mask follows: */
        char *tmp;

        ++addr;

        bits = strtol(addr, &tmp, 0);

        if (tmp == addr) {   /* expected a digit, found something else */
            return 0;
        }

        addr = tmp;

        if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */
            return 0;
        }

    }
    else {
        /*
         * Determine (i.e., "guess") netmask by counting the
         * number of trailing .0's; reduce #quads appropriately
         * (so that 192.168.0.0 is equivalent to 192.168.)
         */

        while (quads > 0 && ip_addr[quads - 1] == 0) {
            --quads;
        }

        /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
        if (quads < 1) {
            return 0;
        }

        /* every zero-byte counts as 8 zero-bits */
        bits = 8 * quads;

        if (bits != 32) {     /* no warning for fully qualified IP address */
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00900)
                         "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
                         inet_ntoa(This->addr), bits);
        }
    }

    This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));

    if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00901)
                     "Warning: NetMask and IP-Addr disagree in %s/%ld",
                     inet_ntoa(This->addr), bits);
        This->addr.s_addr &= This->mask.s_addr;
        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00902)
                     " Set to %s/%ld", inet_ntoa(This->addr), bits);
    }

    if (*addr == '\0') {
        This->matcher = proxy_match_ipaddr;
        return 1;
    }
    else {
        return (*addr == '\0'); /* okay iff we've parsed the whole string */
    }
}

/* Return TRUE if addr represents an IP address (or an IP network address) */
static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
{
    int i, ip_addr[4];
    struct in_addr addr, *ip;
    const char *host = proxy_get_host_of_request(r);

    if (host == NULL) {   /* oops! */
       return 0;
    }

    memset(&addr, '\0'sizeof addr);
    memset(ip_addr, '\0'sizeof ip_addr);

    if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
        for (addr.s_addr = 0, i = 0; i < 4; ++i) {
            /* ap_proxy_is_ipaddr() already confirmed that we have
             * a valid octet in ip_addr[i]
             */

            addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
        }

        if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
#if DEBUGGING
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00903)
                         "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00904)
                         "%s/", inet_ntoa(This->addr));
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00905)
                         "%s", inet_ntoa(This->mask));
#endif
            return 1;
        }
#if DEBUGGING
        else {
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00906)
                         "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00907)
                         "%s/", inet_ntoa(This->addr));
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00908)
                         "%s", inet_ntoa(This->mask));
        }
#endif
    }
    else {
        struct apr_sockaddr_t *reqaddr;

        if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
            != APR_SUCCESS) {
#if DEBUGGING
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00909)
             "2)IP-NoMatch: hostname=%s msg=Host not found", host);
#endif
            return 0;
        }

        /* Try to deal with multiple IP addr's for a host */
        /* FIXME: This needs to be able to deal with IPv6 */
        while (reqaddr) {
            ip = (struct in_addr *) reqaddr->ipaddr_ptr;
            if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
#if DEBUGGING
                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00910)
                             "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00911)
                             "%s/", inet_ntoa(This->addr));
                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00912)
                             "%s", inet_ntoa(This->mask));
#endif
                return 1;
            }
#if DEBUGGING
            else {
                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00913)
                             "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00914)
                             "%s/", inet_ntoa(This->addr));
                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00915)
                             "%s", inet_ntoa(This->mask));
            }
#endif
            reqaddr = reqaddr->next;
        }
    }

    return 0;
}

/* Return TRUE if addr represents a domain name */
PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
{
    char *addr = This->name;
    int i;

    /* Domain name must start with a '.' */
    if (addr[0] != '.') {
        return 0;
    }

    /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
    for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) {
        continue;
    }

#if 0
    if (addr[i] == ':') {
    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03234)
                     "@@@@ handle optional port in proxy_is_domainname()");
    /* @@@@ handle optional port */
    }
#endif

    if (addr[i] != '\0') {
        return 0;
    }

    /* Strip trailing dots */
    for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) {
        addr[i] = '\0';
    }

    This->matcher = proxy_match_domainname;
    return 1;
}

/* Return TRUE if host "host" is in domain "domain" */
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
{
    const char *host = proxy_get_host_of_request(r);
    int d_len = strlen(This->name), h_len;

    if (host == NULL) {      /* some error was logged already */
        return 0;
    }

    h_len = strlen(host);

    /* @@@ do this within the setup? */
    /* Ignore trailing dots in domain comparison: */
    while (d_len > 0 && This->name[d_len - 1] == '.') {
        --d_len;
    }
    while (h_len > 0 && host[h_len - 1] == '.') {
        --h_len;
    }
    return h_len > d_len
        && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
}

/* Return TRUE if host represents a host name */
PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
{
    struct apr_sockaddr_t *addr;
    char *host = This->name;
    int i;

    /* Host names must not start with a '.' */
    if (host[0] == '.') {
        return 0;
    }
    /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
    for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);

    if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) {
        return 0;
    }

    This->hostaddr = addr;

    /* Strip trailing dots */
    for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) {
        host[i] = '\0';
    }

    This->matcher = proxy_match_hostname;
    return 1;
}

/* Return TRUE if host "host" is equal to host2 "host2" */
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
{
    char *host = This->name;
    const char *host2 = proxy_get_host_of_request(r);
    int h2_len;
    int h1_len;

    if (host == NULL || host2 == NULL) {
        return 0; /* oops! */
    }

    h2_len = strlen(host2);
    h1_len = strlen(host);

#if 0
    struct apr_sockaddr_t *addr = *This->hostaddr;

    /* Try to deal with multiple IP addr's for a host */
    while (addr) {
        if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
            return 1;
        addr = addr->next;
    }
#endif

    /* Ignore trailing dots in host2 comparison: */
    while (h2_len > 0 && host2[h2_len - 1] == '.') {
        --h2_len;
    }
    while (h1_len > 0 && host[h1_len - 1] == '.') {
        --h1_len;
    }
    return h1_len == h2_len
        && strncasecmp(host, host2, h1_len) == 0;
}

/* Return TRUE if addr is to be matched as a word */
PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
{
    This->matcher = proxy_match_word;
    return 1;
}

/* Return TRUE if string "str2" occurs literally in "str1" */
static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
{
    const char *host = proxy_get_host_of_request(r);
    return host != NULL && ap_strstr_c(host, This->name) != NULL;
}

/* Backwards-compatible interface. */
PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
                             apr_sockaddr_t *uri_addr)
{
    return ap_proxy_checkproxyblock2(r, conf, uri_addr->hostname, uri_addr);
}

#define MAX_IP_STR_LEN (46)

PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf,
                                             const char *hostname, apr_sockaddr_t *addr)
{
    int j;

    /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
    for (j = 0; j < conf->noproxies->nelts; j++) {
        struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
        struct apr_sockaddr_t *conf_addr;

        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                      "checking remote machine [%s] against [%s]",
                      hostname, npent[j].name);
        if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') {
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00916)
                          "connect to remote machine %s blocked: name %s "
                          "matched", hostname, npent[j].name);
            return HTTP_FORBIDDEN;
        }

        /* No IP address checks if no IP address was passed in,
         * i.e. the forward address proxy case, where this server does
         * not resolve the hostname.  */

        if (!addr)
            continue;

        for (conf_addr = npent[j].addr; conf_addr; conf_addr = conf_addr->next) {
            char caddr[MAX_IP_STR_LEN], uaddr[MAX_IP_STR_LEN];
            apr_sockaddr_t *uri_addr;

            if (apr_sockaddr_ip_getbuf(caddr, sizeof caddr, conf_addr))
                continue;

            for (uri_addr = addr; uri_addr; uri_addr = uri_addr->next) {
                if (apr_sockaddr_ip_getbuf(uaddr, sizeof uaddr, uri_addr))
                    continue;
                ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                              "ProxyBlock comparing %s and %s", caddr, uaddr);
                if (!strcmp(caddr, uaddr)) {
                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00917)
                                  "connect to remote machine %s blocked: "
                                  "IP %s matched", hostname, caddr);
                    return HTTP_FORBIDDEN;
                }
            }
        }
    }

    return OK;
}

/* set up the minimal filter set */
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
{
    ap_add_input_filter("HTTP_IN", NULL, r, c);
    return OK;
}

PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
                              proxy_dir_conf *conf, const char *url)
{
    proxy_req_conf *rconf;
    struct proxy_alias *ent;
    int i, l1, l1_orig, l2;
    char *u;

    /*
     * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
     * after the hostname
     * XXX FIXME: Ensure the /uri component is a case sensitive match
     */

    if (r->proxyreq != PROXYREQ_REVERSE) {
        return url;
    }

    l1_orig = strlen(url);
    if (conf->interpolate_env == 1) {
        rconf = ap_get_module_config(r->request_config, &proxy_module);
        ent = (struct proxy_alias *)rconf->raliases->elts;
    }
    else {
        ent = (struct proxy_alias *)conf->raliases->elts;
    }
    for (i = 0; i < conf->raliases->nelts; i++) {
        proxy_server_conf *sconf = (proxy_server_conf *)
            ap_get_module_config(r->server->module_config, &proxy_module);
        proxy_balancer *balancer;
        const char *real = ent[i].real;

        /* Restore the url length, if it had been changed by the code below */
        l1 = l1_orig;

        /*
         * First check if mapping against a balancer and see
         * if we have such a entity. If so, then we need to
         * find the particulars of the actual worker which may
         * or may not be the right one... basically, we need
         * to find which member actually handled this request.
         */

        if (ap_proxy_valid_balancer_name((char *)real, 0) &&
            (balancer = ap_proxy_get_balancer(r->pool, sconf, real, 1))) {
            int n, l3 = 0;
            proxy_worker **worker = (proxy_worker **)balancer->workers->elts;
            const char *urlpart = ap_strchr_c(real + sizeof(BALANCER_PREFIX) - 1, '/');
            if (urlpart) {
                if (!urlpart[1])
                    urlpart = NULL;
                else
                    l3 = strlen(urlpart);
            }
            /* The balancer comparison is a bit trickier.  Given the context
             *   BalancerMember balancer://alias http://example.com/foo
             *   ProxyPassReverse /bash balancer://alias/bar
             * translate url http://example.com/foo/bar/that to /bash/that
             */

            for (n = 0; n < balancer->workers->nelts; n++) {
                l2 = strlen((*worker)->s->name_ex);
                if (urlpart) {
                    /* urlpart (l3) assuredly starts with its own '/' */
                    if ((*worker)->s->name_ex[l2 - 1] == '/')
                        --l2;
                    if (l1 >= l2 + l3
                            && strncasecmp((*worker)->s->name_ex, url, l2) == 0
                            && strncmp(urlpart, url + l2, l3) == 0) {
                        u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3],
                                        NULL);
                        return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
                    }
                }
                else if (l1 >= l2 && strncasecmp((*worker)->s->name_ex, url, l2) == 0) {
                    /* edge case where fake is just "/"... avoid double slash */
                    if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) {
                        u = apr_pstrdup(r->pool, &url[l2]);
                    } else {
                        u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
                    }
                    return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
                }
                worker++;
            }
        }
        else {
            const char *part = url;
            l2 = strlen(real);
            if (real[0] == '/') {
                part = ap_strstr_c(url, "://");
                if (part) {
                    part = ap_strchr_c(part+3, '/');
                    if (part) {
                        l1 = strlen(part);
                    }
                    else {
                        part = url;
                    }
                }
                else {
                    part = url;
                }
            }
            if (l2 > 0 && l1 >= l2 && strncasecmp(real, part, l2) == 0) {
                u = apr_pstrcat(r->pool, ent[i].fake, &part[l2], NULL);
                return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r);
            }
        }
    }

    return url;
}

/*
 * Cookies are a bit trickier to match: we've got two substrings to worry
 * about, and we can't just find them with strstr 'cos of case.  Regexp
 * matching would be an easy fix, but for better consistency with all the
 * other matches we'll refrain and use apr_strmatch to find path=/domain=
 * and stick to plain strings for the config values.
 */

PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
                              proxy_dir_conf *conf, const char *str)
{
    proxy_req_conf *rconf = ap_get_module_config(r->request_config,
                                                 &proxy_module);
    struct proxy_alias *ent;
    apr_size_t len = strlen(str);
    const char *newpath = NULL;
    const char *newdomain = NULL;
    const char *pathp;
    const char *domainp;
    const char *pathe = NULL;
    const char *domaine = NULL;
    apr_size_t l1, l2, poffs = 0, doffs = 0;
    int i;
    int ddiff = 0;
    int pdiff = 0;
    char *tmpstr, *tmpstr_orig, *token, *last, *ret;

    if (r->proxyreq != PROXYREQ_REVERSE) {
        return str;
    }

   /*
    * Find the match and replacement, but save replacing until we've done
    * both path and domain so we know the new strlen
    */

    tmpstr_orig = tmpstr = apr_pstrdup(r->pool, str);
    while ((token = apr_strtok(tmpstr, ";", &last))) {
        /* skip leading spaces */
        while (apr_isspace(*token)) {
            ++token;
        }

        if (ap_cstr_casecmpn("path=", token, 5) == 0) {
            pathp = token + 5;
            poffs = pathp - tmpstr_orig;
            l1 = strlen(pathp);
            pathe = str + poffs + l1;
            if (conf->interpolate_env == 1) {
                ent = (struct proxy_alias *)rconf->cookie_paths->elts;
            }
            else {
                ent = (struct proxy_alias *)conf->cookie_paths->elts;
            }
            for (i = 0; i < conf->cookie_paths->nelts; i++) {
                l2 = strlen(ent[i].fake);
                if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
                    newpath = ent[i].real;
                    pdiff = strlen(newpath) - l1;
                    break;
                }
            }
        }
        else if (ap_cstr_casecmpn("domain=", token, 7) == 0) {
            domainp = token + 7;
            doffs = domainp - tmpstr_orig;
            l1 = strlen(domainp);
            domaine = str + doffs + l1;
            if (conf->interpolate_env == 1) {
                ent = (struct proxy_alias *)rconf->cookie_domains->elts;
            }
            else {
                ent = (struct proxy_alias *)conf->cookie_domains->elts;
            }
            for (i = 0; i < conf->cookie_domains->nelts; i++) {
                l2 = strlen(ent[i].fake);
                if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
                    newdomain = ent[i].real;
                    ddiff = strlen(newdomain) - l1;
                    break;
                }
            }
        }

        /* Iterate the remaining tokens using apr_strtok(NULL, ...) */
        tmpstr = NULL;
    }

    if (newpath) {
        ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
        l1 = strlen(newpath);
        if (newdomain) {
            l2 = strlen(newdomain);
            if (doffs > poffs) {
                memcpy(ret, str, poffs);
                memcpy(ret + poffs, newpath, l1);
                memcpy(ret + poffs + l1, pathe, str + doffs - pathe);
                memcpy(ret + doffs + pdiff, newdomain, l2);
                strcpy(ret + doffs + pdiff + l2, domaine);
            }
            else {
                memcpy(ret, str, doffs) ;
                memcpy(ret + doffs, newdomain, l2);
                memcpy(ret + doffs + l2, domaine, str + poffs - domaine);
                memcpy(ret + poffs + ddiff, newpath, l1);
                strcpy(ret + poffs + ddiff + l1, pathe);
            }
        }
        else {
            memcpy(ret, str, poffs);
            memcpy(ret + poffs, newpath, l1);
            strcpy(ret + poffs + l1, pathe);
        }
    }
    else if (newdomain) {
            ret = apr_palloc(r->pool, len + ddiff + 1);
            l2 = strlen(newdomain);
            memcpy(ret, str, doffs);
            memcpy(ret + doffs, newdomain, l2);
            strcpy(ret + doffs + l2, domaine);
    }
    else {
        ret = (char *)str; /* no change */
    }

    return ret;
}

/*
 * BALANCER related...
 */


/*
 * verifies that the balancer name conforms to standards.
 */

PROXY_DECLARE(int) ap_proxy_valid_balancer_name(char *name, int i)
{
    if (!i)
        i = sizeof(BALANCER_PREFIX)-1;
    return (!ap_cstr_casecmpn(name, BALANCER_PREFIX, i));
}


PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
                                                      proxy_server_conf *conf,
                                                      const char *url,
                                                      int care)
{
    proxy_balancer *balancer;
    char *c, *uri = apr_pstrdup(p, url);
    int i;
    proxy_hashes hash;

    c = strchr(uri, ':');
    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
        return NULL;
    }
    /* remove path from uri */
    if ((c = strchr(c + 3, '/'))) {
        *c = '\0';
    }
    ap_str_tolower(uri);
    hash.def = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_DEFAULT);
    hash.fnv = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_FNV);
    balancer = (proxy_balancer *)conf->balancers->elts;
    for (i = 0; i < conf->balancers->nelts; i++) {
        if (balancer->hash.def == hash.def && balancer->hash.fnv == hash.fnv) {
            if (!care || !balancer->s->inactive) {
                return balancer;
            }
        }
        balancer++;
    }
    return NULL;
}


PROXY_DECLARE(char *) ap_proxy_update_balancer(apr_pool_t *p,
                                                proxy_balancer *balancer,
                                                const char *url)
{
    apr_uri_t puri;
    if (!url) {
        return NULL;
    }
    if (apr_uri_parse(p, url, &puri) != APR_SUCCESS) {
        return apr_psprintf(p, "unable to parse: %s", url);
    }
    if (puri.path && PROXY_STRNCPY(balancer->s->vpath, puri.path) != APR_SUCCESS) {
        return apr_psprintf(p, "balancer %s front-end virtual-path (%s) too long",
                            balancer->s->name, puri.path);
    }
    if (puri.hostname && PROXY_STRNCPY(balancer->s->vhost, puri.hostname) != APR_SUCCESS) {
        return apr_psprintf(p, "balancer %s front-end vhost name (%s) too long",
                            balancer->s->name, puri.hostname);
    }
    return NULL;
}

#define PROXY_UNSET_NONCE '\n'

PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p,
                                               proxy_balancer **balancer,
                                               proxy_server_conf *conf,
                                               const char *url,
                                               const char *alias,
                                               int do_malloc)
{
    proxy_balancer_method *lbmethod;
    proxy_balancer_shared *bshared;
    char *c, *q, *uri = apr_pstrdup(p, url);
    const char *sname;

    /* We should never get here without a valid BALANCER_PREFIX... */

    c = strchr(uri, ':');
    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
        return apr_psprintf(p, "Bad syntax for a balancer name (%s)", uri);
    /* remove path from uri */
    if ((q = strchr(c + 3, '/')))
        *q = '\0';

    ap_str_tolower(uri);
    *balancer = apr_array_push(conf->balancers);
    memset(*balancer, 0, sizeof(proxy_balancer));

    /*
     * NOTE: The default method is byrequests - if it doesn't
     * exist, that's OK at this time. We check when we share and sync
     */

    lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests""0");
    (*balancer)->lbmethod = lbmethod;
    
    (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *));
#if APR_HAS_THREADS
    (*balancer)->gmutex = NULL;
    (*balancer)->tmutex = NULL;
#endif

    if (do_malloc)
        bshared = ap_malloc(sizeof(proxy_balancer_shared));
    else
        bshared = apr_palloc(p, sizeof(proxy_balancer_shared));

    memset(bshared, 0, sizeof(proxy_balancer_shared));

    bshared->was_malloced = (do_malloc != 0);
    PROXY_STRNCPY(bshared->lbpname, "byrequests");
    if (PROXY_STRNCPY(bshared->name, uri) != APR_SUCCESS) {
        if (do_malloc) free(bshared);
        return apr_psprintf(p, "balancer name (%s) too long", uri);
    }
    (*balancer)->lbmethod_set = 1;

    /*
     * We do the below for verification. The real sname will be
     * done post_config
     */

    ap_pstr2_alnum(p, bshared->name + sizeof(BALANCER_PREFIX) - 1,
                   &sname);
    sname = apr_pstrcat(p, conf->id, "_", sname, NULL);
    if (PROXY_STRNCPY(bshared->sname, sname) != APR_SUCCESS) {
        if (do_malloc) free(bshared);
        return apr_psprintf(p, "balancer safe-name (%s) too long", sname);
    }
    bshared->hash.def = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_DEFAULT);
    bshared->hash.fnv = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_FNV);
    (*balancer)->hash = bshared->hash;

    bshared->forcerecovery = 1;
    bshared->sticky_separator = '.';
    *bshared->nonce = PROXY_UNSET_NONCE;  /* impossible valid input */

    (*balancer)->s = bshared;
    (*balancer)->sconf = conf;

    return ap_proxy_update_balancer(p, *balancer, alias);
}

/*
 * Create an already defined balancer and free up memory.
 */

PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer,
                                                    proxy_balancer_shared *shm,
                                                    int i)
{
    apr_status_t rv = APR_SUCCESS;
    proxy_balancer_method *lbmethod;
    char *action = "copying";
    if (!shm || !balancer->s)
        return APR_EINVAL;

    if ((balancer->s->hash.def != shm->hash.def) ||
        (balancer->s->hash.fnv != shm->hash.fnv)) {
        memcpy(shm, balancer->s, sizeof(proxy_balancer_shared));
        if (balancer->s->was_malloced)
            free(balancer->s);
    } else {
        action = "re-using";
    }
    balancer->s = shm;
    balancer->s->index = i;
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02337)
                 "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm,
                 balancer->s->name);
    /* the below should always succeed */
    lbmethod = ap_lookup_provider(PROXY_LBMETHOD, balancer->s->lbpname, "0");
    if (lbmethod) {
        balancer->lbmethod = lbmethod;
        balancer->lbmethod_set = 1;
    } else {
        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(02432)
                     "Cannot find LB Method: %s", balancer->s->lbpname);
        return APR_EINVAL;
    }
    if (*balancer->s->nonce == PROXY_UNSET_NONCE) {
        char nonce[APR_UUID_FORMATTED_LENGTH + 1];
        apr_uuid_t uuid;

        /* Generate a pseudo-UUID from the PRNG to use as a nonce for
         * the lifetime of the process. uuid.data is a char array so
         * this is an adequate substitute for apr_uuid_get(). */

        ap_random_insecure_bytes(uuid.data, sizeof uuid.data);
        apr_uuid_format(nonce, &uuid);
        rv = PROXY_STRNCPY(balancer->s->nonce, nonce);
    }
    return rv;
}

PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p)
{
#if APR_HAS_THREADS
    apr_status_t rv = APR_SUCCESS;
#endif
    ap_slotmem_provider_t *storage = balancer->storage;
    apr_size_t size;
    unsigned int num;

    if (!storage) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00918)
                     "no provider for %s", balancer->s->name);
        return APR_EGENERAL;
    }
    /*
     * for each balancer we need to init the global
     * mutex and then attach to the shared worker shm
     */

    if (!balancer->gmutex) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00919)
                     "no mutex %s", balancer->s->name);
        return APR_EGENERAL;
    }

    /* Re-open the mutex for the child. */
    rv = apr_global_mutex_child_init(&(balancer->gmutex),
                                     apr_global_mutex_lockfile(balancer->gmutex),
                                     p);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00920)
                     "Failed to reopen mutex %s in child",
                     balancer->s->name);
        return rv;
    }

    /* now attach */
    storage->attach(&(balancer->wslot), balancer->s->sname, &size, &num, p);
    if (!balancer->wslot) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00921) "slotmem_attach failed");
        return APR_EGENERAL;
    }

#if APR_HAS_THREADS
    if (balancer->tmutex == NULL) {
        rv = apr_thread_mutex_create(&(balancer->tmutex), APR_THREAD_MUTEX_DEFAULT, p);
        if (rv != APR_SUCCESS) {
            ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00922)
                         "can not create balancer thread mutex");
            return rv;
        }
    }
#endif
    return APR_SUCCESS;
}

static proxy_worker *proxy_balancer_get_best_worker(proxy_balancer *balancer,
                                                    request_rec *r,
                                                    proxy_is_best_callback_fn_t *is_best,
                                                    void *baton)
{
    int i = 0;
    int cur_lbset = 0;
    int max_lbset = 0;
    int unusable_workers = 0;
    apr_pool_t *tpool = NULL;
    apr_array_header_t *spares = NULL;
    apr_array_header_t *standbys = NULL;
    proxy_worker *worker = NULL;
    proxy_worker *best_worker = NULL;

    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(10122)
                 "proxy: Entering %s for BALANCER (%s)",
                 balancer->lbmethod->name, balancer->s->name);

    apr_pool_create(&tpool, r->pool);
    apr_pool_tag(tpool, "proxy_lb_best");

    spares = apr_array_make(tpool, 1, sizeof(proxy_worker*));
    standbys = apr_array_make(tpool, 1, sizeof(proxy_worker*));

    /* Process lbsets in order, only replacing unusable workers in a given lbset
     * with available spares from the same lbset. Hot standbys will be used as a
     * last resort when all other workers and spares are unavailable.
     */

    for (cur_lbset = 0; !best_worker && (cur_lbset <= max_lbset); cur_lbset++) {
        unusable_workers = 0;
        apr_array_clear(spares);
        apr_array_clear(standbys);

        for (i = 0; i < balancer->workers->nelts; i++) {
            worker = APR_ARRAY_IDX(balancer->workers, i, proxy_worker *);

            if (worker->s->lbset > max_lbset) {
                max_lbset = worker->s->lbset;
            }

            if (worker->s->lbset != cur_lbset) {
                continue;
            }

            /* A draining worker that is neither a spare nor a standby should be
             * considered unusable to be replaced by spares.
             */

            if (PROXY_WORKER_IS_DRAINING(worker)) {
                if (!PROXY_WORKER_IS_SPARE(worker) && !PROXY_WORKER_IS_STANDBY(worker)) {
                    unusable_workers++;
                }

                continue;
            }

            /* If the worker is in error state run retry on that worker. It will
             * be marked as operational if the retry timeout is elapsed.  The
             * worker might still be unusable, but we try anyway.
             */

            if (!PROXY_WORKER_IS_USABLE(worker)) {
                ap_proxy_retry_worker("BALANCER", worker, r->server);
            }

            if (PROXY_WORKER_IS_SPARE(worker)) {
                if (PROXY_WORKER_IS_USABLE(worker)) {
                    APR_ARRAY_PUSH(spares, proxy_worker *) = worker;
                }
            }
            else if (PROXY_WORKER_IS_STANDBY(worker)) {
                if (PROXY_WORKER_IS_USABLE(worker)) {
                    APR_ARRAY_PUSH(standbys, proxy_worker *) = worker;
                }
            }
            else if (PROXY_WORKER_IS_USABLE(worker)) {
              if (is_best(worker, best_worker, baton)) {
                best_worker = worker;
              }
            }
            else {
                unusable_workers++;
            }
        }

        /* Check if any spares are best. */
        for (i = 0; (i < spares->nelts) && (i < unusable_workers); i++) {
          worker = APR_ARRAY_IDX(spares, i, proxy_worker *);

          if (is_best(worker, best_worker, baton)) {
            best_worker = worker;
          }
        }

        /* If no workers are available, use the standbys. */
        if (!best_worker) {
            for (i = 0; i < standbys->nelts; i++) {
              worker = APR_ARRAY_IDX(standbys, i, proxy_worker *);

              if (is_best(worker, best_worker, baton)) {
                best_worker = worker;
              }
            }
        }
    }

    apr_pool_destroy(tpool);

    if (best_worker) {
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(10123)
                     "proxy: %s selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d",
                     balancer->lbmethod->name, best_worker->s->name_ex,
                     best_worker->s->busy, best_worker->s->lbstatus);
    }

    return best_worker;
}

PROXY_DECLARE(proxy_worker *) ap_proxy_balancer_get_best_worker(proxy_balancer *balancer,
                                                                request_rec *r,
                                                                proxy_is_best_callback_fn_t *is_best,
                                                                void *baton)
{
    return proxy_balancer_get_best_worker(balancer, r, is_best, baton);
}

/*
 * CONNECTION related...
 */


static void socket_cleanup(proxy_conn_rec *conn)
{
    conn->sock = NULL;
    conn->tmp_bb = NULL;
    conn->connection = NULL;
    conn->ssl_hostname = NULL;
    apr_pool_clear(conn->scpool);
    conn->close = 0;
}

static void conn_cleanup(proxy_conn_rec *conn)
{
    socket_cleanup(conn);
    conn->address = NULL;
    conn->addr = NULL;
    conn->hostname = NULL;
    conn->port = 0;
    conn->uds_path = NULL;
    if (conn->uds_pool) {
        apr_pool_clear(conn->uds_pool);
    }
}

static apr_status_t conn_pool_cleanup(void *theworker)
{
    /* Signal that the child is exiting */
    ((proxy_worker *)theworker)->cp = NULL;
    return APR_SUCCESS;
}

static apr_pool_t *make_conn_subpool(apr_pool_t *p, const char *tag,
                                     server_rec *s)
{
    apr_pool_t *sp = NULL;
    apr_allocator_t *alloc;
    apr_thread_mutex_t *mutex;
    apr_status_t rv;

    rv = apr_allocator_create(&alloc);
    if (rv == APR_SUCCESS) {
        rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, p);
        if (rv == APR_SUCCESS) {
            apr_allocator_mutex_set(alloc, mutex);
            apr_allocator_max_free_set(alloc, ap_max_mem_free);
            rv = apr_pool_create_ex(&sp, p, NULL, alloc);
        }
        else {
            apr_allocator_destroy(alloc);
        }
    }
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10474)
                     "failed to create %s pool", tag);
        ap_abort_on_oom();
        return NULL; /* not reached */
    }
    apr_allocator_owner_set(alloc, sp);
    apr_pool_tag(sp, tag);

    return sp;
}

static void init_conn_pool(apr_pool_t *p, proxy_worker *worker, server_rec *s)
{
    proxy_conn_pool *cp;

    /*
     * Alloc from the same pool as worker.
     * proxy_conn_pool is permanently attached to the worker.
     */

    cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
    worker->cp = cp;

    /*
     * We need a first pool (cp->pool) to maintain the connections attached to
     * the worker and a second one (cp->dns_pool) to maintain the DNS addresses
     * in use (TTL'ed, refcounted). New connections are created as/on a subpool
     * of cp->pool and new addresses as/on a subpool of cp->dns_pool, such that
     * both leaks (the subpools can be destroyed when the connections and/or
     * addresses are over) and race conditions (the creation/destruction of
     * subpools is protected by the parent pool's mutex) can be avoided.
     *
     * cp->dns_pool is created before cp->pool because when a connection on the
     * latter is destroyed it might destroy an address on the former, so when
     * the base pools are destroyed (e.g. child exit) we thusly make sure that
     * cp->dns_pool and its subpools are still alive when cp->pool gets killed.
     *
     * Both cp->dns_pool and cp->pool have their own allocator/mutex too since
     * acquiring connections and addresses don't need to contend.
     */

    cp->dns_pool = make_conn_subpool(p, "proxy_worker_dns", s);
    cp->pool = make_conn_subpool(p, "proxy_worker_cp", s);

    /* When p is cleaning up the child is exiting, signal that to e.g. avoid
     * destroying the subpools explicitely in connection_destructor() when
     * they have been destroyed already by the reslist cleanup.
     */

    apr_pool_pre_cleanup_register(p, worker, conn_pool_cleanup);
}

PROXY_DECLARE(int) ap_proxy_connection_reusable(proxy_conn_rec *conn)
{
    proxy_worker *worker = conn->worker;

    return !(conn->close || worker->s->disablereuse);
}

static proxy_conn_rec *connection_make(apr_pool_t *p, proxy_worker *worker)
{
    proxy_conn_rec *conn;

    conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
    conn->pool = p;
    conn->worker = worker;

    /*
     * Create another subpool that manages the data for the
     * socket and the connection member of the proxy_conn_rec struct as we
     * destroy this data more frequently than other data in the proxy_conn_rec
     * struct like hostname and addr (at least in the case where we have
     * keepalive connections that timed out).
     *
     * XXX: this is really needed only when worker->s->is_address_reusable,
     *      otherwise conn->scpool = conn->pool would be fine. For now we
     *      can't change it since it's (kind of) part of the API.
     */

    apr_pool_create(&conn->scpool, p);
    apr_pool_tag(conn->scpool, "proxy_conn_scpool");

    return conn;
}

static void connection_cleanup(void *theconn)
{
    proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
    proxy_worker *worker = conn->worker;

    /* Sanity check: Did we already return the pooled connection? */
    if (conn->inreslist) {
        ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923)
                      "Pooled connection 0x%pp for worker %s has been"
                      " already returned to the connection pool.", conn,
                      ap_proxy_worker_name(conn->pool, worker));
        return;
    }

    if (conn->r) {
        apr_pool_destroy(conn->r->pool);
        conn->r = NULL;
    }

    /* determine if the connection should be cleared, closed or reused */
    if (!worker->s->is_address_reusable) {
        apr_pool_t *p = conn->pool;
        apr_pool_clear(p);
        conn = connection_make(p, worker);
    }
    else if (!conn->sock
             || (conn->connection
                 && conn->connection->keepalive == AP_CONN_CLOSE)
             || !ap_proxy_connection_reusable(conn)) {
        socket_cleanup(conn);
    }
    else if (conn->is_ssl) {
        /* The current ssl section/dir config of the conn is not necessarily
         * the one it will be reused for, so while the conn is in the reslist
         * reset its ssl config to the worker's, until a new user sets its own
         * ssl config eventually in proxy_connection_create() and so on.
         */

        ap_proxy_ssl_engine(conn->connection, worker->section_config, 1);
    }

    if (worker->s->hmax && worker->cp->res) {
        conn->inreslist = 1;
        apr_reslist_release(worker->cp->res, (void *)conn);
    }
    else {
        worker->cp->conn = conn;
    }
}

/* DEPRECATED */
PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
                                                            request_rec *r)
{
    apr_status_t rv;

    /*
     * If we have an existing SSL connection it might be possible that the
     * server sent some SSL message we have not read so far (e.g. an SSL
     * shutdown message if the server closed the keepalive connection while
     * the connection was held unused in our pool).
     * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
     * processed. We don't expect any data to be in the returned brigade.
     */

    if (conn->sock && conn->connection) {
        rv = ap_get_brigade(conn->connection->input_filters, conn->tmp_bb,
                            AP_MODE_READBYTES, APR_NONBLOCK_READ,
                            HUGE_STRING_LEN);
        if (!APR_BRIGADE_EMPTY(conn->tmp_bb)) {
            apr_off_t len;

            rv = apr_brigade_length(conn->tmp_bb, 0, &len);
            ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r,
                          "SSL cleanup brigade contained %"
                          APR_OFF_T_FMT " bytes of data.", len);
            apr_brigade_cleanup(conn->tmp_bb);
        }
        if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
            socket_cleanup(conn);
        }
    }
    return APR_SUCCESS;
}

/* reslist constructor */
static apr_status_t connection_constructor(void **resource, void *params,
                                           apr_pool_t *pool)
{
    apr_pool_t *p;
    proxy_conn_rec *conn;
    proxy_worker *worker = (proxy_worker *)params;

    /*
     * Create a subpool for each connection
     * This keeps the memory consumption constant
     * when it's recycled or destroyed.
     */

    apr_pool_create(&p, pool);
    apr_pool_tag(p, "proxy_conn_pool");
    conn = connection_make(p, worker);
    conn->inreslist = 1;

    *resource = conn;
    return APR_SUCCESS;
}

/* reslist destructor */
static apr_status_t connection_destructor(void *resource, void *params,
                                          apr_pool_t *pool)
{
    proxy_worker *worker = params;

    /* Destroy the pool only if not called from reslist_destroy */
    if (worker->cp) {
        proxy_conn_rec *conn = resource;
        apr_pool_destroy(conn->pool);
    }

    return APR_SUCCESS;
}

/*
 * WORKER related...
 */


PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p,
                                           proxy_worker *worker)
{
    if (!(*worker->s->uds_path) || !p) {
        /* just in case */
        return worker->s->name_ex;
    }
    return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name_ex, NULL);
}

PROXY_DECLARE(int) ap_proxy_worker_can_upgrade(apr_pool_t *p,
                                               const proxy_worker *worker,
                                               const char *upgrade,
                                               const char *dflt)
{
    /* Find in worker->s->upgrade list (if any) */
    const char *worker_upgrade = worker->s->upgrade;
    if (*worker_upgrade) {
        return (strcmp(worker_upgrade, "*") == 0
                || ap_cstr_casecmp(worker_upgrade, upgrade) == 0
                || ap_find_token(p, worker_upgrade, upgrade));
    }

    /* Compare to the provided default (if any) */
    return (dflt && ap_cstr_casecmp(dflt, upgrade) == 0);
}

/*
 * Taken from ap_strcmp_match() :
 * Match = 0, NoMatch = 1, Abort = -1, Inval = -2
 * Based loosely on sections of wildmat.c by Rich Salz
 * Hmmm... shouldn't this really go component by component?
 *
 * Adds handling of the "\<any>" => "<any>" unescaping.
 */

static int ap_proxy_strcmp_ematch(const char *str, const char *expected)
{
    apr_size_t x, y;

    for (x = 0, y = 0; expected[y]; ++y, ++x) {
        if (expected[y] == '$' && apr_isdigit(expected[y + 1])) {
            do {
                y += 2;
            } while (expected[y] == '$' && apr_isdigit(expected[y + 1]));
            if (!expected[y])
                return 0;
            while (str[x]) {
                int ret;
                if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1)
                    return ret;
            }
            return -1;
        }
        else if (!str[x]) {
            return -1;
        }
        else if (expected[y] == '\\' && !expected[++y]) {
            /* NUL is an invalid char! */
            return -2;
        }
        if (str[x] != expected[y])
            return 1;
    }
    /* We got all the way through the worker path without a difference */
    return 0;
}

static int worker_matches(proxy_worker *worker,
                          const char *url, apr_size_t url_len,
                          apr_size_t min_match, apr_size_t *max_match,
                          unsigned int mask)
{
    apr_size_t name_len = strlen(worker->s->name_ex);
    if (name_len <= url_len
        && name_len > *max_match
        /* min_match is the length of the scheme://host part only of url,
         * so it's used as a fast path to avoid the match when url is too
         * small, but it's irrelevant when the worker host contains globs
         * (i.e. ->is_host_matchable).
         */

        && (worker->s->is_name_matchable
            ? ((mask & AP_PROXY_WORKER_IS_MATCH)
               && (worker->s->is_host_matchable || name_len >= min_match)
               && !ap_proxy_strcmp_ematch(url, worker->s->name_ex))
            : ((mask & AP_PROXY_WORKER_IS_PREFIX)
               && (name_len >= min_match)
               && !strncmp(url, worker->s->name_ex, name_len)))) {
        *max_match = name_len;
        return 1;
    }
    return 0;
}

PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker_ex(apr_pool_t *p,
                                                     proxy_balancer *balancer,
                                                     proxy_server_conf *conf,
                                                     const char *url,
                                                     unsigned int mask)
{
    proxy_worker *max_worker = NULL;
    apr_size_t min_match, max_match = 0;
    apr_size_t url_len;
    const char *c;
    char *url_copy;
    int i;

    if (!url) {
        return NULL;
    }

    if (!(mask & AP_PROXY_WORKER_NO_UDS)) {
        url = ap_proxy_de_socketfy(p, url);
        if (!url) {
            return NULL;
        }
    }

    c = ap_strchr_c(url, ':');
    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
        return NULL;
    }

    url_len = strlen(url);
    url_copy = apr_pstrmemdup(p, url, url_len);

    /* Default to lookup for both _PREFIX and _MATCH workers */
    if (!(mask & (AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH))) {
        mask |= AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH;
    }

    /*
     * We need to find the start of the path and
     * therefore we know the length of the scheme://hostname/
     * part to we can force-lowercase everything up to
     * the start of the path.
     */

    c = ap_strchr_c(c+3, '/');
    if (c) {
        char *pathstart;
        pathstart = url_copy + (c - url);
        *pathstart = '\0';
        ap_str_tolower(url_copy);
        min_match = strlen(url_copy);
        *pathstart = '/';
    }
    else {
        ap_str_tolower(url_copy);
        min_match = strlen(url_copy);
    }

    /*
     * Do a "longest match" on the worker name to find the worker that
     * fits best to the URL, but keep in mind that we must have at least
     * a minimum matching of length min_match such that
     * scheme://hostname[:port] matches between worker and url.
     */

    if (balancer) {
        proxy_worker **worker = (proxy_worker **)balancer->workers->elts;
        for (i = 0; i < balancer->workers->nelts; i++, worker++) {
            if (worker_matches(*worker, url_copy, url_len,
                               min_match, &max_match, mask)) {
                max_worker = *worker;
            }
        }
    }
    else {
        proxy_worker *worker = (proxy_worker *)conf->workers->elts;
        for (i = 0; i < conf->workers->nelts; i++, worker++) {
            if (worker_matches(worker, url_copy, url_len,
                               min_match, &max_match, mask)) {
                max_worker = worker;
            }
        }
    }

    return max_worker;
}

PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
                                                  proxy_balancer *balancer,
                                                  proxy_server_conf *conf,
                                                  const char *url)
{
    return ap_proxy_get_worker_ex(p, balancer, conf, url, 0);
}

/*
 * To create a worker from scratch first we define the
 * specifics of the worker; this is all local data.
 * We then allocate space for it if data needs to be
 * shared. This allows for dynamic addition during
 * config and runtime.
 */

PROXY_DECLARE(char *) ap_proxy_define_worker_ex(apr_pool_t *p,
                                             proxy_worker **worker,
                                             proxy_balancer *balancer,
                                             proxy_server_conf *conf,
                                             const char *url,
                                             unsigned int mask)
{
    apr_status_t rv;
    proxy_worker_shared *wshared;
    const char *ptr = NULL, *sockpath = NULL, *pdollars = NULL;
    apr_port_t port_of_scheme;
    int address_not_reusable = 0;
    apr_uri_t uri;

    /*
     * Look to see if we are using UDS:
--> --------------------

--> maximum size reached

--> --------------------

90%


¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.