📄 vhost.c
字号:
/***************************************************************************** * run-time vhost matching functions *//* Lowercase and remove any trailing dot and/or :port from the hostname, * and check that it is sane. * * In most configurations the exact syntax of the hostname isn't * important so strict sanity checking isn't necessary. However, in * mass hosting setups (using mod_vhost_alias or mod_rewrite) where * the hostname is interpolated into the filename, we need to be sure * that the interpolation doesn't expose parts of the filesystem. * We don't do strict RFC 952 / RFC 1123 syntax checking in order * to support iDNS and people who erroneously use underscores. * Instead we just check for filesystem metacharacters: directory * separators / and \ and sequences of more than one dot. */static void fix_hostname(request_rec *r){ char *host, *scope_id; char *dst; apr_port_t port; apr_status_t rv; /* According to RFC 2616, Host header field CAN be blank. */ if (!*r->hostname) { return; } rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool); if (rv != APR_SUCCESS || scope_id) { goto bad; } if (!host && port) { /* silly looking host ("Host: 123") but that isn't our job * here to judge; apr_parse_addr_port() would think we had a port * but no address */ host = apr_itoa(r->pool, (int)port); } else 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_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; apr_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. */AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host, apr_port_t 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; apr_port_t port; server_rec *s; server_rec *last_s; name_chain *src; last_s = NULL; apr_sockaddr_port_get(&port, r->connection->local_addr); /* 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; apr_port_t port; apr_sockaddr_port_get(&port, r->connection->local_addr); /* * 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; } }}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; } /* 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. */AP_DECLARE(void) ap_update_vhost_given_ip(conn_rec *conn){ ipaddr_chain *trav; apr_port_t port; /* scan the hash apr_table_t 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; } /* maybe there's a default server or wildcard name-based vhost * matching this port */ apr_sockaddr_port_get(&port, conn->local_addr); trav = find_default_server(port); if (trav) { conn->vhost_lookup_data = trav->names; conn->base_server = trav->server; return; } /* otherwise we're stuck with just the main server * and no name-based vhosts */ conn->vhost_lookup_data = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -