]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
libirs: handle scoped IPv6 addresses in /etc/resolv.conf
authorMichał Kępień <michal@isc.org>
Tue, 23 Oct 2018 12:50:00 +0000 (14:50 +0200)
committerMichał Kępień <michal@isc.org>
Tue, 23 Oct 2018 12:50:00 +0000 (14:50 +0200)
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.

lib/irs/getaddrinfo.c
lib/irs/tests/resconf_test.c
lib/irs/tests/testdata/nameserver-v6-scoped.conf [new file with mode: 0644]
util/copyrights

index 1b2df6d71c9a3df0e75c1f336833a136e0d6a06c..d5481c7afa605880b7c9e5ca17815d5f80e6ea5a 100644 (file)
@@ -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,
index 488ed5ba026fdcda5d9bf2cf49f6440f5aa965f7..23938d2e412cb7e9d6cf5f6a2e7f6c8593ff37d9 100644 (file)
@@ -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 (file)
index 0000000..f850154
--- /dev/null
@@ -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
index 97ed7ab6b80fe233bc9e12c96f93debfb746981c..0a2cc0e0f7dfe78fd8f1622e37f18016a7f2ff13 100644 (file)
 ./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