Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.4.x: add IPv6 link-local scope ID support with APR 1.7+ #440

Open
wants to merge 2 commits into
base: 2.4.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions changes-entries/pr59396.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*) core: Support zone/scope in IPv6 link-local addresses in Listen and
VirtualHost directives (requires APR 1.7.x or later). PR 59396
[Joe Orton]
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 @@ -277,8 +278,32 @@ static apr_status_t close_listeners_on_exec(void *v)
return APR_SUCCESS;
}

/* 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 @@ -288,15 +313,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 @@ -317,19 +337,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 @@ -383,6 +405,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(10102)
"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 @@ -835,10 +869,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 @@ -856,7 +894,8 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
ap_str_tolower(proto);
}

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 @@ -182,9 +183,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 @@ -203,6 +209,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(10103)
"Could not set scope ID %s for %pI -- ignoring!",
scope_id, my_addr);
return NULL;
}
}
#endif
}

/* Remember all addresses for the host */
Expand Down