|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* @file vhost.c
|
|
Packit |
90a5c9 |
* @brief functions pertaining to virtual host addresses
|
|
Packit |
90a5c9 |
* (configuration and run-time)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr.h"
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_lib.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "ap_config.h"
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_log.h"
|
|
Packit |
90a5c9 |
#include "http_vhost.h"
|
|
Packit |
90a5c9 |
#include "http_protocol.h"
|
|
Packit |
90a5c9 |
#include "http_core.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAVE_ARPA_INET_H
|
|
Packit |
90a5c9 |
#include <arpa/inet.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* we know core's module_index is 0 */
|
|
Packit |
90a5c9 |
#undef APLOG_MODULE_INDEX
|
|
Packit |
90a5c9 |
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* After all the definitions there's an explanation of how it's all put
|
|
Packit |
90a5c9 |
* together.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* meta-list of name-vhosts. Each server_rec can be in possibly multiple
|
|
Packit |
90a5c9 |
* lists of name-vhosts.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
typedef struct name_chain name_chain;
|
|
Packit |
90a5c9 |
struct name_chain {
|
|
Packit |
90a5c9 |
name_chain *next;
|
|
Packit |
90a5c9 |
server_addr_rec *sar; /* the record causing it to be in
|
|
Packit |
90a5c9 |
* this chain (needed for port comparisons) */
|
|
Packit |
90a5c9 |
server_rec *server; /* the server to use on a match */
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* meta-list of ip addresses. Each server_rec can be in possibly multiple
|
|
Packit |
90a5c9 |
* hash chains since it can have multiple ips.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
typedef struct ipaddr_chain ipaddr_chain;
|
|
Packit |
90a5c9 |
struct ipaddr_chain {
|
|
Packit |
90a5c9 |
ipaddr_chain *next;
|
|
Packit |
90a5c9 |
server_addr_rec *sar; /* the record causing it to be in
|
|
Packit |
90a5c9 |
* this chain (need for both ip addr and port
|
|
Packit |
90a5c9 |
* comparisons) */
|
|
Packit |
90a5c9 |
server_rec *server; /* the server to use if this matches */
|
|
Packit |
90a5c9 |
name_chain *names; /* if non-NULL then a list of name-vhosts
|
|
Packit |
90a5c9 |
* sharing this address */
|
|
Packit |
90a5c9 |
name_chain *initialnames; /* no runtime use, temporary storage of first
|
|
Packit |
90a5c9 |
* NVH'es names */
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* This defines the size of the hash table used for hashing ip addresses
|
|
Packit |
90a5c9 |
* of virtual hosts. It must be a power of two.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#ifndef IPHASH_TABLE_SIZE
|
|
Packit |
90a5c9 |
#define IPHASH_TABLE_SIZE 256
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* A (n) bucket hash table, each entry has a pointer to a server rec and
|
|
Packit |
90a5c9 |
* a pointer to the other entries in that bucket. Each individual address,
|
|
Packit |
90a5c9 |
* even for virtualhosts with multiple addresses, has an entry in this hash
|
|
Packit |
90a5c9 |
* table. There are extra buckets for _default_, and name-vhost entries.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Note that after config time this is constant, so it is thread-safe.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* dump out statistics about the hash function */
|
|
Packit |
90a5c9 |
/* #define IPHASH_STATISTICS */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* list of the _default_ servers */
|
|
Packit |
90a5c9 |
static ipaddr_chain *default_list;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* whether a config error was seen */
|
|
Packit |
90a5c9 |
static int config_error = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* config check function */
|
|
Packit |
90a5c9 |
static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp, server_rec *s);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* How it's used:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* The ip address determines which chain in iphash_table is interesting, then
|
|
Packit |
90a5c9 |
* a comparison is done down that chain to find the first ipaddr_chain whose
|
|
Packit |
90a5c9 |
* sar matches the address:port pair.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If that ipaddr_chain has names == NULL then you're done, it's an ip-vhost.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Otherwise it's a name-vhost list, and the default is the server in the
|
|
Packit |
90a5c9 |
* ipaddr_chain record. We tuck away the ipaddr_chain record in the
|
|
Packit |
90a5c9 |
* conn_rec field vhost_lookup_data. Later on after the headers we get a
|
|
Packit |
90a5c9 |
* second chance, and we use the name_chain to figure out what name-vhost
|
|
Packit |
90a5c9 |
* matches the headers.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If there was no ip address match in the iphash_table then do a lookup
|
|
Packit |
90a5c9 |
* in the default_list.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* How it's put together ... well you should be able to figure that out
|
|
Packit |
90a5c9 |
* from how it's used. Or something like that.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* called at the beginning of the config */
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_init_vhost_config(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
memset(iphash_table, 0, sizeof(iphash_table));
|
|
Packit |
90a5c9 |
default_list = NULL;
|
|
Packit |
90a5c9 |
ap_hook_check_config(vhost_check_config, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Parses a host of the form <address>[:port]
|
|
Packit |
90a5c9 |
* paddr is used to create a list in the order of input
|
|
Packit |
90a5c9 |
* **paddr is the ->next pointer of the last entry (or s->addrs)
|
|
Packit |
90a5c9 |
* *paddr is the variable used to keep track of **paddr between calls
|
|
Packit |
90a5c9 |
* port is the default port to assume
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static const char *get_addresses(apr_pool_t *p, const char *w_,
|
|
Packit |
90a5c9 |
server_addr_rec ***paddr,
|
|
Packit |
90a5c9 |
apr_port_t default_port)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_sockaddr_t *my_addr;
|
|
Packit |
90a5c9 |
server_addr_rec *sar;
|
|
Packit |
90a5c9 |
char *w, *host, *scope_id;
|
|
Packit |
90a5c9 |
int wild_port;
|
|
Packit |
90a5c9 |
apr_size_t wlen;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (*w_ == '\0')
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
wlen = strlen(w_); /* wlen must be > 0 at this point */
|
|
Packit |
90a5c9 |
w = apr_pstrmemdup(p, w_, wlen);
|
|
Packit |
90a5c9 |
/* apr_parse_addr_port() doesn't understand ":*" so handle that first. */
|
|
Packit |
90a5c9 |
wild_port = 0;
|
|
Packit |
90a5c9 |
if (w[wlen - 1] == '*') {
|
|
Packit |
90a5c9 |
if (wlen < 2) {
|
|
Packit |
90a5c9 |
wild_port = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (w[wlen - 2] == ':') {
|
|
Packit |
90a5c9 |
w[wlen - 2] = '\0';
|
|
Packit |
90a5c9 |
wild_port = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = apr_parse_addr_port(&host, &scope_id, &port, w, p);
|
|
Packit |
90a5c9 |
/* If the string is "80", apr_parse_addr_port() will be happy and set
|
|
Packit |
90a5c9 |
* host to NULL and port to 80, so watch out for that.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return "The address or port is invalid";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!host) {
|
|
Packit |
90a5c9 |
return "Missing address for VirtualHost";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (scope_id) {
|
|
Packit |
90a5c9 |
return "Scope ids are not supported";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!port && !wild_port) {
|
|
Packit |
90a5c9 |
port = default_port;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strcmp(host, "*") == 0 || strcasecmp(host, "_default_") == 0) {
|
|
Packit |
90a5c9 |
rv = apr_sockaddr_info_get(&my_addr, NULL, APR_UNSPEC, port, 0, p);
|
|
Packit |
90a5c9 |
if (rv) {
|
|
Packit |
90a5c9 |
return "Could not determine a wildcard address ('0.0.0.0') -- "
|
|
Packit |
90a5c9 |
"check resolver configuration.";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = apr_sockaddr_info_get(&my_addr, host, APR_UNSPEC, port, 0, p);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00547)
|
|
Packit |
90a5c9 |
"Could not resolve host name %s -- ignoring!", host);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Remember all addresses for the host */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
sar = apr_pcalloc(p, sizeof(server_addr_rec));
|
|
Packit |
90a5c9 |
**paddr = sar;
|
|
Packit |
90a5c9 |
*paddr = &sar->next;
|
|
Packit |
90a5c9 |
sar->host_addr = my_addr;
|
|
Packit |
90a5c9 |
sar->host_port = port;
|
|
Packit |
90a5c9 |
sar->virthost = host;
|
|
Packit |
90a5c9 |
my_addr = my_addr->next;
|
|
Packit |
90a5c9 |
} while (my_addr);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* parse the <VirtualHost> addresses */
|
|
Packit |
90a5c9 |
const char *ap_parse_vhost_addrs(apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *hostname,
|
|
Packit |
90a5c9 |
server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
server_addr_rec **addrs;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* start the list of addresses */
|
|
Packit |
90a5c9 |
addrs = &s->addrs;
|
|
Packit |
90a5c9 |
while (hostname[0]) {
|
|
Packit |
90a5c9 |
err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
*addrs = NULL;
|
|
Packit |
90a5c9 |
return err;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* terminate the list */
|
|
Packit |
90a5c9 |
*addrs = NULL;
|
|
Packit |
90a5c9 |
if (s->addrs) {
|
|
Packit |
90a5c9 |
if (s->addrs->host_port) {
|
|
Packit |
90a5c9 |
/* override the default port which is inherited from main_server */
|
|
Packit |
90a5c9 |
s->port = s->addrs->host_port;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_NONSTD(const char *)ap_set_name_virtual_host(cmd_parms *cmd,
|
|
Packit |
90a5c9 |
void *dummy,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
static int warnonce = 0;
|
|
Packit |
90a5c9 |
if (++warnonce == 1) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_NOTICE|APLOG_STARTUP, APR_SUCCESS, NULL, APLOGNO(00548)
|
|
Packit |
90a5c9 |
"NameVirtualHost has no effect and will be removed in the "
|
|
Packit |
90a5c9 |
"next release %s:%d",
|
|
Packit |
90a5c9 |
cmd->directive->filename,
|
|
Packit |
90a5c9 |
cmd->directive->line_num);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* hash table statistics, keep this in here for the beta period so
|
|
Packit |
90a5c9 |
* we can find out if the hash function is ok
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#ifdef IPHASH_STATISTICS
|
|
Packit |
90a5c9 |
static int iphash_compare(const void *a, const void *b)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return (*(const int *) b - *(const int *) a);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void dump_iphash_statistics(server_rec *main_s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
unsigned count[IPHASH_TABLE_SIZE];
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
ipaddr_chain *src;
|
|
Packit |
90a5c9 |
unsigned total;
|
|
Packit |
90a5c9 |
char buf[HUGE_STRING_LEN];
|
|
Packit |
90a5c9 |
char *p;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
total = 0;
|
|
Packit |
90a5c9 |
for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
|
|
Packit |
90a5c9 |
count[i] = 0;
|
|
Packit |
90a5c9 |
for (src = iphash_table[i]; src; src = src->next) {
|
|
Packit |
90a5c9 |
++count[i];
|
|
Packit |
90a5c9 |
if (i < IPHASH_TABLE_SIZE) {
|
|
Packit |
90a5c9 |
/* don't count the slop buckets in the total */
|
|
Packit |
90a5c9 |
++total;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare);
|
|
Packit |
90a5c9 |
p = buf + apr_snprintf(buf, sizeof(buf),
|
|
Packit |
90a5c9 |
APLOGNO(03235) "iphash: total hashed = %u, avg chain = %u, "
|
|
Packit |
90a5c9 |
"chain lengths (count x len):",
|
|
Packit |
90a5c9 |
total, total / IPHASH_TABLE_SIZE);
|
|
Packit |
90a5c9 |
total = 1;
|
|
Packit |
90a5c9 |
for (i = 1; i < IPHASH_TABLE_SIZE; ++i) {
|
|
Packit |
90a5c9 |
if (count[i - 1] != count[i]) {
|
|
Packit |
90a5c9 |
p += apr_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
|
|
Packit |
90a5c9 |
total, count[i - 1]);
|
|
Packit |
90a5c9 |
total = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
++total;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
p += apr_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
|
|
Packit |
90a5c9 |
total, count[IPHASH_TABLE_SIZE - 1]);
|
|
Packit |
90a5c9 |
/* Intentional no APLOGNO */
|
|
Packit |
90a5c9 |
/* buf provides APLOGNO */
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, main_s, buf);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* This hashing function is designed to get good distribution in the cases
|
|
Packit |
90a5c9 |
* where the server is handling entire "networks" of servers. i.e. a
|
|
Packit |
90a5c9 |
* whack of /24s. This is probably the most common configuration for
|
|
Packit |
90a5c9 |
* ISPs with large virtual servers.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* NOTE: This function is symmetric (i.e. collapses all 4 octets
|
|
Packit |
90a5c9 |
* into one), so machine byte order (big/little endianness) does not matter.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Hash function provided by David Hankins.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static APR_INLINE unsigned hash_inaddr(unsigned key)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
key ^= (key >> 16);
|
|
Packit |
90a5c9 |
return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static APR_INLINE unsigned hash_addr(struct apr_sockaddr_t *sa)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
unsigned key;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The key is the last four bytes of the IP address.
|
|
Packit |
90a5c9 |
* For IPv4, this is the entire address, as always.
|
|
Packit |
90a5c9 |
* For IPv6, this is usually part of the MAC address.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
key = *(unsigned *)((char *)sa->ipaddr_ptr + sa->ipaddr_len - 4);
|
|
Packit |
90a5c9 |
return hash_inaddr(key);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static ipaddr_chain *new_ipaddr_chain(apr_pool_t *p,
|
|
Packit |
90a5c9 |
server_rec *s, server_addr_rec *sar)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ipaddr_chain *new;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
new = apr_palloc(p, sizeof(*new));
|
|
Packit |
90a5c9 |
new->names = NULL;
|
|
Packit |
90a5c9 |
new->initialnames = NULL;
|
|
Packit |
90a5c9 |
new->server = s;
|
|
Packit |
90a5c9 |
new->sar = sar;
|
|
Packit |
90a5c9 |
new->next = NULL;
|
|
Packit |
90a5c9 |
return new;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static name_chain *new_name_chain(apr_pool_t *p,
|
|
Packit |
90a5c9 |
server_rec *s, server_addr_rec *sar)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
name_chain *new;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
new = apr_palloc(p, sizeof(*new));
|
|
Packit |
90a5c9 |
new->server = s;
|
|
Packit |
90a5c9 |
new->sar = sar;
|
|
Packit |
90a5c9 |
new->next = NULL;
|
|
Packit |
90a5c9 |
return new;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static APR_INLINE ipaddr_chain *find_ipaddr(apr_sockaddr_t *sa)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
unsigned bucket;
|
|
Packit |
90a5c9 |
ipaddr_chain *trav = NULL;
|
|
Packit |
90a5c9 |
ipaddr_chain *wild_match = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* scan the hash table for an exact match first */
|
|
Packit |
90a5c9 |
bucket = hash_addr(sa);
|
|
Packit |
90a5c9 |
for (trav = iphash_table[bucket]; trav; trav = trav->next) {
|
|
Packit |
90a5c9 |
server_addr_rec *sar = trav->sar;
|
|
Packit |
90a5c9 |
apr_sockaddr_t *cur = sar->host_addr;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (cur->port == sa->port) {
|
|
Packit |
90a5c9 |
if (apr_sockaddr_equal(cur, sa)) {
|
|
Packit |
90a5c9 |
return trav;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (wild_match == NULL && (cur->port == 0 || sa->port == 0)) {
|
|
Packit |
90a5c9 |
if (apr_sockaddr_equal(cur, sa)) {
|
|
Packit |
90a5c9 |
/* don't break, continue looking for an exact match */
|
|
Packit |
90a5c9 |
wild_match = trav;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return wild_match;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static ipaddr_chain *find_default_server(apr_port_t port)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
server_addr_rec *sar;
|
|
Packit |
90a5c9 |
ipaddr_chain *trav = NULL;
|
|
Packit |
90a5c9 |
ipaddr_chain *wild_match = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (trav = default_list; trav; trav = trav->next) {
|
|
Packit |
90a5c9 |
sar = trav->sar;
|
|
Packit |
90a5c9 |
if (sar->host_port == port) {
|
|
Packit |
90a5c9 |
/* match! */
|
|
Packit |
90a5c9 |
return trav;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (wild_match == NULL && sar->host_port == 0) {
|
|
Packit |
90a5c9 |
/* don't break, continue looking for an exact match */
|
|
Packit |
90a5c9 |
wild_match = trav;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return wild_match;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAVE_IPV6
|
|
Packit |
90a5c9 |
#define IS_IN6_ANYADDR(ad) ((ad)->family == APR_INET6 \
|
|
Packit |
90a5c9 |
&& IN6_IS_ADDR_UNSPECIFIED(&(ad)->sa.sin6.sin6_addr))
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
#define IS_IN6_ANYADDR(ad) (0)
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void dump_a_vhost(apr_file_t *f, ipaddr_chain *ic)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
name_chain *nc;
|
|
Packit |
90a5c9 |
int len;
|
|
Packit |
90a5c9 |
char buf[MAX_STRING_LEN];
|
|
Packit |
90a5c9 |
apr_sockaddr_t *ha = ic->sar->host_addr;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((ha->family == APR_INET && ha->sa.sin.sin_addr.s_addr == INADDR_ANY)
|
|
Packit |
90a5c9 |
|| IS_IN6_ANYADDR(ha)) {
|
|
Packit |
90a5c9 |
len = apr_snprintf(buf, sizeof(buf), "*:%u",
|
|
Packit |
90a5c9 |
ic->sar->host_port);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
len = apr_snprintf(buf, sizeof(buf), "%pI", ha);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (ic->sar->host_port == 0) {
|
|
Packit |
90a5c9 |
buf[len-1] = '*';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (ic->names == NULL) {
|
|
Packit |
90a5c9 |
apr_file_printf(f, "%-22s %s (%s:%u)\n", buf,
|
|
Packit |
90a5c9 |
ic->server->server_hostname,
|
|
Packit |
90a5c9 |
ic->server->defn_name, ic->server->defn_line_number);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_file_printf(f, "%-22s is a NameVirtualHost\n"
|
|
Packit |
90a5c9 |
"%8s default server %s (%s:%u)\n",
|
|
Packit |
90a5c9 |
buf, "", ic->server->server_hostname,
|
|
Packit |
90a5c9 |
ic->server->defn_name, ic->server->defn_line_number);
|
|
Packit |
90a5c9 |
for (nc = ic->names; nc; nc = nc->next) {
|
|
Packit |
90a5c9 |
if (nc->sar->host_port) {
|
|
Packit |
90a5c9 |
apr_file_printf(f, "%8s port %u ", "", nc->sar->host_port);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_file_printf(f, "%8s port * ", "");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_file_printf(f, "namevhost %s (%s:%u)\n",
|
|
Packit |
90a5c9 |
nc->server->server_hostname,
|
|
Packit |
90a5c9 |
nc->server->defn_name, nc->server->defn_line_number);
|
|
Packit |
90a5c9 |
if (nc->server->names) {
|
|
Packit |
90a5c9 |
apr_array_header_t *names = nc->server->names;
|
|
Packit |
90a5c9 |
char **name = (char **)names->elts;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
for (i = 0; i < names->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (name[i]) {
|
|
Packit |
90a5c9 |
apr_file_printf(f, "%16s alias %s\n", "", name[i]);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (nc->server->wild_names) {
|
|
Packit |
90a5c9 |
apr_array_header_t *names = nc->server->wild_names;
|
|
Packit |
90a5c9 |
char **name = (char **)names->elts;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
for (i = 0; i < names->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (name[i]) {
|
|
Packit |
90a5c9 |
apr_file_printf(f, "%16s wild alias %s\n", "", name[i]);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void dump_vhost_config(apr_file_t *f)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ipaddr_chain *ic;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_file_printf(f, "VirtualHost configuration:\n");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* non-wildcard servers */
|
|
Packit |
90a5c9 |
for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
|
|
Packit |
90a5c9 |
for (ic = iphash_table[i]; ic; ic = ic->next) {
|
|
Packit |
90a5c9 |
dump_a_vhost(f, ic);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* wildcard servers */
|
|
Packit |
90a5c9 |
for (ic = default_list; ic; ic = ic->next) {
|
|
Packit |
90a5c9 |
dump_a_vhost(f, ic);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* When a second or later virtual host maps to the same IP chain,
|
|
Packit |
90a5c9 |
* add the relevant server names to the chain. Special care is taken
|
|
Packit |
90a5c9 |
* to avoid adding ic->names until we're sure there are multiple VH'es.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static void add_name_vhost_config(apr_pool_t *p, server_rec *main_s,
|
|
Packit |
90a5c9 |
server_rec *s, server_addr_rec *sar,
|
|
Packit |
90a5c9 |
ipaddr_chain *ic)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
name_chain *nc = new_name_chain(p, s, sar);
|
|
Packit |
90a5c9 |
nc->next = ic->names;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* iterating backwards, so each one we see becomes the current default server */
|
|
Packit |
90a5c9 |
ic->server = s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ic->names == NULL) {
|
|
Packit |
90a5c9 |
if (ic->initialnames == NULL) {
|
|
Packit |
90a5c9 |
/* first pass, set these names aside in case we see another VH.
|
|
Packit |
90a5c9 |
* Until then, this looks like an IP-based VH to runtime.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ic->initialnames = nc;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* second pass through this chain -- this really is an NVH, and we
|
|
Packit |
90a5c9 |
* have two sets of names to link in.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
nc->next = ic->initialnames;
|
|
Packit |
90a5c9 |
ic->names = nc;
|
|
Packit |
90a5c9 |
ic->initialnames = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* 3rd or more -- just keep stacking the names */
|
|
Packit |
90a5c9 |
ic->names = nc;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* compile the tables and such we need to do the run-time vhost lookups */
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
server_addr_rec *sar;
|
|
Packit |
90a5c9 |
int has_default_vhost_addr;
|
|
Packit |
90a5c9 |
server_rec *s;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Main host first */
|
|
Packit |
90a5c9 |
s = main_s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!s->server_hostname) {
|
|
Packit |
90a5c9 |
s->server_hostname = ap_get_local_host(p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* initialize the tails */
|
|
Packit |
90a5c9 |
for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
|
|
Packit |
90a5c9 |
iphash_table_tail[i] = &iphash_table[i];
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The next things to go into the hash table are the virtual hosts
|
|
Packit |
90a5c9 |
* themselves. They're listed off of main_s->next in the reverse
|
|
Packit |
90a5c9 |
* order they occurred in the config file, so we insert them at
|
|
Packit |
90a5c9 |
* the iphash_table_tail but don't advance the tail.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (s = main_s->next; s; s = s->next) {
|
|
Packit |
90a5c9 |
server_addr_rec *sar_prev = NULL;
|
|
Packit |
90a5c9 |
has_default_vhost_addr = 0;
|
|
Packit |
90a5c9 |
for (sar = s->addrs; sar; sar = sar->next) {
|
|
Packit |
90a5c9 |
ipaddr_chain *ic;
|
|
Packit |
90a5c9 |
char inaddr_any[16] = {0}; /* big enough to handle IPv4 or IPv6 */
|
|
Packit |
90a5c9 |
/* XXX: this treats 0.0.0.0 as a "default" server which matches no-exact-match for IPv6 */
|
|
Packit |
90a5c9 |
if (!memcmp(sar->host_addr->ipaddr_ptr, inaddr_any, sar->host_addr->ipaddr_len)) {
|
|
Packit |
90a5c9 |
ic = find_default_server(sar->host_port);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ic && sar->host_port == ic->sar->host_port) { /* we're a match for an existing "default server" */
|
|
Packit |
90a5c9 |
if (!sar_prev || memcmp(sar_prev->host_addr->ipaddr_ptr, inaddr_any, sar_prev->host_addr->ipaddr_len)
|
|
Packit |
90a5c9 |
|| sar_prev->host_port != sar->host_port) {
|
|
Packit |
90a5c9 |
add_name_vhost_config(p, main_s, s, sar, ic);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* No default server, or we found a default server but
|
|
Packit |
90a5c9 |
** exactly one of us is a wildcard port, which means we want
|
|
Packit |
90a5c9 |
** two ip-based vhosts not an NVH with two names
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ic = new_ipaddr_chain(p, s, sar);
|
|
Packit |
90a5c9 |
ic->next = default_list;
|
|
Packit |
90a5c9 |
default_list = ic;
|
|
Packit |
90a5c9 |
add_name_vhost_config(p, main_s, s, sar, ic);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
has_default_vhost_addr = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* see if it matches something we've already got */
|
|
Packit |
90a5c9 |
ic = find_ipaddr(sar->host_addr);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ic || sar->host_port != ic->sar->host_port) {
|
|
Packit |
90a5c9 |
/* No matching server, or we found a matching server but
|
|
Packit |
90a5c9 |
** exactly one of us is a wildcard port, which means we want
|
|
Packit |
90a5c9 |
** two ip-based vhosts not an NVH with two names
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
unsigned bucket = hash_addr(sar->host_addr);
|
|
Packit |
90a5c9 |
ic = new_ipaddr_chain(p, s, sar);
|
|
Packit |
90a5c9 |
ic->next = *iphash_table_tail[bucket];
|
|
Packit |
90a5c9 |
*iphash_table_tail[bucket] = ic;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
add_name_vhost_config(p, main_s, s, sar, ic);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
sar_prev = sar;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Ok now we want to set up a server_hostname if the user was
|
|
Packit |
90a5c9 |
* silly enough to forget one.
|
|
Packit |
90a5c9 |
* XXX: This is silly we should just crash and burn.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (!s->server_hostname) {
|
|
Packit |
90a5c9 |
if (has_default_vhost_addr) {
|
|
Packit |
90a5c9 |
s->server_hostname = main_s->server_hostname;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!s->addrs) {
|
|
Packit |
90a5c9 |
/* what else can we do? at this point this vhost has
|
|
Packit |
90a5c9 |
no configured name, probably because they used
|
|
Packit |
90a5c9 |
DNS in the VirtualHost statement. It's disabled
|
|
Packit |
90a5c9 |
anyhow by the host matching code. -djg */
|
|
Packit |
90a5c9 |
s->server_hostname =
|
|
Packit |
90a5c9 |
apr_pstrdup(p, "bogus_host_without_forward_dns");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
char *hostname;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_getnameinfo(&hostname, s->addrs->host_addr, 0);
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
s->server_hostname = apr_pstrdup(p, hostname);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* again, what can we do? They didn't specify a
|
|
Packit |
90a5c9 |
ServerName, and their DNS isn't working. -djg */
|
|
Packit |
90a5c9 |
char *ipaddr_str;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_sockaddr_ip_get(&ipaddr_str, s->addrs->host_addr);
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, main_s, APLOGNO(00549)
|
|
Packit |
90a5c9 |
"Failed to resolve server name "
|
|
Packit |
90a5c9 |
"for %s (check DNS) -- or specify an explicit "
|
|
Packit |
90a5c9 |
"ServerName",
|
|
Packit |
90a5c9 |
ipaddr_str);
|
|
Packit |
90a5c9 |
s->server_hostname =
|
|
Packit |
90a5c9 |
apr_pstrdup(p, "bogus_host_without_reverse_dns");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifdef IPHASH_STATISTICS
|
|
Packit |
90a5c9 |
dump_iphash_statistics(main_s);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
if (ap_exists_config_define("DUMP_VHOSTS")) {
|
|
Packit |
90a5c9 |
apr_file_t *thefile = NULL;
|
|
Packit |
90a5c9 |
apr_file_open_stdout(&thefile, p);
|
|
Packit |
90a5c9 |
dump_vhost_config(thefile);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return config_error ? !OK : OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*****************************************************************************
|
|
Packit |
90a5c9 |
* run-time vhost matching functions
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t fix_hostname_v6_literal(request_rec *r, char *host)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *dst;
|
|
Packit |
90a5c9 |
int double_colon = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (dst = host; *dst; dst++) {
|
|
Packit |
90a5c9 |
if (apr_isxdigit(*dst)) {
|
|
Packit |
90a5c9 |
if (apr_isupper(*dst)) {
|
|
Packit |
90a5c9 |
*dst = apr_tolower(*dst);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (*dst == ':') {
|
|
Packit |
90a5c9 |
if (*(dst + 1) == ':') {
|
|
Packit |
90a5c9 |
if (double_colon)
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
double_colon = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (*(dst + 1) == '.') {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (*dst == '.') {
|
|
Packit |
90a5c9 |
/* For IPv4-mapped IPv6 addresses like ::FFFF:129.144.52.38 */
|
|
Packit |
90a5c9 |
if (*(dst + 1) == ':' || *(dst + 1) == '.')
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t fix_hostname_non_v6(request_rec *r, char *host)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *dst;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (dst = host; *dst; dst++) {
|
|
Packit |
90a5c9 |
if (apr_islower(*dst)) {
|
|
Packit |
90a5c9 |
/* leave char unchanged */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (*dst == '.') {
|
|
Packit |
90a5c9 |
if (*(dst + 1) == '.') {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (apr_isupper(*dst)) {
|
|
Packit |
90a5c9 |
*dst = apr_tolower(*dst);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (*dst == '/' || *dst == '\\') {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* strip trailing gubbins */
|
|
Packit |
90a5c9 |
if (dst > host && dst[-1] == '.') {
|
|
Packit |
90a5c9 |
dst[-1] = '\0';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If strict mode ever becomes the default, this should be folded into
|
|
Packit |
90a5c9 |
* fix_hostname_non_v6()
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t strict_hostname_check(request_rec *r, char *host)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *ch;
|
|
Packit |
90a5c9 |
int is_dotted_decimal = 1, leading_zeroes = 0, dots = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (ch = host; *ch; ch++) {
|
|
Packit |
90a5c9 |
if (apr_isalpha(*ch) || *ch == '-' || *ch == '_') {
|
|
Packit |
90a5c9 |
is_dotted_decimal = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (ch[0] == '.') {
|
|
Packit |
90a5c9 |
dots++;
|
|
Packit |
90a5c9 |
if (ch[1] == '0' && apr_isdigit(ch[2]))
|
|
Packit |
90a5c9 |
leading_zeroes = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!apr_isdigit(*ch)) {
|
|
Packit |
90a5c9 |
/* also takes care of multiple Host headers by denying commas */
|
|
Packit |
90a5c9 |
goto bad;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (is_dotted_decimal) {
|
|
Packit |
90a5c9 |
if (host[0] == '.' || (host[0] == '0' && apr_isdigit(host[1])))
|
|
Packit |
90a5c9 |
leading_zeroes = 1;
|
|
Packit |
90a5c9 |
if (leading_zeroes || dots != 3) {
|
|
Packit |
90a5c9 |
/* RFC 3986 7.4 */
|
|
Packit |
90a5c9 |
goto bad;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* The top-level domain must start with a letter (RFC 1123 2.1) */
|
|
Packit |
90a5c9 |
while (ch > host && *ch != '.')
|
|
Packit |
90a5c9 |
ch--;
|
|
Packit |
90a5c9 |
if (ch[0] == '.' && ch[1] != '\0' && !apr_isalpha(ch[1]))
|
|
Packit |
90a5c9 |
goto bad;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
bad:
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02415)
|
|
Packit |
90a5c9 |
"[strict] Invalid host name '%s'%s%.6s",
|
|
Packit |
90a5c9 |
host, *ch ? ", problem near: " : "", ch);
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Lowercase and remove any trailing dot and/or :port from the hostname,
|
|
Packit |
90a5c9 |
* and check that it is sane.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* In most configurations the exact syntax of the hostname isn't
|
|
Packit |
90a5c9 |
* important so strict sanity checking isn't necessary. However, in
|
|
Packit |
90a5c9 |
* mass hosting setups (using mod_vhost_alias or mod_rewrite) where
|
|
Packit |
90a5c9 |
* the hostname is interpolated into the filename, we need to be sure
|
|
Packit |
90a5c9 |
* that the interpolation doesn't expose parts of the filesystem.
|
|
Packit |
90a5c9 |
* We don't do strict RFC 952 / RFC 1123 syntax checking in order
|
|
Packit |
90a5c9 |
* to support iDNS and people who erroneously use underscores.
|
|
Packit |
90a5c9 |
* Instead we just check for filesystem metacharacters: directory
|
|
Packit |
90a5c9 |
* separators / and \ and sequences of more than one dot.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int fix_hostname(request_rec *r, const char *host_header,
|
|
Packit |
90a5c9 |
unsigned http_conformance)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *src;
|
|
Packit |
90a5c9 |
char *host, *scope_id;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
const char *c;
|
|
Packit |
90a5c9 |
int is_v6literal = 0;
|
|
Packit |
90a5c9 |
int strict = (http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
src = host_header ? host_header : r->hostname;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* According to RFC 2616, Host header field CAN be blank */
|
|
Packit |
90a5c9 |
if (!*src) {
|
|
Packit |
90a5c9 |
return is_v6literal;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* apr_parse_addr_port will interpret a bare integer as a port
|
|
Packit |
90a5c9 |
* which is incorrect in this context. So treat it separately.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
for (c = src; apr_isdigit(*c); ++c);
|
|
Packit |
90a5c9 |
if (!*c) {
|
|
Packit |
90a5c9 |
/* pure integer */
|
|
Packit |
90a5c9 |
if (strict) {
|
|
Packit |
90a5c9 |
/* RFC 3986 7.4 */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02416)
|
|
Packit |
90a5c9 |
"[strict] purely numeric host names not allowed: %s",
|
|
Packit |
90a5c9 |
src);
|
|
Packit |
90a5c9 |
goto bad_nolog;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
r->hostname = src;
|
|
Packit |
90a5c9 |
return is_v6literal;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (host_header) {
|
|
Packit |
90a5c9 |
rv = apr_parse_addr_port(&host, &scope_id, &port, src, r->pool);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS || scope_id)
|
|
Packit |
90a5c9 |
goto bad;
|
|
Packit |
90a5c9 |
if (port) {
|
|
Packit |
90a5c9 |
/* Don't throw the Host: header's port number away:
|
|
Packit |
90a5c9 |
save it in parsed_uri -- ap_get_server_port() needs it! */
|
|
Packit |
90a5c9 |
/* @@@ XXX there should be a better way to pass the port.
|
|
Packit |
90a5c9 |
* Like r->hostname, there should be a r->portno
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->parsed_uri.port = port;
|
|
Packit |
90a5c9 |
r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (host_header[0] == '[')
|
|
Packit |
90a5c9 |
is_v6literal = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Already parsed, surrounding [ ] (if IPv6 literal) and :port have
|
|
Packit |
90a5c9 |
* already been removed.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
host = apr_pstrdup(r->pool, r->hostname);
|
|
Packit |
90a5c9 |
if (ap_strchr(host, ':') != NULL)
|
|
Packit |
90a5c9 |
is_v6literal = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (is_v6literal) {
|
|
Packit |
90a5c9 |
rv = fix_hostname_v6_literal(r, host);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = fix_hostname_non_v6(r, host);
|
|
Packit |
90a5c9 |
if (strict && rv == APR_SUCCESS)
|
|
Packit |
90a5c9 |
rv = strict_hostname_check(r, host);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS)
|
|
Packit |
90a5c9 |
goto bad;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->hostname = host;
|
|
Packit |
90a5c9 |
return is_v6literal;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
bad:
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00550)
|
|
Packit |
90a5c9 |
"Client sent malformed Host header: %s",
|
|
Packit |
90a5c9 |
src);
|
|
Packit |
90a5c9 |
bad_nolog:
|
|
Packit |
90a5c9 |
r->status = HTTP_BAD_REQUEST;
|
|
Packit |
90a5c9 |
return is_v6literal;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* return 1 if host matches ServerName or ServerAliases */
|
|
Packit |
90a5c9 |
static int matches_aliases(server_rec *s, const char *host)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
apr_array_header_t *names;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* match ServerName */
|
|
Packit |
90a5c9 |
if (!strcasecmp(host, s->server_hostname)) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* search all the aliases from ServerAlias directive */
|
|
Packit |
90a5c9 |
names = s->names;
|
|
Packit |
90a5c9 |
if (names) {
|
|
Packit |
90a5c9 |
char **name = (char **) names->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < names->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (!name[i]) continue;
|
|
Packit |
90a5c9 |
if (!strcasecmp(host, name[i]))
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
names = s->wild_names;
|
|
Packit |
90a5c9 |
if (names) {
|
|
Packit |
90a5c9 |
char **name = (char **) names->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < names->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (!name[i]) continue;
|
|
Packit |
90a5c9 |
if (!ap_strcasecmp_match(host, name[i]))
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Suppose a request came in on the same socket as this r, and included
|
|
Packit |
90a5c9 |
* a header "Host: host:port", would it map to r->server? It's more
|
|
Packit |
90a5c9 |
* than just that though. When we do the normal matches for each request
|
|
Packit |
90a5c9 |
* we don't even bother considering Host: etc on non-namevirtualhosts,
|
|
Packit |
90a5c9 |
* we just call it a match. But here we require the host:port to match
|
|
Packit |
90a5c9 |
* the ServerName and/or ServerAliases.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
|
|
Packit |
90a5c9 |
apr_port_t port)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
server_rec *s;
|
|
Packit |
90a5c9 |
server_addr_rec *sar;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
s = r->server;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* search all the <VirtualHost> values */
|
|
Packit |
90a5c9 |
/* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing
|
|
Packit |
90a5c9 |
* consider:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* NameVirtualHost 10.1.1.1
|
|
Packit |
90a5c9 |
* <VirtualHost 10.1.1.1>
|
|
Packit |
90a5c9 |
* ServerName v1
|
|
Packit |
90a5c9 |
* </VirtualHost>
|
|
Packit |
90a5c9 |
* <VirtualHost 10.1.1.1>
|
|
Packit |
90a5c9 |
* ServerName v2
|
|
Packit |
90a5c9 |
* </VirtualHost>
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Suppose r->server is v2, and we're asked to match "10.1.1.1". We'll say
|
|
Packit |
90a5c9 |
* "yup it's v2", when really it isn't... if a request came in for 10.1.1.1
|
|
Packit |
90a5c9 |
* it would really go to v1.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
for (sar = s->addrs; sar; sar = sar->next) {
|
|
Packit |
90a5c9 |
if ((sar->host_port == 0 || port == sar->host_port)
|
|
Packit |
90a5c9 |
&& !strcasecmp(host, sar->virthost)) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* the Port has to match now, because the rest don't have ports associated
|
|
Packit |
90a5c9 |
* with them. */
|
|
Packit |
90a5c9 |
if (port != s->port) {
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return matches_aliases(s, host);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void check_hostalias(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Even if the request has a Host: header containing a port we ignore
|
|
Packit |
90a5c9 |
* that port. We always use the physical port of the socket. There
|
|
Packit |
90a5c9 |
* are a few reasons for this:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* - the default of 80 or 443 for SSL is easier to handle this way
|
|
Packit |
90a5c9 |
* - there is less of a possibility of a security problem
|
|
Packit |
90a5c9 |
* - it simplifies the data structure
|
|
Packit |
90a5c9 |
* - the client may have no idea that a proxy somewhere along the way
|
|
Packit |
90a5c9 |
* translated the request to another ip:port
|
|
Packit |
90a5c9 |
* - except for the addresses from the VirtualHost line, none of the other
|
|
Packit |
90a5c9 |
* names we'll match have ports associated with them
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
const char *host = r->hostname;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
server_rec *s;
|
|
Packit |
90a5c9 |
server_rec *virthost_s;
|
|
Packit |
90a5c9 |
server_rec *last_s;
|
|
Packit |
90a5c9 |
name_chain *src;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
virthost_s = NULL;
|
|
Packit |
90a5c9 |
last_s = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
port = r->connection->local_addr->port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Recall that the name_chain is a list of server_addr_recs, some of
|
|
Packit |
90a5c9 |
* whose ports may not match. Also each server may appear more than
|
|
Packit |
90a5c9 |
* once in the chain -- specifically, it will appear once for each
|
|
Packit |
90a5c9 |
* address from its VirtualHost line which matched. We only want to
|
|
Packit |
90a5c9 |
* do the full ServerName/ServerAlias comparisons once for each
|
|
Packit |
90a5c9 |
* server, fortunately we know that all the VirtualHost addresses for
|
|
Packit |
90a5c9 |
* a single server are adjacent to each other.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (src = r->connection->vhost_lookup_data; src; src = src->next) {
|
|
Packit |
90a5c9 |
server_addr_rec *sar;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We only consider addresses on the name_chain which have a matching
|
|
Packit |
90a5c9 |
* port
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
sar = src->sar;
|
|
Packit |
90a5c9 |
if (sar->host_port != 0 && port != sar->host_port) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
s = src->server;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we still need to do ServerName and ServerAlias checks for this
|
|
Packit |
90a5c9 |
* server, do them now.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (s != last_s) {
|
|
Packit |
90a5c9 |
/* does it match any ServerName or ServerAlias directive? */
|
|
Packit |
90a5c9 |
if (matches_aliases(s, host)) {
|
|
Packit |
90a5c9 |
goto found;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
last_s = s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Fallback: does it match the virthost from the sar? */
|
|
Packit |
90a5c9 |
if (!strcasecmp(host, sar->virthost)) {
|
|
Packit |
90a5c9 |
/* only the first match is used */
|
|
Packit |
90a5c9 |
if (virthost_s == NULL) {
|
|
Packit |
90a5c9 |
virthost_s = s;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If ServerName and ServerAlias check failed, we end up here. If it
|
|
Packit |
90a5c9 |
* matches a VirtualHost, virthost_s is set. Use that as fallback
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (virthost_s) {
|
|
Packit |
90a5c9 |
s = virthost_s;
|
|
Packit |
90a5c9 |
goto found;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
found:
|
|
Packit |
90a5c9 |
/* s is the first matching server, we're done */
|
|
Packit |
90a5c9 |
r->server = s;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void check_serverpath(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
server_rec *s;
|
|
Packit |
90a5c9 |
server_rec *last_s;
|
|
Packit |
90a5c9 |
name_chain *src;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
port = r->connection->local_addr->port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* This is in conjunction with the ServerPath code in http_core, so we
|
|
Packit |
90a5c9 |
* get the right host attached to a non- Host-sending request.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* See the comment in check_hostalias about how each vhost can be
|
|
Packit |
90a5c9 |
* listed multiple times.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
last_s = NULL;
|
|
Packit |
90a5c9 |
for (src = r->connection->vhost_lookup_data; src; src = src->next) {
|
|
Packit |
90a5c9 |
/* We only consider addresses on the name_chain which have a matching
|
|
Packit |
90a5c9 |
* port
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (src->sar->host_port != 0 && port != src->sar->host_port) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
s = src->server;
|
|
Packit |
90a5c9 |
if (s == last_s) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
last_s = s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (s->path && !strncmp(r->uri, s->path, s->pathlen) &&
|
|
Packit |
90a5c9 |
(s->path[s->pathlen - 1] == '/' ||
|
|
Packit |
90a5c9 |
r->uri[s->pathlen] == '/' ||
|
|
Packit |
90a5c9 |
r->uri[s->pathlen] == '\0')) {
|
|
Packit |
90a5c9 |
r->server = s;
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static APR_INLINE const char *construct_host_header(request_rec *r,
|
|
Packit |
90a5c9 |
int is_v6literal)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
struct iovec iov[5];
|
|
Packit |
90a5c9 |
apr_size_t nvec = 0;
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* We cannot use ap_get_server_name/port here, because we must
|
|
Packit |
90a5c9 |
* ignore UseCanonicalName/Port.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (is_v6literal) {
|
|
Packit |
90a5c9 |
iov[nvec].iov_base = "[";
|
|
Packit |
90a5c9 |
iov[nvec].iov_len = 1;
|
|
Packit |
90a5c9 |
nvec++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
iov[nvec].iov_base = (void *)r->hostname;
|
|
Packit |
90a5c9 |
iov[nvec].iov_len = strlen(r->hostname);
|
|
Packit |
90a5c9 |
nvec++;
|
|
Packit |
90a5c9 |
if (is_v6literal) {
|
|
Packit |
90a5c9 |
iov[nvec].iov_base = "]";
|
|
Packit |
90a5c9 |
iov[nvec].iov_len = 1;
|
|
Packit |
90a5c9 |
nvec++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (r->parsed_uri.port_str) {
|
|
Packit |
90a5c9 |
iov[nvec].iov_base = ":";
|
|
Packit |
90a5c9 |
iov[nvec].iov_len = 1;
|
|
Packit |
90a5c9 |
nvec++;
|
|
Packit |
90a5c9 |
iov[nvec].iov_base = r->parsed_uri.port_str;
|
|
Packit |
90a5c9 |
iov[nvec].iov_len = strlen(r->parsed_uri.port_str);
|
|
Packit |
90a5c9 |
nvec++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return apr_pstrcatv(r->pool, iov, nvec, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
core_server_config *conf = ap_get_core_module_config(r->server->module_config);
|
|
Packit |
90a5c9 |
const char *host_header = apr_table_get(r->headers_in, "Host");
|
|
Packit |
90a5c9 |
int is_v6literal = 0;
|
|
Packit |
90a5c9 |
int have_hostname_from_url = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->hostname) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If there was a host part in the Request-URI, ignore the 'Host'
|
|
Packit |
90a5c9 |
* header.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
have_hostname_from_url = 1;
|
|
Packit |
90a5c9 |
is_v6literal = fix_hostname(r, NULL, conf->http_conformance);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (host_header != NULL) {
|
|
Packit |
90a5c9 |
is_v6literal = fix_hostname(r, host_header, conf->http_conformance);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (r->status != HTTP_OK)
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If we have both hostname from an absoluteURI and a Host header,
|
|
Packit |
90a5c9 |
* we must ignore the Host header (RFC 2616 5.2).
|
|
Packit |
90a5c9 |
* To enforce this, we reset the Host header to the value from the
|
|
Packit |
90a5c9 |
* request line.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (have_hostname_from_url && host_header != NULL) {
|
|
Packit |
90a5c9 |
const char *repl = construct_host_header(r, is_v6literal);
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_in, "Host", repl);
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02417)
|
|
Packit |
90a5c9 |
"Replacing host header '%s' with host '%s' given "
|
|
Packit |
90a5c9 |
"in the request uri", host_header, repl);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* check if we tucked away a name_chain */
|
|
Packit |
90a5c9 |
if (r->connection->vhost_lookup_data) {
|
|
Packit |
90a5c9 |
if (r->hostname)
|
|
Packit |
90a5c9 |
check_hostalias(r);
|
|
Packit |
90a5c9 |
else
|
|
Packit |
90a5c9 |
check_serverpath(r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* For every virtual host on this connection, call func_cb.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_vhost_iterate_given_conn(conn_rec *conn,
|
|
Packit |
90a5c9 |
ap_vhost_iterate_conn_cb func_cb,
|
|
Packit |
90a5c9 |
void* baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
server_rec *s;
|
|
Packit |
90a5c9 |
server_rec *last_s;
|
|
Packit |
90a5c9 |
name_chain *src;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
int rv = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (conn->vhost_lookup_data) {
|
|
Packit |
90a5c9 |
last_s = NULL;
|
|
Packit |
90a5c9 |
port = conn->local_addr->port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (src = conn->vhost_lookup_data; src; src = src->next) {
|
|
Packit |
90a5c9 |
server_addr_rec *sar;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We only consider addresses on the name_chain which have a
|
|
Packit |
90a5c9 |
* matching port.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
sar = src->sar;
|
|
Packit |
90a5c9 |
if (sar->host_port != 0 && port != sar->host_port) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
s = src->server;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (s == last_s) {
|
|
Packit |
90a5c9 |
/* we've already done a callback for this vhost. */
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
last_s = s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = func_cb(baton, conn, s);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rv != 0) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = func_cb(baton, conn, conn->base_server);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Called for a new connection which has a known local_addr. Note that the
|
|
Packit |
90a5c9 |
* new connection is assumed to have conn->server == main server.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_update_vhost_given_ip(conn_rec *conn)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ipaddr_chain *trav;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* scan the hash table for an exact match first */
|
|
Packit |
90a5c9 |
trav = find_ipaddr(conn->local_addr);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (trav) {
|
|
Packit |
90a5c9 |
/* save the name_chain for later in case this is a name-vhost */
|
|
Packit |
90a5c9 |
conn->vhost_lookup_data = trav->names;
|
|
Packit |
90a5c9 |
conn->base_server = trav->server;
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* maybe there's a default server or wildcard name-based vhost
|
|
Packit |
90a5c9 |
* matching this port
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
port = conn->local_addr->port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
trav = find_default_server(port);
|
|
Packit |
90a5c9 |
if (trav) {
|
|
Packit |
90a5c9 |
conn->vhost_lookup_data = trav->names;
|
|
Packit |
90a5c9 |
conn->base_server = trav->server;
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* otherwise we're stuck with just the main server
|
|
Packit |
90a5c9 |
* and no name-based vhosts
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
conn->vhost_lookup_data = NULL;
|
|
Packit |
90a5c9 |
}
|