diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2010-12-01 16:40:52 +0000 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-12-01 21:47:46 +0000 |
commit | 40fca394d11b9e6104e5f9c8ff6090c5af719a1b (patch) | |
tree | d8c3ae0f84ae0b8a2afdd185f9fc1b622b4be65b /gweb | |
parent | 3c9edd5e215d2939b0f61c7f6cd87d30c6965f6d (diff) | |
download | connman-40fca394d11b9e6104e5f9c8ff6090c5af719a1b.tar.gz connman-40fca394d11b9e6104e5f9c8ff6090c5af719a1b.tar.bz2 connman-40fca394d11b9e6104e5f9c8ff6090c5af719a1b.zip |
gresolv: Fix handling of IPv6 nameservers
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.
Diffstat (limited to 'gweb')
-rw-r--r-- | gweb/gresolv.c | 57 |
1 files changed, 43 insertions, 14 deletions
diff --git a/gweb/gresolv.c b/gweb/gresolv.c index 99dc57a1..539eb2d7 100644 --- a/gweb/gresolv.c +++ b/gweb/gresolv.c @@ -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) |