diff --git a/lib/irs/getaddrinfo.c b/lib/irs/getaddrinfo.c index 1b2df6d71c..d5481c7afa 100644 --- a/lib/irs/getaddrinfo.c +++ b/lib/irs/getaddrinfo.c @@ -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, diff --git a/lib/irs/tests/resconf_test.c b/lib/irs/tests/resconf_test.c index 488ed5ba02..23938d2e41 100644 --- a/lib/irs/tests/resconf_test.c +++ b/lib/irs/tests/resconf_test.c @@ -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 diff --git a/lib/irs/tests/testdata/nameserver-v6-scoped.conf b/lib/irs/tests/testdata/nameserver-v6-scoped.conf new file mode 100644 index 0000000000..f8501547e4 --- /dev/null +++ b/lib/irs/tests/testdata/nameserver-v6-scoped.conf @@ -0,0 +1,10 @@ +# 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 diff --git a/util/copyrights b/util/copyrights index 97ed7ab6b8..0a2cc0e0f7 100644 --- a/util/copyrights +++ b/util/copyrights @@ -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