]> granicus.if.org Git - apache/blobdiff - server/vhost.c
Merge r1741310, r1741461 from trunk:
[apache] / server / vhost.c
index 69fb6fb5cf4e78a68577cfee38469a7e0009c0da..b8b25e64ba5f0467c056ef8ee45a5fc3e319d114 100644 (file)
@@ -1,67 +1,32 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
+/* 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
  *
- * Copyright (c) 2000 The Apache Software Foundation.  All rights
- * reserved.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" must
- *    not be used to endorse or promote products derived from this
- *    software without prior written permission. For written
- *    permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without prior written
- *    permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- * Portions of this software are based upon public domain software
- * originally written at the National Center for Supercomputing Applications,
- * University of Illinois, Urbana-Champaign.
+ * 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.
  */
 
-/*
- * http_vhost.c: functions pertaining to virtual host addresses
- *     (configuration and run-time)
+/**
+ * @file  vhost.c
+ * @brief functions pertaining to virtual host addresses
+ *        (configuration and run-time)
  */
 
-#define CORE_PRIVATE
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
 #include "ap_config.h"
 #include "httpd.h"
 #include "http_config.h"
 #include "http_vhost.h"
 #include "http_protocol.h"
 #include "http_core.h"
-#include "apr_strings.h"
 
-#ifdef HAVE_ARPA_INET_H
+#if APR_HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
 
 /*
  * After all the definitions there's an explanation of how it's all put
@@ -98,9 +54,9 @@
 typedef struct name_chain name_chain;
 struct name_chain {
     name_chain *next;
-    server_addr_rec *sar;      /* the record causing it to be in
-                                * this chain (needed for port comparisons) */
-    server_rec *server;                /* the server to use on a match */
+    server_addr_rec *sar;       /* the record causing it to be in
+                                 * this chain (needed for port comparisons) */
+    server_rec *server;         /* the server to use on a match */
 };
 
 /* meta-list of ip addresses.  Each server_rec can be in possibly multiple
@@ -109,15 +65,17 @@ struct name_chain {
 typedef struct ipaddr_chain ipaddr_chain;
 struct ipaddr_chain {
     ipaddr_chain *next;
-    server_addr_rec *sar;      /* the record causing it to be in
-                                * this chain (need for both ip addr and port
-                                * comparisons) */
-    server_rec *server;                /* the server to use if this matches */
-    name_chain *names;         /* if non-NULL then a list of name-vhosts
-                                * sharing this address */
+    server_addr_rec *sar;       /* the record causing it to be in
+                                 * this chain (need for both ip addr and port
+                                 * comparisons) */
+    server_rec *server;         /* the server to use if this matches */
+    name_chain *names;          /* if non-NULL then a list of name-vhosts
+                                 * sharing this address */
+    name_chain *initialnames;   /* no runtime use, temporary storage of first
+                                 * NVH'es names */
 };
 
-/* This defines the size of the hash apr_table_t used for hashing ip addresses
+/* This defines the size of the hash table used for hashing ip addresses
  * of virtual hosts.  It must be a power of two.
  */
 #ifndef IPHASH_TABLE_SIZE
@@ -139,9 +97,12 @@ static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE];
 /* list of the _default_ servers */
 static ipaddr_chain *default_list;
 
-/* list of the NameVirtualHost addresses */
-static server_addr_rec *name_vhost_list;
-static server_addr_rec **name_vhost_list_tail;
+/* whether a config error was seen */
+static int config_error = 0;
+
+/* config check function */
+static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
+                              apr_pool_t *ptemp, server_rec *s);
 
 /*
  * How it's used:
@@ -167,12 +128,11 @@ static server_addr_rec **name_vhost_list_tail;
 
 
 /* called at the beginning of the config */
-void ap_init_vhost_config(apr_pool_t *p)
+AP_DECLARE(void) ap_init_vhost_config(apr_pool_t *p)
 {
     memset(iphash_table, 0, sizeof(iphash_table));
     default_list = NULL;
-    name_vhost_list = NULL;
-    name_vhost_list_tail = &name_vhost_list;
+    ap_hook_check_config(vhost_check_config, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
 
@@ -184,55 +144,86 @@ void ap_init_vhost_config(apr_pool_t *p)
  * port is the default port to assume
  */
 static const char *get_addresses(apr_pool_t *p, const char *w_,
-                                server_addr_rec ***paddr, apr_port_t port)
+                                 server_addr_rec ***paddr,
+                                 apr_port_t default_port)
 {
-    apr_in_addr_t my_addr;
+    apr_sockaddr_t *my_addr;
     server_addr_rec *sar;
-    char *t;
-    int i;
-    char *w;
-
-    if (*w_ == 0)
-       return NULL;
-
-    w=apr_pstrdup(p, w_);
-    t = strchr(w, ':');
-    if (t) {
-       if (strcmp(t + 1, "*") == 0) {
-           port = 0;
-       }
-       else if ((i = atoi(t + 1))) {
-           port = i;
-       }
-       else {
-           return ":port must be numeric";
-       }
-       *t = 0;
-    }
-
-    if (strcasecmp(w, "_default_") == 0
-        || strcmp(w, "255.255.255.255") == 0) {
-        my_addr.s_addr = DEFAULT_VHOST_ADDR;
-    } else {
-        if (apr_get_inaddr(&my_addr, w) != APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL,
-                "Cannot resolve host name %s --- ignoring!", w);
+    char *w, *host, *scope_id;
+    int wild_port;
+    apr_size_t wlen;
+    apr_port_t port;
+    apr_status_t rv;
+
+    if (*w_ == '\0')
+        return NULL;
+
+    wlen = strlen(w_);                   /* wlen must be > 0 at this point */
+    w = apr_pstrmemdup(p, w_, wlen);
+    /* apr_parse_addr_port() doesn't understand ":*" so handle that first. */
+    wild_port = 0;
+    if (w[wlen - 1] == '*') {
+        if (wlen < 2) {
+            wild_port = 1;
+        }
+        else if (w[wlen - 2] == ':') {
+            w[wlen - 2] = '\0';
+            wild_port = 1;
+        }
+    }
+    rv = apr_parse_addr_port(&host, &scope_id, &port, w, p);
+    /* If the string is "80", apr_parse_addr_port() will be happy and set
+     * host to NULL and port to 80, so watch out for that.
+     */
+    if (rv != APR_SUCCESS) {
+        return "The address or port is invalid";
+    }
+    if (!host) {
+        return "Missing address for VirtualHost";
+    }
+    if (scope_id) {
+        return "Scope ids are not supported";
+    }
+    if (!port && !wild_port) {
+        port = default_port;
+    }
+
+    if (strcmp(host, "*") == 0 || strcasecmp(host, "_default_") == 0) {
+        rv = apr_sockaddr_info_get(&my_addr, NULL, APR_UNSPEC, port, 0, p);
+        if (rv) {
+            return "Could not determine a wildcard address ('0.0.0.0') -- "
+                "check resolver configuration.";
+        }
+    }
+    else {
+        rv = apr_sockaddr_info_get(&my_addr, host, APR_UNSPEC, port, 0, p);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00547)
+                "Could not resolve host name %s -- ignoring!", host);
             return NULL;
         }
     }
 
-    sar = apr_pcalloc(p, sizeof(server_addr_rec));
-    **paddr = sar;
-    *paddr = &sar->next;
-    sar->host_addr = my_addr;
-    sar->host_port = port;
-    sar->virthost = apr_pstrdup(p, w);
+    /* Remember all addresses for the host */
+
+    do {
+        sar = apr_pcalloc(p, sizeof(server_addr_rec));
+        **paddr = sar;
+        *paddr = &sar->next;
+        sar->host_addr = my_addr;
+        sar->host_port = port;
+        sar->virthost = host;
+        my_addr = my_addr->next;
+    } while (my_addr);
+
     return NULL;
 }
 
 
 /* parse the <VirtualHost> addresses */
-const char *ap_parse_vhost_addrs(apr_pool_t *p, const char *hostname, server_rec *s)
+const char *ap_parse_vhost_addrs(apr_pool_t *p,
+                                 const char *hostname,
+                                 server_rec *s)
 {
     server_addr_rec **addrs;
     const char *err;
@@ -240,34 +231,42 @@ const char *ap_parse_vhost_addrs(apr_pool_t *p, const char *hostname, server_rec
     /* start the list of addreses */
     addrs = &s->addrs;
     while (hostname[0]) {
-       err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port);
-       if (err) {
-           *addrs = NULL;
-           return err;
-       }
+        err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port);
+        if (err) {
+            *addrs = NULL;
+            return err;
+        }
     }
     /* terminate the list */
     *addrs = NULL;
     if (s->addrs) {
-       if (s->addrs->host_port) {
-           /* override the default port which is inherited from main_server */
-           s->port = s->addrs->host_port;
-       }
+        if (s->addrs->host_port) {
+            /* override the default port which is inherited from main_server */
+            s->port = s->addrs->host_port;
+        }
     }
     return NULL;
 }
 
 
-const char *ap_set_name_virtual_host (cmd_parms *cmd, void *dummy,
-                                     const char *arg)
+AP_DECLARE_NONSTD(const char *)ap_set_name_virtual_host(cmd_parms *cmd,
+                                                        void *dummy,
+                                                        const char *arg)
 {
-    /* use whatever port the main server has at this point */
-    return get_addresses(cmd->pool, arg, &name_vhost_list_tail,
-                           cmd->server->port);
+    static int warnonce = 0;
+    if (++warnonce == 1) {
+        ap_log_error(APLOG_MARK, APLOG_NOTICE|APLOG_STARTUP, APR_SUCCESS, NULL, APLOGNO(00548)
+                     "NameVirtualHost has no effect and will be removed in the "
+                     "next release %s:%d",
+                     cmd->directive->filename,
+                     cmd->directive->line_num);
+    }
+
+    return NULL;
 }
 
 
-/* hash apr_table_t statistics, keep this in here for the beta period so
+/* hash table statistics, keep this in here for the beta period so
  * we can find out if the hash function is ok
  */
 #ifdef IPHASH_STATISTICS
@@ -288,34 +287,36 @@ static void dump_iphash_statistics(server_rec *main_s)
 
     total = 0;
     for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
-       count[i] = 0;
-       for (src = iphash_table[i]; src; src = src->next) {
-           ++count[i];
-           if (i < IPHASH_TABLE_SIZE) {
-               /* don't count the slop buckets in the total */
-               ++total;
-           }
-       }
+        count[i] = 0;
+        for (src = iphash_table[i]; src; src = src->next) {
+            ++count[i];
+            if (i < IPHASH_TABLE_SIZE) {
+                /* don't count the slop buckets in the total */
+                ++total;
+            }
+        }
     }
     qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare);
     p = buf + apr_snprintf(buf, sizeof(buf),
-                   "iphash: total hashed = %u, avg chain = %u, "
-                   "chain lengths (count x len):",
-                   total, total / IPHASH_TABLE_SIZE);
+                           APLOGNO(03235) "iphash: total hashed = %u, avg chain = %u, "
+                           "chain lengths (count x len):",
+                           total, total / IPHASH_TABLE_SIZE);
     total = 1;
     for (i = 1; i < IPHASH_TABLE_SIZE; ++i) {
-       if (count[i - 1] != count[i]) {
-           p += apr_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
-                            total, count[i - 1]);
-           total = 1;
-       }
-       else {
-           ++total;
-       }
+        if (count[i - 1] != count[i]) {
+            p += apr_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
+                              total, count[i - 1]);
+            total = 1;
+        }
+        else {
+            ++total;
+        }
     }
     p += apr_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
-                    total, count[IPHASH_TABLE_SIZE - 1]);
-    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, main_s, buf);
+                      total, count[IPHASH_TABLE_SIZE - 1]);
+    /* Intentional no APLOGNO */
+    /* buf provides APLOGNO */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, main_s, buf);
 }
 #endif
 
@@ -330,21 +331,32 @@ static void dump_iphash_statistics(server_rec *main_s)
  *
  * Hash function provided by David Hankins.
  */
-static apr_inline unsigned hash_inaddr(unsigned key)
+static APR_INLINE unsigned hash_inaddr(unsigned key)
 {
     key ^= (key >> 16);
     return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
 }
 
+static APR_INLINE unsigned hash_addr(struct apr_sockaddr_t *sa)
+{
+    unsigned key;
 
+    /* The key is the last four bytes of the IP address.
+     * For IPv4, this is the entire address, as always.
+     * For IPv6, this is usually part of the MAC address.
+     */
+    key = *(unsigned *)((char *)sa->ipaddr_ptr + sa->ipaddr_len - 4);
+    return hash_inaddr(key);
+}
 
 static ipaddr_chain *new_ipaddr_chain(apr_pool_t *p,
-                                   server_rec *s, server_addr_rec *sar)
+                                      server_rec *s, server_addr_rec *sar)
 {
     ipaddr_chain *new;
 
     new = apr_palloc(p, sizeof(*new));
     new->names = NULL;
+    new->initialnames = NULL;
     new->server = s;
     new->sar = sar;
     new->next = NULL;
@@ -352,7 +364,8 @@ static ipaddr_chain *new_ipaddr_chain(apr_pool_t *p,
 }
 
 
-static name_chain *new_name_chain(apr_pool_t *p, server_rec *s, server_addr_rec *sar)
+static name_chain *new_name_chain(apr_pool_t *p,
+                                  server_rec *s, server_addr_rec *sar)
 {
     name_chain *new;
 
@@ -364,82 +377,118 @@ static name_chain *new_name_chain(apr_pool_t *p, server_rec *s, server_addr_rec
 }
 
 
-static apr_inline ipaddr_chain *find_ipaddr(apr_in_addr_t *server_ip,
-    apr_port_t port)
+static APR_INLINE ipaddr_chain *find_ipaddr(apr_sockaddr_t *sa)
 {
     unsigned bucket;
-    ipaddr_chain *trav;
-    unsigned addr;
+    ipaddr_chain *trav = NULL;
+    ipaddr_chain *wild_match = NULL;
 
-    /* scan the hash apr_table_t for an exact match first */
-    addr = server_ip->s_addr;
-    bucket = hash_inaddr(addr);
+    /* scan the hash table for an exact match first */
+    bucket = hash_addr(sa);
     for (trav = iphash_table[bucket]; trav; trav = trav->next) {
-       server_addr_rec *sar = trav->sar;
-       if ((sar->host_addr.s_addr == addr)
-           && (sar->host_port == 0 || sar->host_port == port
-               || port == 0)) {
-           return trav;
-       }
+        server_addr_rec *sar = trav->sar;
+        apr_sockaddr_t *cur = sar->host_addr;
+
+        if (cur->port == sa->port) {
+            if (apr_sockaddr_equal(cur, sa)) {
+                return trav;
+            }
+        }
+        if (wild_match == NULL && (cur->port == 0 || sa->port == 0)) {
+            if (apr_sockaddr_equal(cur, sa)) {
+                /* don't break, continue looking for an exact match */
+                wild_match = trav;
+            }
+        }
     }
-    return NULL;
+    return wild_match;
 }
 
-
 static ipaddr_chain *find_default_server(apr_port_t port)
 {
     server_addr_rec *sar;
-    ipaddr_chain *trav;
+    ipaddr_chain *trav = NULL;
+    ipaddr_chain *wild_match = NULL;
 
     for (trav = default_list; trav; trav = trav->next) {
         sar = trav->sar;
-        if (sar->host_port == 0 || sar->host_port == port) {
+        if (sar->host_port == port) {
             /* match! */
-           return trav;
+            return trav;
+        }
+        if (wild_match == NULL && sar->host_port == 0) {
+            /* don't break, continue looking for an exact match */
+            wild_match = trav;
         }
     }
-    return NULL;
+    return wild_match;
 }
 
+#if APR_HAVE_IPV6
+#define IS_IN6_ANYADDR(ad) ((ad)->family == APR_INET6                   \
+                            && IN6_IS_ADDR_UNSPECIFIED(&(ad)->sa.sin6.sin6_addr))
+#else
+#define IS_IN6_ANYADDR(ad) (0)
+#endif
+
 static void dump_a_vhost(apr_file_t *f, ipaddr_chain *ic)
 {
     name_chain *nc;
     int len;
     char buf[MAX_STRING_LEN];
+    apr_sockaddr_t *ha = ic->sar->host_addr;
 
-    if (ic->sar->host_addr.s_addr == DEFAULT_VHOST_ADDR) {
-       len = apr_snprintf(buf, sizeof(buf), "_default_:%u",
-               ic->sar->host_port);
-    }
-    else if (ic->sar->host_addr.s_addr == INADDR_ANY) {
-       len = apr_snprintf(buf, sizeof(buf), "*:%u",
-               ic->sar->host_port);
+    if ((ha->family == APR_INET && ha->sa.sin.sin_addr.s_addr == INADDR_ANY)
+        || IS_IN6_ANYADDR(ha)) {
+        len = apr_snprintf(buf, sizeof(buf), "*:%u",
+                           ic->sar->host_port);
     }
     else {
-       len = apr_snprintf(buf, sizeof(buf), "%pA:%u",
-               &ic->sar->host_addr, ic->sar->host_port);
+        len = apr_snprintf(buf, sizeof(buf), "%pI", ha);
     }
     if (ic->sar->host_port == 0) {
-       buf[len-1] = '*';
+        buf[len-1] = '*';
     }
     if (ic->names == NULL) {
-       apr_fprintf(f, "%-22s %s (%s:%u)\n", buf, ic->server->server_hostname,
-               ic->server->defn_name, ic->server->defn_line_number);
-       return;
-    }
-    apr_fprintf(f, "%-22s is a NameVirtualHost\n"
-           "%22s default server %s (%s:%u)\n",
-           buf, "", ic->server->server_hostname,
-           ic->server->defn_name, ic->server->defn_line_number);
+        apr_file_printf(f, "%-22s %s (%s:%u)\n", buf,
+                        ic->server->server_hostname,
+                        ic->server->defn_name, ic->server->defn_line_number);
+        return;
+    }
+    apr_file_printf(f, "%-22s is a NameVirtualHost\n"
+                    "%8s default server %s (%s:%u)\n",
+                    buf, "", ic->server->server_hostname,
+                    ic->server->defn_name, ic->server->defn_line_number);
     for (nc = ic->names; nc; nc = nc->next) {
-       if (nc->sar->host_port) {
-           apr_fprintf(f, "%22s port %u ", "", nc->sar->host_port);
-       }
-       else {
-           apr_fprintf(f, "%22s port * ", "");
-       }
-       apr_fprintf(f, "namevhost %s (%s:%u)\n", nc->server->server_hostname,
-               nc->server->defn_name, nc->server->defn_line_number);
+        if (nc->sar->host_port) {
+            apr_file_printf(f, "%8s port %u ", "", nc->sar->host_port);
+        }
+        else {
+            apr_file_printf(f, "%8s port * ", "");
+        }
+        apr_file_printf(f, "namevhost %s (%s:%u)\n",
+                        nc->server->server_hostname,
+                        nc->server->defn_name, nc->server->defn_line_number);
+        if (nc->server->names) {
+            apr_array_header_t *names = nc->server->names;
+            char **name = (char **)names->elts;
+            int i;
+            for (i = 0; i < names->nelts; ++i) {
+                if (name[i]) {
+                    apr_file_printf(f, "%16s alias %s\n", "", name[i]);
+                }
+            }
+        }
+        if (nc->server->wild_names) {
+            apr_array_header_t *names = nc->server->wild_names;
+            char **name = (char **)names->elts;
+            int i;
+            for (i = 0; i < names->nelts; ++i) {
+                if (name[i]) {
+                    apr_file_printf(f, "%16s wild alias %s\n", "", name[i]);
+                }
+            }
+        }
     }
 }
 
@@ -448,77 +497,62 @@ static void dump_vhost_config(apr_file_t *f)
     ipaddr_chain *ic;
     int i;
 
-    apr_fprintf(f, "VirtualHost configuration:\n");
+    apr_file_printf(f, "VirtualHost configuration:\n");
+
+    /* non-wildcard servers */
     for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
-       for (ic = iphash_table[i]; ic; ic = ic->next) {
-           dump_a_vhost(f, ic);
-       }
+        for (ic = iphash_table[i]; ic; ic = ic->next) {
+            dump_a_vhost(f, ic);
+        }
     }
-    if (default_list) {
-       apr_fprintf(f, "wildcard NameVirtualHosts and _default_ servers:\n");
-       for (ic = default_list; ic; ic = ic->next) {
-           dump_a_vhost(f, ic);
-       }
+
+    /* wildcard servers */
+    for (ic = default_list; ic; ic = ic->next) {
+        dump_a_vhost(f, ic);
     }
 }
 
+
 /*
- * Two helper functions for ap_fini_vhost_config()
+ * When a second or later virtual host maps to the same IP chain,
+ * add the relevant server names to the chain.  Special care is taken
+ * to avoid adding ic->names until we're sure there are multiple VH'es.
  */
-static int add_name_vhost_config(apr_pool_t *p, server_rec *main_s, server_rec *s,
-                                server_addr_rec *sar, ipaddr_chain *ic)
+static void add_name_vhost_config(apr_pool_t *p, server_rec *main_s,
+                                 server_rec *s, server_addr_rec *sar,
+                                 ipaddr_chain *ic)
 {
-    /* the first time we encounter a NameVirtualHost address
-     * ic->server will be NULL, on subsequent encounters
-     * ic->names will be non-NULL.
-     */
-    if (ic->names || ic->server == NULL) {
-       name_chain *nc = new_name_chain(p, s, sar);
-       nc->next = ic->names;
-       ic->names = nc;
-       ic->server = s;
-       if (sar->host_port != ic->sar->host_port) {
-           /* one of the two is a * port, the other isn't */
-           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, main_s,
-                   "VirtualHost %s:%u -- mixing * "
-                   "ports and non-* ports with "
-                   "a NameVirtualHost address is not supported,"
-                   " proceeding with undefined results",
-                   sar->virthost, sar->host_port);
-       }
-       return 1;
-    }
-    else {
-       /* IP-based vhosts are handled by the caller */
-       return 0;
-    }
-}
 
-static void remove_unused_name_vhosts(server_rec *main_s, ipaddr_chain **pic)
-{
-    while (*pic) {
-       ipaddr_chain *ic = *pic;
-       
-       if (ic->server == NULL) {
-           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, main_s,
-                        "NameVirtualHost %s:%u has no VirtualHosts",
-                        ic->sar->virthost, ic->sar->host_port);
-           *pic = ic->next;
-       }
-       else if (ic->names == NULL) {
-           /* if server != NULL and names == NULL then we're done
-            * looking at NameVirtualHosts
-            */
-           break;
-       }
-       else {
-           pic = &ic->next;
-       }
-    }
+   name_chain *nc = new_name_chain(p, s, sar);
+   nc->next = ic->names;
+
+   /* iterating backwards, so each one we see becomes the current default server */
+   ic->server = s;
+
+   if (ic->names == NULL) {
+       if (ic->initialnames == NULL) {
+           /* first pass, set these names aside in case we see another VH.
+            * Until then, this looks like an IP-based VH to runtime.
+            */
+           ic->initialnames = nc;
+       }
+       else {
+           /* second pass through this chain -- this really is an NVH, and we
+            * have two sets of names to link in.
+            */
+           nc->next = ic->initialnames;
+           ic->names = nc;
+           ic->initialnames = NULL;
+       }
+   }
+   else {
+       /* 3rd or more -- just keep stacking the names */
+       ic->names = nc;
+   }
 }
 
 /* compile the tables and such we need to do the run-time vhost lookups */
-void ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
+AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
 {
     server_addr_rec *sar;
     int has_default_vhost_addr;
@@ -526,152 +560,128 @@ void ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
     int i;
     ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE];
 
-    /* terminate the name_vhost list */
-    *name_vhost_list_tail = NULL;
-
     /* Main host first */
     s = main_s;
 
     if (!s->server_hostname) {
-       s->server_hostname = ap_get_local_host(p);
+        s->server_hostname = ap_get_local_host(p);
     }
 
     /* initialize the tails */
     for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
-       iphash_table_tail[i] = &iphash_table[i];
+        iphash_table_tail[i] = &iphash_table[i];
     }
 
-    /* The first things to go into the hash apr_table_t are the NameVirtualHosts
-     * Since name_vhost_list is in the same order that the directives
-     * occured in the config file, we'll copy it in that order.
-     */
-    for (sar = name_vhost_list; sar; sar = sar->next) {
-       unsigned bucket = hash_inaddr(sar->host_addr.s_addr);
-       ipaddr_chain *ic = new_ipaddr_chain(p, NULL, sar);
-
-       if (sar->host_addr.s_addr != INADDR_ANY) {
-           *iphash_table_tail[bucket] = ic;
-           iphash_table_tail[bucket] = &ic->next;
-       }
-       else {
-           /* A wildcard NameVirtualHost goes on the default_list so
-            * that it can catch incoming requests on any address.
-            */
-           ic->next = default_list;
-           default_list = ic;
-       }
-       /* Notice that what we've done is insert an ipaddr_chain with
-        * both server and names NULL. This fact is used to spot name-
-        * based vhosts in add_name_vhost_config().
-        */
-    }
-
-    /* The next things to go into the hash apr_table_t are the virtual hosts
+    /* The next things to go into the hash table are the virtual hosts
      * themselves.  They're listed off of main_s->next in the reverse
      * order they occured in the config file, so we insert them at
      * the iphash_table_tail but don't advance the tail.
      */
 
     for (s = main_s->next; s; s = s->next) {
-       has_default_vhost_addr = 0;
-       for (sar = s->addrs; sar; sar = sar->next) {
-           ipaddr_chain *ic;
-
-           if (sar->host_addr.s_addr == DEFAULT_VHOST_ADDR
-               || sar->host_addr.s_addr == INADDR_ANY) {
-               ic = find_default_server(sar->host_port);
-               if (!ic || !add_name_vhost_config(p, main_s, s, sar, ic)) {
-                   if (ic && ic->sar->host_port != 0) {
-                       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
-                           0, main_s, "_default_ VirtualHost overlap on port %u,"
-                           " the first has precedence", sar->host_port);
-                   }
-                   ic = new_ipaddr_chain(p, s, sar);
-                   ic->next = default_list;
-                   default_list = ic;
-               }
-               has_default_vhost_addr = 1;
-           }
-           else {
-               /* see if it matches something we've already got */
-               ic = find_ipaddr(&sar->host_addr, sar->host_port);
-
-               if (!ic) {
-                   unsigned bucket = hash_inaddr(sar->host_addr.s_addr);
-
-                   ic = new_ipaddr_chain(p, s, sar);
-                   ic->next = *iphash_table_tail[bucket];
-                   *iphash_table_tail[bucket] = ic;
-               }
-               else if (!add_name_vhost_config(p, main_s, s, sar, ic)) {
-                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, main_s,
-                           "VirtualHost %s:%u overlaps with "
-                           "VirtualHost %s:%u, the first has precedence, "
-                           "perhaps you need a NameVirtualHost directive",
-                           sar->virthost, sar->host_port,
-                           ic->sar->virthost, ic->sar->host_port);
-                   ic->sar = sar;
-                   ic->server = s;
-               }
-           }
-       }
-
-       /* Ok now we want to set up a server_hostname if the user was
-        * silly enough to forget one.
-        * XXX: This is silly we should just crash and burn.
-        */
-       if (!s->server_hostname) {
-           if (has_default_vhost_addr) {
-               s->server_hostname = main_s->server_hostname;
-           }
-           else if (!s->addrs) {
-               /* what else can we do?  at this point this vhost has
-                   no configured name, probably because they used
-                   DNS in the VirtualHost statement.  It's disabled
-                   anyhow by the host matching code.  -djg */
-               s->server_hostname =
-                   apr_pstrdup(p, "bogus_host_without_forward_dns");
-           }
-           else {
-               struct hostent *h;
-
-               if ((h = gethostbyaddr((char *) &(s->addrs->host_addr),
-                                       sizeof(struct in_addr), AF_INET))) {
-                   s->server_hostname = apr_pstrdup(p, (char *) h->h_name);
-               }
-               else {
-                   /* again, what can we do?  They didn't specify a
-                      ServerName, and their DNS isn't working. -djg */
-                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, main_s,
-                           "Failed to resolve server name "
-                           "for %s (check DNS) -- or specify an explicit "
-                           "ServerName",
-                           inet_ntoa(s->addrs->host_addr));
-                   s->server_hostname =
-                       apr_pstrdup(p, "bogus_host_without_reverse_dns");
-               }
-           }
-       }
-    }
-
-    /* now go through and delete any NameVirtualHosts that didn't have any
-     * hosts associated with them.  Lamers.
-     */
-    for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
-       remove_unused_name_vhosts(main_s, &iphash_table[i]);
+        server_addr_rec *sar_prev = NULL;
+        has_default_vhost_addr = 0;
+        for (sar = s->addrs; sar; sar = sar->next) {
+            ipaddr_chain *ic;
+            char inaddr_any[16] = {0}; /* big enough to handle IPv4 or IPv6 */
+            /* XXX: this treats 0.0.0.0 as a "default" server which matches no-exact-match for IPv6 */
+            if (!memcmp(sar->host_addr->ipaddr_ptr, inaddr_any, sar->host_addr->ipaddr_len)) {
+                ic = find_default_server(sar->host_port);
+
+                if (ic && sar->host_port == ic->sar->host_port) { /* we're a match for an existing "default server"  */
+                    if (!sar_prev || memcmp(sar_prev->host_addr->ipaddr_ptr, inaddr_any, sar_prev->host_addr->ipaddr_len)
+                                  || sar_prev->host_port != sar->host_port) { 
+                        add_name_vhost_config(p, main_s, s, sar, ic);
+                    }
+                }
+                else { 
+                    /* No default server, or we found a default server but
+                    ** exactly one of us is a wildcard port, which means we want
+                    ** two ip-based vhosts not an NVH with two names
+                    */
+                    ic = new_ipaddr_chain(p, s, sar);
+                    ic->next = default_list;
+                    default_list = ic;
+                    add_name_vhost_config(p, main_s, s, sar, ic);
+                }
+                has_default_vhost_addr = 1;
+            }
+            else {
+                /* see if it matches something we've already got */
+                ic = find_ipaddr(sar->host_addr);
+
+                if (!ic || sar->host_port != ic->sar->host_port) {
+                    /* No matching server, or we found a matching server but
+                    ** exactly one of us is a wildcard port, which means we want
+                    ** two ip-based vhosts not an NVH with two names
+                    */
+                    unsigned bucket = hash_addr(sar->host_addr);
+                    ic = new_ipaddr_chain(p, s, sar);
+                    ic->next = *iphash_table_tail[bucket];
+                    *iphash_table_tail[bucket] = ic;
+                }
+                add_name_vhost_config(p, main_s, s, sar, ic);
+            }
+            sar_prev = sar;
+        }
+
+        /* Ok now we want to set up a server_hostname if the user was
+         * silly enough to forget one.
+         * XXX: This is silly we should just crash and burn.
+         */
+        if (!s->server_hostname) {
+            if (has_default_vhost_addr) {
+                s->server_hostname = main_s->server_hostname;
+            }
+            else if (!s->addrs) {
+                /* what else can we do?  at this point this vhost has
+                    no configured name, probably because they used
+                    DNS in the VirtualHost statement.  It's disabled
+                    anyhow by the host matching code.  -djg */
+                s->server_hostname =
+                    apr_pstrdup(p, "bogus_host_without_forward_dns");
+            }
+            else {
+                apr_status_t rv;
+                char *hostname;
+
+                rv = apr_getnameinfo(&hostname, s->addrs->host_addr, 0);
+                if (rv == APR_SUCCESS) {
+                    s->server_hostname = apr_pstrdup(p, hostname);
+                }
+                else {
+                    /* again, what can we do?  They didn't specify a
+                       ServerName, and their DNS isn't working. -djg */
+                    char *ipaddr_str;
+
+                    apr_sockaddr_ip_get(&ipaddr_str, s->addrs->host_addr);
+                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, main_s, APLOGNO(00549)
+                                 "Failed to resolve server name "
+                                 "for %s (check DNS) -- or specify an explicit "
+                                 "ServerName",
+                                 ipaddr_str);
+                    s->server_hostname =
+                        apr_pstrdup(p, "bogus_host_without_reverse_dns");
+                }
+            }
+        }
     }
-    remove_unused_name_vhosts(main_s, &default_list);
 
 #ifdef IPHASH_STATISTICS
     dump_iphash_statistics(main_s);
 #endif
-    if (getenv("DUMP_VHOSTS")) {
+    if (ap_exists_config_define("DUMP_VHOSTS")) {
         apr_file_t *thefile = NULL;
-        apr_open_stderr(&thefile, p);
-       dump_vhost_config(thefile);
+        apr_file_open_stdout(&thefile, p);
+        dump_vhost_config(thefile);
     }
 }
 
+static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
+                              apr_pool_t *ptemp, server_rec *s)
+{
+    return config_error ? !OK : OK;
+}
 
 /*****************************************************************************
  * run-time vhost matching functions
@@ -692,52 +702,73 @@ void ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
  */
 static void fix_hostname(request_rec *r)
 {
-    char *host = apr_palloc(r->pool, strlen(r->hostname) + 1);
-    const char *src;
+    char *host, *scope_id;
     char *dst;
+    apr_port_t port;
+    apr_status_t rv;
+    const char *c;
 
-    /* check and copy the host part */
-    src = r->hostname;
-    dst = host;
-    while (*src) {
-       if (!apr_isalnum(*src) && *src != '-') {
-           if (*src == '.') {
-               *dst++ = *src++;
-               if (*src == '.')
-                   goto bad;
-               else
-                   continue;
-           }
-           if (*src == ':')
-               break;
-           else
-               goto bad;
-       } else {
-           *dst++ = *src++;
-       }
-    }
-    /* check the port part */
-    if (*src++ == ':') {
-       while (*src) {
-           if (!apr_isdigit(*src++)) {
-               goto bad;
-           }
-       }
-    }
-    /* strip trailing gubbins */
-    if (dst > host && dst[-1] == '.') {
-       dst[-1] = '\0';
-    } else {
-       dst[0] = '\0';
+    /* According to RFC 2616, Host header field CAN be blank. */
+    if (!*r->hostname) {
+        return;
     }
 
+    /* apr_parse_addr_port will interpret a bare integer as a port
+     * which is incorrect in this context.  So treat it separately.
+     */
+    for (c = r->hostname; apr_isdigit(*c); ++c);
+    if (!*c) {  /* pure integer */
+        return;
+    }
+
+    rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
+    if (rv != APR_SUCCESS || scope_id) {
+        goto bad;
+    }
+
+    if (port) {
+        /* Don't throw the Host: header's port number away:
+           save it in parsed_uri -- ap_get_server_port() needs it! */
+        /* @@@ XXX there should be a better way to pass the port.
+         *         Like r->hostname, there should be a r->portno
+         */
+        r->parsed_uri.port = port;
+        r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
+    }
+
+    /* if the hostname is an IPv6 numeric address string, it was validated
+     * already; otherwise, further validation is needed
+     */
+    if (r->hostname[0] != '[') {
+        for (dst = host; *dst; dst++) {
+            if (apr_islower(*dst)) {
+                /* leave char unchanged */
+            }
+            else if (*dst == '.') {
+                if (*(dst + 1) == '.') {
+                    goto bad;
+                }
+            }
+            else if (apr_isupper(*dst)) {
+                *dst = apr_tolower(*dst);
+            }
+            else if (*dst == '/' || *dst == '\\') {
+                goto bad;
+            }
+        }
+        /* strip trailing gubbins */
+        if (dst > host && dst[-1] == '.') {
+            dst[-1] = '\0';
+        }
+    }
     r->hostname = host;
     return;
 
 bad:
     r->status = HTTP_BAD_REQUEST;
-    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                 "Client sent malformed Host header");
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00550)
+                  "Client sent malformed Host header: %s",
+                  r->hostname);
     return;
 }
 
@@ -750,27 +781,27 @@ static int matches_aliases(server_rec *s, const char *host)
 
     /* match ServerName */
     if (!strcasecmp(host, s->server_hostname)) {
-       return 1;
+        return 1;
     }
 
     /* search all the aliases from ServerAlias directive */
     names = s->names;
     if (names) {
-       char **name = (char **) names->elts;
-       for (i = 0; i < names->nelts; ++i) {
-           if(!name[i]) continue;
-           if (!strcasecmp(host, name[i]))
-               return 1;
-       }
+        char **name = (char **) names->elts;
+        for (i = 0; i < names->nelts; ++i) {
+            if(!name[i]) continue;
+            if (!strcasecmp(host, name[i]))
+                return 1;
+        }
     }
     names = s->wild_names;
     if (names) {
-       char **name = (char **) names->elts;
-       for (i = 0; i < names->nelts; ++i) {
-           if(!name[i]) continue;
-           if (!ap_strcasecmp_match(host, name[i]))
-               return 1;
-       }
+        char **name = (char **) names->elts;
+        for (i = 0; i < names->nelts; ++i) {
+            if(!name[i]) continue;
+            if (!ap_strcasecmp_match(host, name[i]))
+                return 1;
+        }
     }
     return 0;
 }
@@ -784,7 +815,7 @@ static int matches_aliases(server_rec *s, const char *host)
  * the ServerName and/or ServerAliases.
  */
 AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
-    apr_port_t port)
+                                         apr_port_t port)
 {
     server_rec *s;
     server_addr_rec *sar;
@@ -793,7 +824,7 @@ AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
 
     /* search all the <VirtualHost> values */
     /* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing
-     * consider: 
+     * consider:
      *
      *     NameVirtualHost 10.1.1.1
      *     <VirtualHost 10.1.1.1>
@@ -808,16 +839,16 @@ AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
      * it would really go to v1.
      */
     for (sar = s->addrs; sar; sar = sar->next) {
-       if ((sar->host_port == 0 || port == sar->host_port)
-           && !strcasecmp(host, sar->virthost)) {
-           return 1;
-       }
+        if ((sar->host_port == 0 || port == sar->host_port)
+            && !strcasecmp(host, sar->virthost)) {
+            return 1;
+        }
     }
 
     /* the Port has to match now, because the rest don't have ports associated
      * with them. */
     if (port != s->port) {
-       return 0;
+        return 0;
     }
 
     return matches_aliases(s, host);
@@ -842,11 +873,14 @@ static void check_hostalias(request_rec *r)
     const char *host = r->hostname;
     apr_port_t port;
     server_rec *s;
+    server_rec *virthost_s;
     server_rec *last_s;
     name_chain *src;
 
+    virthost_s = NULL;
     last_s = NULL;
-    apr_get_port(&port, APR_LOCAL, r->connection->client_socket);
+
+    port = r->connection->local_addr->port;
 
     /* Recall that the name_chain is a list of server_addr_recs, some of
      * whose ports may not match.  Also each server may appear more than
@@ -860,33 +894,44 @@ static void check_hostalias(request_rec *r)
     for (src = r->connection->vhost_lookup_data; src; src = src->next) {
         server_addr_rec *sar;
 
-       /* We only consider addresses on the name_chain which have a matching
-        * port
-        */
-       sar = src->sar;
-       if (sar->host_port != 0 && port != sar->host_port) {
-           continue;
-       }
+        /* We only consider addresses on the name_chain which have a matching
+         * port
+         */
+        sar = src->sar;
+        if (sar->host_port != 0 && port != sar->host_port) {
+            continue;
+        }
 
         s = src->server;
 
-       /* does it match the virthost from the sar? */
-       if (!strcasecmp(host, sar->virthost)) {
-           goto found;
-       }
-
-       if (s == last_s) {
-           /* we've already done ServerName and ServerAlias checks for this
-            * vhost
-            */
-           continue;
-       }
-       last_s = s;
+        /* If we still need to do ServerName and ServerAlias checks for this
+         * server, do them now.
+         */
+        if (s != last_s) {
+            /* does it match any ServerName or ServerAlias directive? */
+            if (matches_aliases(s, host)) {
+                goto found;
+            }
+        }
+        last_s = s;
+
+        /* Fallback: does it match the virthost from the sar? */
+        if (!strcasecmp(host, sar->virthost)) {
+            /* only the first match is used */
+            if (virthost_s == NULL) {
+                virthost_s = s;
+            }
+        }
+    }
 
-       if (matches_aliases(s, host)) {
-           goto found;
-       }
+    /* If ServerName and ServerAlias check failed, we end up here.  If it
+     * matches a VirtualHost, virthost_s is set. Use that as fallback
+     */
+    if (virthost_s) {
+        s = virthost_s;
+        goto found;
     }
+
     return;
 
 found:
@@ -901,8 +946,9 @@ static void check_serverpath(request_rec *r)
     server_rec *last_s;
     name_chain *src;
     apr_port_t port;
-    apr_get_port(&port, APR_LOCAL, r->connection->client_socket);
-   
+
+    port = r->connection->local_addr->port;
+
     /*
      * This is in conjunction with the ServerPath code in http_core, so we
      * get the right host attached to a non- Host-sending request.
@@ -913,37 +959,37 @@ static void check_serverpath(request_rec *r)
 
     last_s = NULL;
     for (src = r->connection->vhost_lookup_data; src; src = src->next) {
-       /* We only consider addresses on the name_chain which have a matching
-        * port
-        */
-       if (src->sar->host_port != 0 && port != src->sar->host_port) {
-           continue;
-       }
+        /* We only consider addresses on the name_chain which have a matching
+         * port
+         */
+        if (src->sar->host_port != 0 && port != src->sar->host_port) {
+            continue;
+        }
 
         s = src->server;
-       if (s == last_s) {
-           continue;
-       }
-       last_s = s;
+        if (s == last_s) {
+            continue;
+        }
+        last_s = s;
 
         if (s->path && !strncmp(r->uri, s->path, s->pathlen) &&
             (s->path[s->pathlen - 1] == '/' ||
              r->uri[s->pathlen] == '/' ||
              r->uri[s->pathlen] == '\0')) {
             r->server = s;
-           return;
-       }
+            return;
+        }
     }
 }
 
 
-void ap_update_vhost_from_headers(request_rec *r)
+AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
 {
     /* must set this for HTTP/1.1 support */
     if (r->hostname || (r->hostname = apr_table_get(r->headers_in, "Host"))) {
-       fix_hostname(r);
-       if (r->status != HTTP_OK)
-           return;
+        fix_hostname(r);
+        if (r->status != HTTP_OK)
+            return;
     }
     /* check if we tucked away a name_chain */
     if (r->connection->vhost_lookup_data) {
@@ -954,33 +1000,85 @@ void ap_update_vhost_from_headers(request_rec *r)
     }
 }
 
+/**
+ * For every virtual host on this connection, call func_cb.
+ */
+AP_DECLARE(int) ap_vhost_iterate_given_conn(conn_rec *conn,
+                                            ap_vhost_iterate_conn_cb func_cb,
+                                            void* baton)
+{
+    server_rec *s;
+    server_rec *last_s;
+    name_chain *src;
+    apr_port_t port;
+    int rv = 0;
+
+    if (conn->vhost_lookup_data) {
+        last_s = NULL;
+        port = conn->local_addr->port;
+
+        for (src = conn->vhost_lookup_data; src; src = src->next) {
+            server_addr_rec *sar;
+
+            /* We only consider addresses on the name_chain which have a
+             * matching port.
+             */
+            sar = src->sar;
+            if (sar->host_port != 0 && port != sar->host_port) {
+                continue;
+            }
+
+            s = src->server;
+
+            if (s == last_s) {
+                /* we've already done a callback for this vhost. */
+                continue;
+            }
+
+            last_s = s;
+
+            rv = func_cb(baton, conn, s);
+
+            if (rv != 0) {
+                break;
+            }
+        }
+    }
+    else {
+        rv = func_cb(baton, conn, conn->base_server);
+    }
+
+    return rv;
+}
 
 /* Called for a new connection which has a known local_addr.  Note that the
  * new connection is assumed to have conn->server == main server.
  */
-void ap_update_vhost_given_ip(conn_rec *conn)
+AP_DECLARE(void) ap_update_vhost_given_ip(conn_rec *conn)
 {
     ipaddr_chain *trav;
     apr_port_t port;
-    apr_get_port(&port, APR_LOCAL, conn->client_socket);
 
-    /* scan the hash apr_table_t for an exact match first */
-    trav = find_ipaddr(&conn->local_addr.sin_addr, port);
+    /* scan the hash table for an exact match first */
+    trav = find_ipaddr(conn->local_addr);
+
     if (trav) {
-       /* save the name_chain for later in case this is a name-vhost */
-       conn->vhost_lookup_data = trav->names;
-       conn->base_server = trav->server;
-       return;
+        /* save the name_chain for later in case this is a name-vhost */
+        conn->vhost_lookup_data = trav->names;
+        conn->base_server = trav->server;
+        return;
     }
 
     /* maybe there's a default server or wildcard name-based vhost
      * matching this port
      */
+    port = conn->local_addr->port;
+
     trav = find_default_server(port);
     if (trav) {
-       conn->vhost_lookup_data = trav->names;
-       conn->base_server = trav->server;
-       return;
+        conn->vhost_lookup_data = trav->names;
+        conn->base_server = trav->server;
+        return;
     }
 
     /* otherwise we're stuck with just the main server