mirror of
https://github.com/apache/httpd.git
synced 2025-08-15 23:27:39 +00:00

warnings! git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85595 13f79535-47bb-0310-9956-ffa450edef68
967 lines
27 KiB
C
967 lines
27 KiB
C
/* ====================================================================
|
|
* The Apache Software License, Version 1.1
|
|
*
|
|
* Copyright (c) 2000 The Apache Software Foundation. All rights
|
|
* reserved.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* http_vhost.c: functions pertaining to virtual host addresses
|
|
* (configuration and run-time)
|
|
*/
|
|
|
|
#define CORE_PRIVATE
|
|
#include "ap_config.h"
|
|
#include "httpd.h"
|
|
#include "http_config.h"
|
|
#include "http_log.h"
|
|
#include "http_vhost.h"
|
|
#include "http_protocol.h"
|
|
#include "http_core.h"
|
|
|
|
#ifdef 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
|
|
|
|
/*
|
|
* After all the definitions there's an explanation of how it's all put
|
|
* together.
|
|
*/
|
|
|
|
/* meta-list of name-vhosts. Each server_rec can be in possibly multiple
|
|
* lists of name-vhosts.
|
|
*/
|
|
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 */
|
|
};
|
|
|
|
/* meta-list of ip addresses. Each server_rec can be in possibly multiple
|
|
* hash chains since it can have multiple ips.
|
|
*/
|
|
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 */
|
|
};
|
|
|
|
/* This defines the size of the hash ap_table_t used for hashing ip addresses
|
|
* of virtual hosts. It must be a power of two.
|
|
*/
|
|
#ifndef IPHASH_TABLE_SIZE
|
|
#define IPHASH_TABLE_SIZE 256
|
|
#endif
|
|
|
|
/* A (n) bucket hash table, each entry has a pointer to a server rec and
|
|
* a pointer to the other entries in that bucket. Each individual address,
|
|
* even for virtualhosts with multiple addresses, has an entry in this hash
|
|
* table. There are extra buckets for _default_, and name-vhost entries.
|
|
*
|
|
* Note that after config time this is constant, so it is thread-safe.
|
|
*/
|
|
static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE];
|
|
|
|
/* dump out statistics about the hash function */
|
|
/* #define IPHASH_STATISTICS */
|
|
|
|
/* 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;
|
|
|
|
/*
|
|
* How it's used:
|
|
*
|
|
* The ip address determines which chain in iphash_table is interesting, then
|
|
* a comparison is done down that chain to find the first ipaddr_chain whose
|
|
* sar matches the address:port pair.
|
|
*
|
|
* If that ipaddr_chain has names == NULL then you're done, it's an ip-vhost.
|
|
*
|
|
* Otherwise it's a name-vhost list, and the default is the server in the
|
|
* ipaddr_chain record. We tuck away the ipaddr_chain record in the
|
|
* conn_rec field vhost_lookup_data. Later on after the headers we get a
|
|
* second chance, and we use the name_chain to figure out what name-vhost
|
|
* matches the headers.
|
|
*
|
|
* If there was no ip address match in the iphash_table then do a lookup
|
|
* in the default_list.
|
|
*
|
|
* How it's put together ... well you should be able to figure that out
|
|
* from how it's used. Or something like that.
|
|
*/
|
|
|
|
|
|
/* called at the beginning of the config */
|
|
void ap_init_vhost_config(ap_pool_t *p)
|
|
{
|
|
memset(iphash_table, 0, sizeof(iphash_table));
|
|
default_list = NULL;
|
|
name_vhost_list = NULL;
|
|
name_vhost_list_tail = &name_vhost_list;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parses a host of the form <address>[:port]
|
|
* paddr is used to create a list in the order of input
|
|
* **paddr is the ->next pointer of the last entry (or s->addrs)
|
|
* *paddr is the variable used to keep track of **paddr between calls
|
|
* port is the default port to assume
|
|
*/
|
|
static const char *get_addresses(ap_pool_t *p, const char *w,
|
|
server_addr_rec ***paddr, unsigned port)
|
|
{
|
|
struct hostent *hep;
|
|
unsigned long my_addr;
|
|
server_addr_rec *sar;
|
|
char *t;
|
|
int i, is_an_ip_addr;
|
|
|
|
if (*w == 0)
|
|
return NULL;
|
|
|
|
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;
|
|
}
|
|
|
|
is_an_ip_addr = 0;
|
|
if (strcmp(w, "*") == 0) {
|
|
my_addr = htonl(INADDR_ANY);
|
|
is_an_ip_addr = 1;
|
|
}
|
|
else if (strcasecmp(w, "_default_") == 0
|
|
|| strcmp(w, "255.255.255.255") == 0) {
|
|
my_addr = DEFAULT_VHOST_ADDR;
|
|
is_an_ip_addr = 1;
|
|
}
|
|
else if ((my_addr = ap_inet_addr(w)) != INADDR_NONE) {
|
|
is_an_ip_addr = 1;
|
|
}
|
|
if (is_an_ip_addr) {
|
|
sar = ap_pcalloc(p, sizeof(server_addr_rec));
|
|
**paddr = sar;
|
|
*paddr = &sar->next;
|
|
sar->host_addr.s_addr = my_addr;
|
|
sar->host_port = port;
|
|
sar->virthost = ap_pstrdup(p, w);
|
|
if (t != NULL)
|
|
*t = ':';
|
|
return NULL;
|
|
}
|
|
|
|
hep = gethostbyname(w);
|
|
|
|
if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
|
|
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL,
|
|
"Cannot resolve host name %s --- ignoring!", w);
|
|
if (t != NULL)
|
|
*t = ':';
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; hep->h_addr_list[i]; ++i) {
|
|
sar = ap_pcalloc(p, sizeof(server_addr_rec));
|
|
**paddr = sar;
|
|
*paddr = &sar->next;
|
|
sar->host_addr = *(struct in_addr *) hep->h_addr_list[i];
|
|
sar->host_port = port;
|
|
sar->virthost = ap_pstrdup(p, w);
|
|
}
|
|
|
|
if (t != NULL)
|
|
*t = ':';
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* parse the <VirtualHost> addresses */
|
|
const char *ap_parse_vhost_addrs(ap_pool_t *p, const char *hostname, server_rec *s)
|
|
{
|
|
server_addr_rec **addrs;
|
|
const char *err;
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
/* 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;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/* hash ap_table_t statistics, keep this in here for the beta period so
|
|
* we can find out if the hash function is ok
|
|
*/
|
|
#ifdef IPHASH_STATISTICS
|
|
static int iphash_compare(const void *a, const void *b)
|
|
{
|
|
return (*(const int *) b - *(const int *) a);
|
|
}
|
|
|
|
|
|
static void dump_iphash_statistics(server_rec *main_s)
|
|
{
|
|
unsigned count[IPHASH_TABLE_SIZE];
|
|
int i;
|
|
ipaddr_chain *src;
|
|
unsigned total;
|
|
char buf[HUGE_STRING_LEN];
|
|
char *p;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare);
|
|
p = buf + ap_snprintf(buf, sizeof(buf),
|
|
"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 += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
|
|
total, count[i - 1]);
|
|
total = 1;
|
|
}
|
|
else {
|
|
++total;
|
|
}
|
|
}
|
|
p += ap_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);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* This hashing function is designed to get good distribution in the cases
|
|
* where the server is handling entire "networks" of servers. i.e. a
|
|
* whack of /24s. This is probably the most common configuration for
|
|
* ISPs with large virtual servers.
|
|
*
|
|
* NOTE: This function is symmetric (i.e. collapses all 4 octets
|
|
* into one), so machine byte order (big/little endianness) does not matter.
|
|
*
|
|
* Hash function provided by David Hankins.
|
|
*/
|
|
static ap_inline unsigned hash_inaddr(unsigned key)
|
|
{
|
|
key ^= (key >> 16);
|
|
return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
|
|
}
|
|
|
|
|
|
|
|
static ipaddr_chain *new_ipaddr_chain(ap_pool_t *p,
|
|
server_rec *s, server_addr_rec *sar)
|
|
{
|
|
ipaddr_chain *new;
|
|
|
|
new = ap_palloc(p, sizeof(*new));
|
|
new->names = NULL;
|
|
new->server = s;
|
|
new->sar = sar;
|
|
new->next = NULL;
|
|
return new;
|
|
}
|
|
|
|
|
|
static name_chain *new_name_chain(ap_pool_t *p, server_rec *s, server_addr_rec *sar)
|
|
{
|
|
name_chain *new;
|
|
|
|
new = ap_palloc(p, sizeof(*new));
|
|
new->server = s;
|
|
new->sar = sar;
|
|
new->next = NULL;
|
|
return new;
|
|
}
|
|
|
|
|
|
static ap_inline ipaddr_chain *find_ipaddr(struct in_addr *server_ip,
|
|
unsigned port)
|
|
{
|
|
unsigned bucket;
|
|
ipaddr_chain *trav;
|
|
unsigned addr;
|
|
|
|
/* scan the hash ap_table_t for an exact match first */
|
|
addr = server_ip->s_addr;
|
|
bucket = hash_inaddr(addr);
|
|
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;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static ipaddr_chain *find_default_server(unsigned port)
|
|
{
|
|
server_addr_rec *sar;
|
|
ipaddr_chain *trav;
|
|
|
|
for (trav = default_list; trav; trav = trav->next) {
|
|
sar = trav->sar;
|
|
if (sar->host_port == 0 || sar->host_port == port) {
|
|
/* match! */
|
|
return trav;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void dump_vhost_config(ap_file_t *f)
|
|
{
|
|
int i;
|
|
ipaddr_chain *ic;
|
|
name_chain *nc;
|
|
char buf[MAX_STRING_LEN];
|
|
|
|
ap_fprintf(f, "VirtualHost configuration:\n");
|
|
for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
|
|
for (ic = iphash_table[i]; ic; ic = ic->next) {
|
|
if (ic->sar->host_port == 0) {
|
|
ap_snprintf(buf, sizeof(buf), "%pA:*", &ic->sar->host_addr);
|
|
}
|
|
else {
|
|
ap_snprintf(buf, sizeof(buf), "%pA:%u", &ic->sar->host_addr,
|
|
ic->sar->host_port);
|
|
}
|
|
if (ic->names == NULL) {
|
|
ap_fprintf(f, "%-22s %s (%s:%u)\n", buf,
|
|
ic->server->server_hostname, ic->server->defn_name,
|
|
ic->server->defn_line_number);
|
|
continue;
|
|
}
|
|
ap_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);
|
|
for (nc = ic->names; nc; nc = nc->next) {
|
|
if (nc->sar->host_port) {
|
|
ap_fprintf(f, "%22s port %u ", "", nc->sar->host_port);
|
|
}
|
|
else {
|
|
ap_fprintf(f, "%22s port * ", "");
|
|
}
|
|
ap_fprintf(f, "namevhost %s (%s:%u)\n",
|
|
nc->server->server_hostname,
|
|
nc->server->defn_name,
|
|
nc->server->defn_line_number);
|
|
}
|
|
}
|
|
}
|
|
if (default_list) {
|
|
ap_fprintf(f, "_default_ servers:\n");
|
|
for (ic = default_list; ic; ic = ic->next) {
|
|
if (ic->sar->host_port == 0) {
|
|
ap_fprintf(f, "port * ");
|
|
}
|
|
else {
|
|
ap_fprintf(f, "port %u ", ic->sar->host_port);
|
|
}
|
|
ap_fprintf(f, "server %s (%s:%u)\n",
|
|
ic->server->server_hostname, ic->server->defn_name,
|
|
ic->server->defn_line_number);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* compile the tables and such we need to do the run-time vhost lookups */
|
|
void ap_fini_vhost_config(ap_pool_t *p, server_rec *main_s)
|
|
{
|
|
server_addr_rec *sar;
|
|
int has_default_vhost_addr;
|
|
server_rec *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);
|
|
}
|
|
|
|
/* initialize the tails */
|
|
for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
|
|
iphash_table_tail[i] = &iphash_table[i];
|
|
}
|
|
|
|
/* The first things to go into the hash ap_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 *new = new_ipaddr_chain(p, NULL, sar);
|
|
|
|
*iphash_table_tail[bucket] = new;
|
|
iphash_table_tail[bucket] = &new->next;
|
|
|
|
/* Notice that what we've done is insert an ipaddr_chain with
|
|
* both server and names NULL. Remember that.
|
|
*/
|
|
}
|
|
|
|
/* The next things to go into the hash ap_table_t 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) {
|
|
/* add it to default bucket for each appropriate sar
|
|
* since we need to do a port test
|
|
*/
|
|
ipaddr_chain *other;
|
|
|
|
other = find_default_server(sar->host_port);
|
|
if (other && other->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);
|
|
}
|
|
has_default_vhost_addr = 1;
|
|
ic = new_ipaddr_chain(p, s, sar);
|
|
ic->next = default_list;
|
|
default_list = ic;
|
|
}
|
|
else {
|
|
/* see if it matches something we've already got */
|
|
ic = find_ipaddr(&sar->host_addr, sar->host_port);
|
|
|
|
/* the first time we encounter a NameVirtualHost address
|
|
* ic->server will be NULL, on subsequent encounters
|
|
* ic->names will be non-NULL.
|
|
*/
|
|
if (ic && (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);
|
|
}
|
|
}
|
|
else if (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;
|
|
}
|
|
else {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 =
|
|
ap_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 = ap_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 =
|
|
ap_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) {
|
|
ipaddr_chain **pic = &iphash_table[i];
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef IPHASH_STATISTICS
|
|
dump_iphash_statistics(main_s);
|
|
#endif
|
|
if (getenv("DUMP_VHOSTS")) {
|
|
ap_file_t *thefile = NULL;
|
|
ap_open_stderr(&thefile, p);
|
|
dump_vhost_config(thefile);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* run-time vhost matching functions
|
|
*/
|
|
|
|
/* Lowercase and remove any trailing dot and/or :port from the hostname,
|
|
* and check that it is sane.
|
|
*/
|
|
static void fix_hostname(request_rec *r)
|
|
{
|
|
char *host = ap_palloc(r->pool, strlen(r->hostname) + 1);
|
|
const char *src;
|
|
char *dst;
|
|
|
|
/* check and copy the host part */
|
|
src = r->hostname;
|
|
dst = host;
|
|
while (*src) {
|
|
if (!ap_isalnum(*src) && *src != '.' && *src != '-') {
|
|
if (*src == ':')
|
|
break;
|
|
else
|
|
goto bad;
|
|
} else {
|
|
*dst++ = *src++;
|
|
}
|
|
}
|
|
/* check the port part */
|
|
if (*src++ == ':') {
|
|
while (*src) {
|
|
if (!ap_isdigit(*src++)) {
|
|
goto bad;
|
|
}
|
|
}
|
|
}
|
|
/* strip trailing gubbins */
|
|
if (dst > host && dst[-1] == '.') {
|
|
dst[-1] = '\0';
|
|
} else {
|
|
dst[0] = '\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");
|
|
return;
|
|
}
|
|
|
|
|
|
/* return 1 if host matches ServerName or ServerAliases */
|
|
static int matches_aliases(server_rec *s, const char *host)
|
|
{
|
|
int i;
|
|
ap_array_header_t *names;
|
|
|
|
/* match ServerName */
|
|
if (!strcasecmp(host, s->server_hostname)) {
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Suppose a request came in on the same socket as this r, and included
|
|
* a header "Host: host:port", would it map to r->server? It's more
|
|
* than just that though. When we do the normal matches for each request
|
|
* we don't even bother considering Host: etc on non-namevirtualhosts,
|
|
* we just call it a match. But here we require the host:port to match
|
|
* the ServerName and/or ServerAliases.
|
|
*/
|
|
API_EXPORT(int) ap_matches_request_vhost(request_rec *r, const char *host,
|
|
unsigned port)
|
|
{
|
|
server_rec *s;
|
|
server_addr_rec *sar;
|
|
|
|
s = r->server;
|
|
|
|
/* search all the <VirtualHost> values */
|
|
/* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing
|
|
* consider:
|
|
*
|
|
* NameVirtualHost 10.1.1.1
|
|
* <VirtualHost 10.1.1.1>
|
|
* ServerName v1
|
|
* </VirtualHost>
|
|
* <VirtualHost 10.1.1.1>
|
|
* ServerName v2
|
|
* </VirtualHost>
|
|
*
|
|
* Suppose r->server is v2, and we're asked to match "10.1.1.1". We'll say
|
|
* "yup it's v2", when really it isn't... if a request came in for 10.1.1.1
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
/* the Port has to match now, because the rest don't have ports associated
|
|
* with them. */
|
|
if (port != s->port) {
|
|
return 0;
|
|
}
|
|
|
|
return matches_aliases(s, host);
|
|
}
|
|
|
|
|
|
static void check_hostalias(request_rec *r)
|
|
{
|
|
/*
|
|
* Even if the request has a Host: header containing a port we ignore
|
|
* that port. We always use the physical port of the socket. There
|
|
* are a few reasons for this:
|
|
*
|
|
* - the default of 80 or 443 for SSL is easier to handle this way
|
|
* - there is less of a possibility of a security problem
|
|
* - it simplifies the data structure
|
|
* - the client may have no idea that a proxy somewhere along the way
|
|
* translated the request to another ip:port
|
|
* - except for the addresses from the VirtualHost line, none of the other
|
|
* names we'll match have ports associated with them
|
|
*/
|
|
const char *host = r->hostname;
|
|
unsigned port = ntohs(r->connection->local_addr.sin_port);
|
|
server_rec *s;
|
|
server_rec *last_s;
|
|
name_chain *src;
|
|
|
|
last_s = NULL;
|
|
|
|
/* 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
|
|
* once in the chain -- specifically, it will appear once for each
|
|
* address from its VirtualHost line which matched. We only want to
|
|
* do the full ServerName/ServerAlias comparisons once for each
|
|
* server, fortunately we know that all the VirtualHost addresses for
|
|
* a single server are adjacent to each other.
|
|
*/
|
|
|
|
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;
|
|
}
|
|
|
|
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 (matches_aliases(s, host)) {
|
|
goto found;
|
|
}
|
|
}
|
|
return;
|
|
|
|
found:
|
|
/* s is the first matching server, we're done */
|
|
r->server = s;
|
|
}
|
|
|
|
|
|
static void check_serverpath(request_rec *r)
|
|
{
|
|
server_rec *s;
|
|
server_rec *last_s;
|
|
name_chain *src;
|
|
/* use AP func here. */
|
|
unsigned port = ntohs(r->connection->local_addr.sin_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.
|
|
*
|
|
* See the comment in check_hostalias about how each vhost can be
|
|
* listed multiple times.
|
|
*/
|
|
|
|
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;
|
|
}
|
|
|
|
s = src->server;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ap_update_vhost_from_headers(request_rec *r)
|
|
{
|
|
/* must set this for HTTP/1.1 support */
|
|
if (r->hostname || (r->hostname = ap_table_get(r->headers_in, "Host"))) {
|
|
fix_hostname(r);
|
|
if (r->status != HTTP_OK)
|
|
return;
|
|
}
|
|
/* check if we tucked away a name_chain */
|
|
if (r->connection->vhost_lookup_data) {
|
|
if (r->hostname)
|
|
check_hostalias(r);
|
|
else
|
|
check_serverpath(r);
|
|
}
|
|
}
|
|
|
|
|
|
/* 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)
|
|
{
|
|
ipaddr_chain *trav;
|
|
unsigned port = ntohs(conn->local_addr.sin_port);
|
|
|
|
/* scan the hash ap_table_t for an exact match first */
|
|
trav = find_ipaddr(&conn->local_addr.sin_addr, port);
|
|
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;
|
|
}
|
|
|
|
/* There's certainly no name-vhosts with this address, they would have
|
|
* been matched above.
|
|
*/
|
|
conn->vhost_lookup_data = NULL;
|
|
|
|
/* maybe there's a default server matching this port */
|
|
trav = find_default_server(port);
|
|
if (trav) {
|
|
conn->base_server = trav->server;
|
|
}
|
|
|
|
/* otherwise we're stuck with just the main server */
|
|
}
|