Skip to content

Commit

Permalink
Support IPv6 link-local scope IDs in Listen and VirtualHost, if built
Browse files Browse the repository at this point in the history
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
  • Loading branch information
notroj committed Nov 29, 2017
1 parent b75ebb4 commit 42b3eab
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 16 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
69 changes: 54 additions & 15 deletions server/listen.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "apr_version.h"

#include "ap_config.h"
#include "httpd.h"
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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";
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand All @@ -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,
Expand Down
19 changes: 18 additions & 1 deletion server/vhost.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 */
Expand Down

0 comments on commit 42b3eab

Please sign in to comment.