From 42b3eabb308df0c4da640ee2f6654f0a37181138 Mon Sep 17 00:00:00 2001 From: Joe Orton Date: Wed, 29 Nov 2017 09:07:48 +0000 Subject: [PATCH] Support IPv6 link-local scope IDs in Listen and VirtualHost, if built with APR 1.7 or later: * server/listen.c (match_address): New function, factored out from find_listeners. (find_listeners): Use it; add scope and temp pool arguments. (alloc_listener): Take scope_id and pool arguments and pass through; set zone for addresses if present. (ap_set_listener): Pass through temp pool and scope id. * server/vhost.c (get_addresses): Set zone for vhost address if present. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1816609 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 3 +++ server/listen.c | 69 ++++++++++++++++++++++++++++++++++++++----------- server/vhost.c | 19 +++++++++++++- 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 6d358aba2d..29e7f56d6a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.1 + *) core: Support zone/scope in IPv6 link-local addresses in Listen and + VirtualHost directives (requires APR 1.7.x or later). [Joe Orton] + *) mod_md: v1.0.5, restricting post_config dry run to be more silent and performing only necessary work for mod_ssl to be also happy with the configuration. [Stefan Eissing] diff --git a/server/listen.c b/server/listen.c index a8f32ecc49..6632be3a35 100644 --- a/server/listen.c +++ b/server/listen.c @@ -19,6 +19,7 @@ #define APR_WANT_STRFUNC #include "apr_want.h" +#include "apr_version.h" #include "ap_config.h" #include "httpd.h" @@ -404,8 +405,32 @@ static const char *set_systemd_listener(process_rec *process, apr_port_t port, #endif /* HAVE_SYSTEMD */ +/* Returns non-zero if socket address SA matches hostname, port and + * scope_id. p is used for temporary allocations. */ +static int match_address(const apr_sockaddr_t *sa, + const char *hostname, apr_port_t port, + const char *scope_id, apr_pool_t *p) +{ + const char *old_scope = NULL; + +#if APR_VERSION_AT_LEAST(1,7,0) + /* To be clever here we could correctly match numeric and + * non-numeric zone ids. Ignore failure, old_scope will be left + * as NULL. */ + (void) apr_sockaddr_zone_get(sa, &old_scope, NULL, p); +#endif + + return port == sa->port + && ((!hostname && !sa->hostname) + || (hostname && sa->hostname && !strcmp(sa->hostname, hostname))) + && ((!scope_id && !old_scope) + || (scope_id && old_scope && !strcmp(scope_id, old_scope))); +} + +/* ### This logic doesn't cope with DNS changes across a restart. */ static int find_listeners(ap_listen_rec **from, ap_listen_rec **to, - const char *addr, apr_port_t port) + const char *addr, apr_port_t port, + const char *scope_id, apr_pool_t *temp_pool) { int found = 0; @@ -415,15 +440,10 @@ static int find_listeners(ap_listen_rec **from, ap_listen_rec **to, /* Some listeners are not real so they will not have a bind_addr. */ if (sa) { ap_listen_rec *new; - apr_port_t oldport; - oldport = sa->port; - /* If both ports are equivalent, then if their names are equivalent, - * then we will re-use the existing record. - */ - if (port == oldport && - ((!addr && !sa->hostname) || - ((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) { + /* Re-use the existing record if it matches completely + * against an existing listener. */ + if (match_address(sa, addr, port, scope_id, temp_pool)) { found = 1; if (!to) { break; @@ -444,19 +464,21 @@ static int find_listeners(ap_listen_rec **from, ap_listen_rec **to, static const char *alloc_listener(process_rec *process, const char *addr, apr_port_t port, const char* proto, - void *slave) + const char *scope_id, void *slave, + apr_pool_t *temp_pool) { ap_listen_rec *last; apr_status_t status; apr_sockaddr_t *sa; /* see if we've got a listener for this address:port, which is an error */ - if (find_listeners(&ap_listeners, NULL, addr, port)) { + if (find_listeners(&ap_listeners, NULL, addr, port, scope_id, temp_pool)) { return "Cannot define multiple Listeners on the same IP:port"; } /* see if we've got an old listener for this address:port */ - if (find_listeners(&old_listeners, &ap_listeners, addr, port)) { + if (find_listeners(&old_listeners, &ap_listeners, addr, port, + scope_id, temp_pool)) { if (ap_listeners->slave != slave) { return "Cannot define a slave on the same IP:port as a Listener"; } @@ -510,6 +532,18 @@ static const char *alloc_listener(process_rec *process, const char *addr, return "Listen setup failed"; } +#if APR_VERSION_AT_LEAST(1,7,0) + if (scope_id) { + status = apr_sockaddr_zone_set(new->bind_addr, scope_id); + if (status) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool, APLOGNO() + "alloc_listener: failed to set scope for %pI to %s", + new->bind_addr, scope_id); + return "Listen step failed"; + } + } +#endif + /* We need to preserve the order returned by getaddrinfo() */ if (last == NULL) { ap_listeners = last = new; @@ -1000,10 +1034,14 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy, host = NULL; } +#if !APR_VERSION_AT_LEAST(1,7,0) if (scope_id) { - /* XXX scope id support is useful with link-local IPv6 addresses */ - return "Scope id is not supported"; + return apr_pstrcat(cmd->pool, + "Scope ID in address '", argv[0], + "' not supported with APR " APR_VERSION_STRING, + NULL); } +#endif if (!port) { return "Port must be specified"; @@ -1027,7 +1065,8 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy, } #endif - return alloc_listener(cmd->server->process, host, port, proto, NULL); + return alloc_listener(cmd->server->process, host, port, proto, + scope_id, NULL, cmd->temp_pool); } AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd, diff --git a/server/vhost.c b/server/vhost.c index 5ad4efd381..15ba3bbb93 100644 --- a/server/vhost.c +++ b/server/vhost.c @@ -23,6 +23,7 @@ #include "apr.h" #include "apr_strings.h" #include "apr_lib.h" +#include "apr_version.h" #define APR_WANT_STRFUNC #include "apr_want.h" @@ -181,9 +182,14 @@ static const char *get_addresses(apr_pool_t *p, const char *w_, if (!host) { return "Missing address for VirtualHost"; } +#if !APR_VERSION_AT_LEAST(1,7,0) if (scope_id) { - return "Scope ids are not supported"; + return apr_pstrcat(p, + "Scope ID in address '", w, + "' not supported with APR " APR_VERSION_STRING, + NULL); } +#endif if (!port && !wild_port) { port = default_port; } @@ -202,6 +208,17 @@ static const char *get_addresses(apr_pool_t *p, const char *w_, "Could not resolve host name %s -- ignoring!", host); return NULL; } +#if APR_VERSION_AT_LEAST(1,7,0) + if (scope_id) { + rv = apr_sockaddr_zone_set(my_addr, scope_id); + if (rv) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO() + "Could not set scope ID %s for %pI -- ignoring!", + scope_id, my_addr); + return NULL; + } + } +#endif } /* Remember all addresses for the host */ -- 2.40.0