gresolv: Fix handling of IPv6 nameservers
authorDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 1 Dec 2010 16:40:52 +0000 (16:40 +0000)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 1 Dec 2010 21:47:46 +0000 (21:47 +0000)
If IPv6 nameservers were specified in /etc/resolv.conf, we would end up
sending NS queries to 0.0.0.0 because we weren't parsing the __res_state
structure correctly. We were assuming that all listed nameservers would
be Legacy IP.

Also fix connect_udp_channel() to generate the address correctly instead
of just asssuming Legacy IP.

gweb/gresolv.c

index 99dc57a..539eb2d 100644 (file)
@@ -28,7 +28,9 @@
 #include <stdarg.h>
 #include <string.h>
 #include <resolv.h>
+#include <sys/types.h>
 #include <sys/socket.h>
+#include <netdb.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
 
@@ -303,23 +305,40 @@ static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
 
 static int connect_udp_channel(struct resolv_nameserver *nameserver)
 {
-       struct sockaddr_in sin;
-       int sk;
-
-       sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       if (sk < 0)
+       struct addrinfo hints, *rp;
+       char portnr[6];
+       int err, sk;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
+
+       sprintf(portnr, "%d", nameserver->port);
+       err = getaddrinfo(nameserver->address, portnr, &hints, &rp);
+       if (err)
+               return -EINVAL;
+
+       /* Do not blindly copy this code elsewhere; it doesn't loop over the
+          results using ->ai_next as it should. That's OK in *this* case
+          because it was a numeric lookup; we *know* there's only one. */
+       if (!rp)
+               return -EINVAL;
+
+       sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+       if (sk < 0) {
+               freeaddrinfo(rp);
                return -EIO;
+       }
 
-       memset(&sin, 0, sizeof(sin));
-       sin.sin_family = AF_INET;
-       sin.sin_port = htons(nameserver->port);
-       sin.sin_addr.s_addr = inet_addr(nameserver->address);
-
-       if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+       if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) {
                close(sk);
+               freeaddrinfo(rp);
                return -EIO;
        }
 
+       freeaddrinfo(rp);
+
        nameserver->udp_channel = g_io_channel_unix_new(sk);
        if (nameserver->udp_channel == NULL) {
                close(sk);
@@ -461,9 +480,19 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
                int i;
 
                for (i = 0; i < resolv->res.nscount; i++) {
-                       struct sockaddr_in *addr = &resolv->res.nsaddr_list[i];
-                       g_resolv_add_nameserver(resolv,
-                                       inet_ntoa(addr->sin_addr), 53, 0);
+                       char buf[100];
+                       int family = resolv->res.nsaddr_list[i].sin_family;
+                       void *sa_addr = &resolv->res.nsaddr_list[i].sin_addr;
+
+                       if (family != AF_INET && resolv->res._u._ext.nsaddrs[i]) {
+                               family = AF_INET6;
+                               sa_addr = &resolv->res._u._ext.nsaddrs[i]->sin6_addr;
+                       }
+                       if (family != AF_INET && family != AF_INET6)
+                               continue;
+
+                       if (inet_ntop(family, sa_addr, buf, sizeof(buf)))
+                               g_resolv_add_nameserver(resolv, buf, 53, 0);
                }
 
                if (resolv->nameserver_list == NULL)