dns: fix workaround for systems defaulting to ipv6-only sockets
authorAlexey Izbyshev <izbyshev@ispras.ru>
Fri, 24 Feb 2023 22:07:33 +0000 (01:07 +0300)
committerRich Felker <dalias@aerifal.cx>
Mon, 27 Feb 2023 15:03:56 +0000 (10:03 -0500)
When IPv6 nameservers are present, __res_msend_rc attempts to disable
IPV6_V6ONLY socket option to ensure that it can communicate with IPv4
nameservers (if they are present too) via IPv4-mapped IPv6 addresses.
However, this option can't be disabled on bound sockets, so setsockopt
always fails.

src/network/res_msend.c

index 2643be2..86c2fcf 100644 (file)
@@ -133,6 +133,22 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
                family = AF_INET;
                sl = sizeof sa.sin;
        }
+
+       /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
+       if (fd >= 0 && family == AF_INET6) {
+               setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
+               for (i=0; i<nns; i++) {
+                       if (ns[i].sin.sin_family != AF_INET) continue;
+                       memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
+                               &ns[i].sin.sin_addr, 4);
+                       memcpy(ns[i].sin6.sin6_addr.s6_addr,
+                               "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+                       ns[i].sin6.sin6_family = AF_INET6;
+                       ns[i].sin6.sin6_flowinfo = 0;
+                       ns[i].sin6.sin6_scope_id = 0;
+               }
+       }
+
        sa.sin.sin_family = family;
        if (fd < 0 || bind(fd, (void *)&sa, sl) < 0) {
                if (fd >= 0) close(fd);
@@ -152,21 +168,6 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
        pthread_cleanup_push(cleanup, pfd);
        pthread_setcancelstate(cs, 0);
 
-       /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
-       if (family == AF_INET6) {
-               setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
-               for (i=0; i<nns; i++) {
-                       if (ns[i].sin.sin_family != AF_INET) continue;
-                       memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
-                               &ns[i].sin.sin_addr, 4);
-                       memcpy(ns[i].sin6.sin6_addr.s6_addr,
-                               "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
-                       ns[i].sin6.sin6_family = AF_INET6;
-                       ns[i].sin6.sin6_flowinfo = 0;
-                       ns[i].sin6.sin6_scope_id = 0;
-               }
-       }
-
        memset(alens, 0, sizeof *alens * nqueries);
 
        retry_interval = timeout / attempts;