summaryrefslogtreecommitdiffstats
path: root/server/listen.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/listen.c')
-rw-r--r--server/listen.c69
1 files changed, 54 insertions, 15 deletions
diff --git a/server/listen.c b/server/listen.c
index 5242c2a..9577d60 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"
@@ -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;
@@ -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;
@@ -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";
}
@@ -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;
@@ -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";
@@ -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,