Commit 76d49c05 authored by Michał Kępień's avatar Michał Kępień
Browse files

libirs: handle scoped IPv6 addresses in /etc/resolv.conf

Commonly used network configuration tools write scoped IPv6 nameserver
addresses to /etc/resolv.conf.  libirs only handles these when it is
compiled with -DIRS_HAVE_SIN6_SCOPE_ID, which is not the default, and
only handles numeric scopes, which is not what network configuration
tools typically use.  This causes dig to be practically unable to handle
scoped IPv6 nameserver addresses in /etc/resolv.conf.

Fix the problem by:

  - not requiring a custom compile-time flag to be set in order for
    scoped IPv6 addresses to be processed by getaddrinfo(),

  - parsing non-numeric scope identifiers using if_nametoindex(),

  - setting the sin6_scope_id field in struct sockaddr_in6 structures
    returned by getaddrinfo() even if the AI_CANONNAME flag is not set.
parent 2791bf92
......@@ -181,6 +181,47 @@ static void _freeaddrinfo(struct addrinfo *ai);
#define FOUND_IPV6 0x2
#define FOUND_MAX 2
/*%
* Try converting the scope identifier in 'src' to a network interface index.
* Upon success, return true and store the resulting index in 'dst'. Upon
* failure, return false.
*/
static bool
parse_scopeid(const char *src, uint32_t *dst) {
uint32_t scopeid = 0;
REQUIRE(src != NULL);
REQUIRE(dst != NULL);
#ifdef HAVE_IF_NAMETOINDEX
/*
* Try using if_nametoindex() first if it is available. As it does not
* handle numeric scopes, we do not simply return if it fails.
*/
scopeid = (uint32_t)if_nametoindex(src);
#endif
/*
* Fall back to numeric scope processing if if_nametoindex() either
* fails or is unavailable.
*/
if (scopeid == 0) {
char *endptr = NULL;
scopeid = (uint32_t)strtoul(src, &endptr, 10);
/*
* The scope identifier must not be empty and no trailing
* characters are allowed after it.
*/
if (src == endptr || endptr == NULL || *endptr != '\0') {
return (false);
}
}
*dst = scopeid;
return (true);
}
#define ISC_AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST)
/*%
* Get a list of IP addresses and port numbers for host hostname and
......@@ -365,39 +406,24 @@ getaddrinfo(const char *hostname, const char *servname,
char abuf[sizeof(struct in6_addr)];
char nbuf[NI_MAXHOST];
int addrsize, addroff;
#ifdef IRS_HAVE_SIN6_SCOPE_ID
char *p, *ep;
char ntmp[NI_MAXHOST];
uint32_t scopeid;
#endif
uint32_t scopeid = 0;
#ifdef IRS_HAVE_SIN6_SCOPE_ID
/*
* Scope identifier portion.
*/
ntmp[0] = '\0';
if (strchr(hostname, '%') != NULL) {
char *p;
strlcpy(ntmp, hostname, sizeof(ntmp));
p = strchr(ntmp, '%');
ep = NULL;
/*
* Vendors may want to support non-numeric
* scopeid around here.
*/
if (p != NULL)
scopeid = (uint32_t)strtoul(p + 1,
&ep, 10);
if (p != NULL && ep != NULL && ep[0] == '\0')
if (p != NULL && parse_scopeid(p + 1, &scopeid)) {
*p = '\0';
else {
} else {
ntmp[0] = '\0';
scopeid = 0;
}
} else
scopeid = 0;
#endif
}
if (inet_pton(AF_INET, hostname, (struct in_addr *)abuf)
== 1) {
......@@ -415,7 +441,6 @@ getaddrinfo(const char *hostname, const char *servname,
addroff = offsetof(struct sockaddr_in, sin_addr);
family = AF_INET;
goto common;
#ifdef IRS_HAVE_SIN6_SCOPE_ID
} else if (ntmp[0] != '\0' &&
inet_pton(AF_INET6, ntmp, abuf) == 1) {
if (family && family != AF_INET6)
......@@ -424,7 +449,6 @@ getaddrinfo(const char *hostname, const char *servname,
addroff = offsetof(struct sockaddr_in6, sin6_addr);
family = AF_INET6;
goto common;
#endif
} else if (inet_pton(AF_INET6, hostname, abuf) == 1) {
if (family != 0 && family != AF_INET6)
return (EAI_NONAME);
......@@ -444,12 +468,10 @@ getaddrinfo(const char *hostname, const char *servname,
ai->ai_socktype = socktype;
SIN(ai->ai_addr)->sin_port = port;
memmove((char *)ai->ai_addr + addroff, abuf, addrsize);
if (ai->ai_family == AF_INET6) {
SIN6(ai->ai_addr)->sin6_scope_id = scopeid;
}
if ((flags & AI_CANONNAME) != 0) {
#ifdef IRS_HAVE_SIN6_SCOPE_ID
if (ai->ai_family == AF_INET6)
SIN6(ai->ai_addr)->sin6_scope_id =
scopeid;
#endif
if (getnameinfo(ai->ai_addr,
(socklen_t)ai->ai_addrlen,
nbuf, sizeof(nbuf), NULL, 0,
......
......@@ -64,6 +64,9 @@ ATF_TC_BODY(irs_resconf_load, tc) {
}, {
"testdata/nameserver-v6.conf", ISC_R_SUCCESS,
NULL, ISC_R_SUCCESS
}, {
"testdata/nameserver-v6-scoped.conf", ISC_R_SUCCESS,
NULL, ISC_R_SUCCESS
}, {
"testdata/options-debug.conf", ISC_R_SUCCESS,
NULL, ISC_R_SUCCESS
......
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
nameserver fe80::1%1
......@@ -3298,6 +3298,7 @@
./lib/irs/tests/resconf_test.c C 2016,2018
./lib/irs/tests/testdata/domain.conf CONF-SH 2016,2018
./lib/irs/tests/testdata/nameserver-v4.conf CONF-SH 2016,2018
./lib/irs/tests/testdata/nameserver-v6-scoped.conf CONF-SH 2018
./lib/irs/tests/testdata/nameserver-v6.conf CONF-SH 2016,2018
./lib/irs/tests/testdata/options-bad-ndots.conf CONF-SH 2018
./lib/irs/tests/testdata/options-debug.conf CONF-SH 2016,2018
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment