rwrap: fix resolv wrapper with ipv6 addresses and old glibc versions
authorStefan Metzmacher <metze@samba.org>
Thu, 14 Feb 2019 14:46:22 +0000 (15:46 +0100)
committerAndreas Schneider <asn@samba.org>
Fri, 20 Mar 2020 12:20:57 +0000 (13:20 +0100)
The handling of __res_state._u._ext was different before
this glibc commit (e.g. glibc-2.19):

  commit 2212c1420c92a33b0e0bd9a34938c9814a56c0f7
  Author:     Andreas Schwab <schwab@suse.de>
  AuthorDate: Thu Feb 19 15:52:08 2015 +0100
  Commit:     Andreas Schwab <schwab@suse.de>
  CommitDate: Thu May 21 15:16:37 2015 +0200

      Simplify handling of nameserver configuration in resolver

      Remove use of ext.nsmap member of struct __res_state and always use
      an identity mapping betwen the nsaddr_list array and the ext.nsaddrs
      array.  The fact that a nameserver has an IPv6 address is signalled by
      setting nsaddr_list[].sin_family to zero.

As a result of fixing this, it's now possible to run 'test_res_init'
even without using resolv_wrapper.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
src/resolv_wrapper.c
tests/test_res_init.c

index cbca24864343a679c4f85c7e0fb63a7e36ffe1c0..20ba2d1a899536977daaa20ac6bd9d5dacbf7272 100644 (file)
@@ -1645,14 +1645,13 @@ static int rwrap_parse_resolv_conf(struct __res_state *state,
 
                        ok = inet_pton(AF_INET, p, &a);
                        if (ok) {
-                               state->nsaddr_list[state->nscount] = (struct sockaddr_in) {
+                               state->nsaddr_list[nserv] = (struct sockaddr_in) {
                                        .sin_family = AF_INET,
                                        .sin_addr = a,
                                        .sin_port = htons(53),
                                        .sin_zero = { 0 },
                                };
 
-                               state->nscount++;
                                nserv++;
                        } else {
 #ifdef HAVE_RESOLV_IPV6_NSADDRS
@@ -1673,11 +1672,11 @@ static int rwrap_parse_resolv_conf(struct __res_state *state,
                                        sa6->sin6_flowinfo = 0;
                                        sa6->sin6_addr = a6;
 
-                                       state->_u._ext.nsaddrs[state->_u._ext.nscount] = sa6;
-                                       state->_u._ext.nssocks[state->_u._ext.nscount] = -1;
-                                       state->_u._ext.nsmap[state->_u._ext.nscount] = MAXNS + 1;
+                                       state->_u._ext.nsaddrs[nserv] = sa6;
+                                       state->_u._ext.nssocks[nserv] = -1;
+                                       state->_u._ext.nsmap[nserv] = MAXNS + 1;
 
-                                       state->_u._ext.nscount++;
+                                       state->_u._ext.nscount6++;
                                        nserv++;
                                } else {
                                        RWRAP_LOG(RWRAP_LOG_ERROR,
@@ -1700,6 +1699,13 @@ static int rwrap_parse_resolv_conf(struct __res_state *state,
                } /* TODO: match other keywords */
        }
 
+       /*
+        * note that state->_u._ext.nscount is left as 0,
+        * this matches glibc and allows resolv wrapper
+        * to work with most (maybe all) glibc versions.
+        */
+       state->nscount = nserv;
+
        if (ferror(fp)) {
                RWRAP_LOG(RWRAP_LOG_ERROR,
                          "Reading from %s failed",
@@ -1725,21 +1731,36 @@ static int rwrap_res_ninit(struct __res_state *state)
                const char *resolv_conf = getenv("RESOLV_WRAPPER_CONF");
 
                if (resolv_conf != NULL) {
+                       /* Delete name servers */
+#ifdef HAVE_RESOLV_IPV6_NSADDRS
                        uint16_t i;
 
-                       (void)i; /* maybe unused */
+                       for (i = 0; i < state->nscount; i++) {
+                               if (state->_u._ext.nssocks[i] != -1) {
+                                       close(state->_u._ext.nssocks[i]);
+                                       state->_u._ext.nssocks[i] = -1;
+                               }
+
+                               SAFE_FREE(state->_u._ext.nsaddrs[i]);
+                       }
+#endif
 
-                       /* Delete name servers */
                        state->nscount = 0;
                        memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list));
 
 #ifdef HAVE_RESOLV_IPV6_NSADDRS
+                       state->ipv6_unavail = false;
+                       state->_u._ext.nsinit = 0;
                        state->_u._ext.nscount = 0;
-                       for (i = 0; i < state->_u._ext.nscount; i++) {
-                               SAFE_FREE(state->_u._ext.nsaddrs[i]);
+                       state->_u._ext.nscount6 = 0;
+                       for (i = 0; i < MAXNS; i++) {
+                               state->_u._ext.nsaddrs[i] = NULL;
+                               state->_u._ext.nssocks[i] = -1;
+                               state->_u._ext.nsmap[i] = MAXNS;
                        }
 #endif
 
+                       /* And parse the new name servers */
                        rc = rwrap_parse_resolv_conf(state, resolv_conf);
                }
        }
@@ -1786,19 +1807,7 @@ int __res_init(void)
 
 static void rwrap_res_nclose(struct __res_state *state)
 {
-#ifdef HAVE_RESOLV_IPV6_NSADDRS
-       int i;
-#endif
-
        libc_res_nclose(state);
-
-#ifdef HAVE_RESOLV_IPV6_NSADDRS
-       if (state != NULL) {
-               for (i = 0; i < state->_u._ext.nscount; i++) {
-                       SAFE_FREE(state->_u._ext.nsaddrs[i]);
-               }
-       }
-#endif
 }
 
 #if !defined(res_nclose) && defined(HAVE_RES_NCLOSE)
index 7f7337838adc8d96a7dfa24ad4abbd27cf749052..f0cd2ee6b116c0b1961458f1e062f2b773dce483 100644 (file)
@@ -121,26 +121,17 @@ static void test_res_ninit(void **state)
 
        /*
         * Validate the number of parsed name servers.
-        */
-
-       assert_int_equal(dnsstate.nscount + dnsstate._u._ext.nscount, MAXNS);
-
-#ifndef HAVE_RESOLV_IPV6_NSADDRS
-       /*
+       *
         * On platforms that don't support IPv6, the v6 address is skipped
         * and we end up reading three v4 addresses.
-        */
-       assert_int_equal(dnsstate.nscount, MAXNS);
-#else
-       /*
+        *
         * test we have two v4 and one v6 server
         *
         * Note: This test assumes MAXNS == 3, which is the
         * case on all systems encountered so far.
         */
-       assert_int_equal(dnsstate.nscount, 2);
-       assert_int_equal(dnsstate._u._ext.nscount, 1);
-#endif /* HAVE_RESOLV_IPV6_NSADDRS */
+       assert_int_equal(MAXNS, 3);
+       assert_int_equal(dnsstate.nscount, MAXNS);
 
        /* Validate the servers. */
 
@@ -150,12 +141,18 @@ static void test_res_ninit(void **state)
        inet_ntop(AF_INET, &(dnsstate.nsaddr_list[0].sin_addr),
                  straddr, INET6_ADDRSTRLEN);
        assert_string_equal(nameservers[0], straddr);
+#ifdef HAVE_RESOLV_IPV6_NSADDRS
+       assert_null(dnsstate._u._ext.nsaddrs[0]);
+#endif
 
        assert_int_equal(dnsstate.nsaddr_list[1].sin_family, AF_INET);
        assert_int_equal(dnsstate.nsaddr_list[1].sin_port, htons(53));
        inet_ntop(AF_INET, &(dnsstate.nsaddr_list[1].sin_addr),
                  straddr, INET6_ADDRSTRLEN);
        assert_string_equal(nameservers[1], straddr);
+#ifdef HAVE_RESOLV_IPV6_NSADDRS
+       assert_null(dnsstate._u._ext.nsaddrs[1]);
+#endif
 
 #ifndef HAVE_RESOLV_IPV6_NSADDRS
        /*
@@ -169,7 +166,8 @@ static void test_res_ninit(void **state)
        assert_string_equal(nameservers[3], straddr);
 #else
        /* IPv6 */
-       sa6 = dnsstate._u._ext.nsaddrs[0];
+       assert_non_null(dnsstate._u._ext.nsaddrs[2]);
+       sa6 = dnsstate._u._ext.nsaddrs[2];
        assert_int_equal(sa6->sin6_family, AF_INET6);
        assert_int_equal(sa6->sin6_port, htons(53));
        inet_ntop(AF_INET6, &(sa6->sin6_addr), straddr, INET6_ADDRSTRLEN);