chiark / gitweb /
Support transport over IPv6.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 5 May 2014 17:34:56 +0000 (18:34 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 10 May 2014 13:32:26 +0000 (14:32 +0100)
We need multiple UDP sockets, for each address family required, with the
knock-on complexity to the sending and receiving code.

[[REBASE NOTE: this commit commutes syntatically with the adns_r_addr
enhancement patch, but won't actually work.  Before the series is
submitted for review, it wants to be hoisted /after/ that patch.  This
note can then be removed.]]

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
src/addrfam.c
src/adns.h
src/event.c
src/general.c
src/internal.h
src/setup.c
src/transmit.c

index ff533c939ca0fd6baf76c2e29d77a7230d8fa50f..d222d31ac3a6f4471546f746188658d1c84936aa 100644 (file)
  */
 
 #define SIN(sa) ((struct sockaddr_in *)(sa))
+#define CSIN(sa) ((const struct sockaddr_in *)(sa))
 
 static void *inet_sockaddr_to_inaddr(struct sockaddr *sa)
   { return &SIN(sa)->sin_addr; }
 
+static int inet_sockaddr_equalp(const struct sockaddr *sa,
+                               const struct sockaddr *sb)
+{
+  const struct sockaddr_in *sina = CSIN(sa), *sinb = CSIN(sb);
+  return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr &&
+         sina->sin_port == sinb->sin_port);
+}
+
 static void inet_prefix_mask(int len, union gen_addr *mask)
   { mask->v4.s_addr = htonl(!len ? 0 : 0xffffffff << (32 - len)); }
 
@@ -82,9 +91,9 @@ static void inet_rev_mkaddr(union gen_addr *addr, const byte *ipv)
                          (ipv[1]<<8) | (ipv[0]));
 }
 
-static char *inet_rev_mkname(struct sockaddr *sa, char *buf)
+static char *inet_rev_mkname(const struct sockaddr *sa, char *buf)
 {
-  unsigned long a = ntohl(SIN(sa)->sin_addr.s_addr);
+  unsigned long a = ntohl(CSIN(sa)->sin_addr.s_addr);
   int i;
 
   for (i = 0; i < 4; i++) {
@@ -97,7 +106,8 @@ static char *inet_rev_mkname(struct sockaddr *sa, char *buf)
 
 const afinfo adns__inet_afinfo = {
   AF_INET, 32, '.', 4, 3, adns_r_a,
-  inet_sockaddr_to_inaddr, inet_prefix_mask, inet_guess_len, inet_matchp,
+  inet_sockaddr_to_inaddr, inet_sockaddr_equalp,
+  inet_prefix_mask, inet_guess_len, inet_matchp,
   inet_rev_parsecomp, inet_rev_mkaddr, inet_rev_mkname
 };
 
@@ -106,10 +116,22 @@ const afinfo adns__inet_afinfo = {
  */
 
 #define SIN6(sa) ((struct sockaddr_in6 *)(sa))
+#define CSIN6(sa) ((const struct sockaddr_in6 *)(sa))
 
 static void *inet6_sockaddr_to_inaddr(struct sockaddr *sa)
   { return &SIN6(sa)->sin6_addr; }
 
+static int inet6_sockaddr_equalp(const struct sockaddr *sa,
+                                const struct sockaddr *sb)
+{
+  const struct sockaddr_in6 *sin6a = CSIN6(sa), *sin6b = CSIN6(sb);
+  return (memcmp(sin6a->sin6_addr.s6_addr,
+                sin6b->sin6_addr.s6_addr,
+                sizeof(sin6a->sin6_addr.s6_addr)) == 0 &&
+         sin6a->sin6_port == sin6b->sin6_port &&
+         sin6a->sin6_scope_id == sin6b->sin6_scope_id);
+}
+
 static void inet6_prefix_mask(int len, union gen_addr *mask)
 {
   int i = len/8, j = len%8;
@@ -156,9 +178,9 @@ static void inet6_rev_mkaddr(union gen_addr *addr, const byte *ipv)
     a[i] = (ipv[31-2*i] << 4) | (ipv[30-2*i] << 0);
 }
 
-static char *inet6_rev_mkname(struct sockaddr *sa, char *buf)
+static char *inet6_rev_mkname(const struct sockaddr *sa, char *buf)
 {
-  unsigned char *a = SIN6(sa)->sin6_addr.s6_addr + 16;
+  const unsigned char *a = CSIN6(sa)->sin6_addr.s6_addr + 16;
   unsigned c, y;
   int i, j;
 
@@ -177,6 +199,7 @@ static char *inet6_rev_mkname(struct sockaddr *sa, char *buf)
 
 const afinfo adns__inet6_afinfo = {
   AF_INET6, 128, ':', 32, 1, adns_r_aaaa,
-  inet6_sockaddr_to_inaddr, inet6_prefix_mask, inet6_guess_len, inet6_matchp,
+  inet6_sockaddr_to_inaddr, inet6_sockaddr_equalp,
+  inet6_prefix_mask, inet6_guess_len, inet6_matchp,
   inet6_rev_parsecomp, inet6_rev_mkaddr, inet6_rev_mkname
 };
index 79f601dc3d9360e5529dfd9d1b2f11f17f98d13f..3989f4fe50f91037312bdba68275c9fb0e539b39 100644 (file)
@@ -833,7 +833,7 @@ int adns_beforepoll(adns_state ads, struct pollfd *fds,
  * In any case this call won't block.
  */
 
-#define ADNS_POLLFDS_RECOMMENDED 2
+#define ADNS_POLLFDS_RECOMMENDED 3
 /* If you allocate an fds buf with at least RECOMMENDED entries then
  * you are unlikely to need to enlarge it.  You are recommended to do
  * so if it's convenient.  However, you must be prepared for adns to
index ad5861ec65c578a571a27e45d8497b9bf767200d..7050ca8287a50a02089825207c664d564cd4a1cb 100644 (file)
@@ -99,7 +99,7 @@ static void tcp_broken_events(adns_state ads) {
 
 void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
   int r, fd, tries;
-  struct sockaddr_in addr;
+  adns_rr_addr *addr;
   struct protoent *proto;
 
   for (tries=0; tries<ads->nservers; tries++) {
@@ -123,7 +123,8 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
       adns__diag(ads,-1,0,"unable to find protocol no. for TCP !");
       return;
     }
-    fd= socket(AF_INET,SOCK_STREAM,proto->p_proto);
+    addr = &ads->servers[ads->tcpserver];
+    fd= socket(addr->addr.sa.sa_family, SOCK_STREAM, proto->p_proto);
     if (fd<0) {
       adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno));
       return;
@@ -135,11 +136,7 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
       close(fd);
       return;
     }
-    memset(&addr,0,sizeof(addr));
-    addr.sin_family= AF_INET;
-    addr.sin_port= htons(DNS_PORT);
-    addr.sin_addr= ads->servers[ads->tcpserver].addr;
-    r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
+    r= connect(fd,&addr->addr.sa,addr->len);
     ads->tcpsocket= fd;
     ads->tcpstate= server_connecting;
     if (r==0) { tcp_connected(ads,now); return; }
@@ -311,34 +308,39 @@ void adns_processtimeouts(adns_state ads, const struct timeval *now) {
 int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
   /* Returns the number of entries filled in.  Always zeroes revents. */
 
-  assert(MAX_POLLFDS==2);
+  int i;
 
-  pollfds_buf[0].fd= ads->udpsocket;
-  pollfds_buf[0].events= POLLIN;
-  pollfds_buf[0].revents= 0;
+  assert(MAX_POLLFDS == MAXUDP + 1);
+
+  for (i = 0; i < ads->nudp; i++) {
+    pollfds_buf[i].fd= ads->udpsocket[i].fd;
+    pollfds_buf[i].events= POLLIN;
+    pollfds_buf[i].revents= 0;
+  }
 
   switch (ads->tcpstate) {
   case server_disconnected:
   case server_broken:
     return 1;
   case server_connecting:
-    pollfds_buf[1].events= POLLOUT;
+    pollfds_buf[i].events= POLLOUT;
     break;
   case server_ok:
-    pollfds_buf[1].events=
+    pollfds_buf[i].events=
       ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
     break;
   default:
     abort();
   }
-  pollfds_buf[1].fd= ads->tcpsocket;
-  return 2;
+  pollfds_buf[i++].fd= ads->tcpsocket;
+  return i;
 }
 
 int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
-  int want, dgramlen, r, udpaddrlen, serv, old_skip;
+  int want, dgramlen, r, i, udpaddrlen, serv, old_skip;
   byte udpbuf[DNS_MAXUDP];
-  struct sockaddr_in udpaddr;
+  struct udpsocket *udp;
+  adns_sockaddr udpaddr;
   
   adns__consistency(ads,0,cc_entex);
 
@@ -391,44 +393,33 @@ int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
   default:
     abort();
   }
-  if (fd == ads->udpsocket) {
-    for (;;) {
-      udpaddrlen= sizeof(udpaddr);
-      r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
-                 (struct sockaddr*)&udpaddr,&udpaddrlen);
-      if (r<0) {
-       if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; }
-       if (errno == EINTR) continue;
-       if (errno_resources(errno)) { r= errno; goto xit; }
-       adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
-       r= 0; goto xit;
-      }
-      if (udpaddrlen != sizeof(udpaddr)) {
-       adns__diag(ads,-1,0,"datagram received with wrong address length %d"
-                  " (expected %lu)", udpaddrlen,
-                  (unsigned long)sizeof(udpaddr));
-       continue;
-      }
-      if (udpaddr.sin_family != AF_INET) {
-       adns__diag(ads,-1,0,"datagram received with wrong protocol family"
-                  " %u (expected %u)",udpaddr.sin_family,AF_INET);
-       continue;
-      }
-      if (ntohs(udpaddr.sin_port) != DNS_PORT) {
-       adns__diag(ads,-1,0,"datagram received from wrong port"
-                  " %u (expected %u)", ntohs(udpaddr.sin_port),DNS_PORT);
-       continue;
-      }
-      for (serv= 0;
-          serv < ads->nservers &&
-            ads->servers[serv].addr.s_addr != udpaddr.sin_addr.s_addr;
-          serv++);
-      if (serv >= ads->nservers) {
-       adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
-                  inet_ntoa(udpaddr.sin_addr));
-       continue;
+  for (i = 0; i < ads->nudp; i++) {
+    udp = &ads->udpsocket[i];
+    if (fd == udp->fd) {
+      for (;;) {
+       udpaddrlen= sizeof(udpaddr);
+       r= recvfrom(fd,udpbuf,sizeof(udpbuf),0, &udpaddr.sa,&udpaddrlen);
+       if (r<0) {
+         if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; }
+         if (errno == EINTR) continue;
+         if (errno_resources(errno)) { r= errno; goto xit; }
+         adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
+         r= 0; goto xit;
+       }
+       for (serv= 0;
+            serv < ads->nservers &&
+              !(udpaddr.sa.sa_family == ads->servers[serv].addr.sa.sa_family &&
+                udp->ai->sockaddr_equalp(&udpaddr.sa,
+                                         &ads->servers[serv].addr.sa));
+            serv++);
+       if (serv >= ads->nservers) {
+         adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
+                    adns__sockaddr_ntoa(&udpaddr.sa, udpaddrlen));
+         continue;
+       }
+       adns__procdgram(ads,udpbuf,r,serv,0,*now);
       }
-      adns__procdgram(ads,udpbuf,r,serv,0,*now);
+      break;
     }
   }
   r= 0;
index 4404b31109191d4754f65246beb654bd2324fee4..facc0b5b8d433ce1465848e291393e697b93d010 100644 (file)
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 
 #include "internal.h"
 
 /* Core diagnostic functions */
 
+const char *adns__sockaddr_ntoa(struct sockaddr *sa, size_t n)
+{
+  static char buf[64];
+  int err;
+
+  err = getnameinfo(sa, n, buf, sizeof(buf), 0, 0, NI_NUMERICHOST);
+  assert(!err);
+  return buf;
+}
+
 void adns__vlprintf(adns_state ads, const char *fmt, va_list al) {
   ads->logfn(ads,ads->logfndata,fmt,al);
 }
@@ -83,7 +94,9 @@ void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
   }
   
   if (serv>=0) {
-    adns__lprintf(ads,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
+    adns__lprintf(ads,"%sNS=%s",bef,
+                 adns__sockaddr_ntoa(&ads->servers[serv].addr.sa,
+                                     ads->servers[serv].len));
     bef=", "; aft=")\n";
   }
 
index 7cf4cec9daa94f190f63fd94ba149eaee6fdec3d..2c5a073a7ea124ca4bfe02127f951be432807785 100644 (file)
@@ -71,6 +71,9 @@ typedef unsigned char byte;
 #define DNS_INADDR_ARPA "in-addr", "arpa"
 #define DNS_IP6_ARPA "ip6", "arpa"
 
+#define STRINGIFY(x) REALLY_STRINGIFY(x)
+#define REALLY_STRINGIFY(x) #x
+
 #define MAX_POLLFDS  ADNS_POLLFDS_RECOMMENDED
 
 typedef enum {
@@ -126,13 +129,15 @@ typedef struct {
   int revcompwd;
   adns_rrtype rrtype;
   void *(*sockaddr_to_inaddr)(struct sockaddr *sa);
+  int (*sockaddr_equalp)(const struct sockaddr *sa,
+                        const struct sockaddr *sb);
   void (*prefix_mask)(int len, union gen_addr *a);
   int (*guess_len)(const union gen_addr *a);
   int (*matchp)(const union gen_addr *addr,
                const union gen_addr *base, const union gen_addr *mask);
   int (*rev_parsecomp)(const char *p, size_t n);
   void (*rev_mkaddr)(union gen_addr *addr, const byte *ipv);
-  char *(*rev_mkname)(struct sockaddr *sa, char *buf);
+  char *(*rev_mkname)(const struct sockaddr *sa, char *buf);
 } afinfo;
 
 struct afinfo_addr { const afinfo *ai; union gen_addr addr; };
@@ -331,6 +336,8 @@ struct adns__query {
 
 struct query_queue { adns_query head, tail; };
 
+#define MAXUDP 2
+
 struct adns__state {
   adns_initflags iflags;
   adns_logcallbackfn *logfn;
@@ -338,7 +345,9 @@ struct adns__state {
   int configerrno;
   struct query_queue udpw, tcpw, childw, output;
   adns_query forallnext;
-  int nextid, udpsocket, tcpsocket;
+  int nextid, tcpsocket;
+  struct udpsocket { const afinfo *ai; int fd; } udpsocket[MAXUDP];
+  int nudp;
   vbuf tcpsend, tcprecv;
   int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip;
   enum adns__tcpstate {
@@ -354,9 +363,7 @@ struct adns__state {
   struct sigaction stdsigpipe;
   sigset_t stdsigmask;
   struct pollfd pollfds_buf[MAX_POLLFDS];
-  struct server {
-    struct in_addr addr;
-  } servers[MAXSERVERS];
+  adns_rr_addr servers[MAXSERVERS];
   struct sortlist {
     const afinfo *ai;
     union gen_addr base, mask;
@@ -375,6 +382,7 @@ int adns__setnonblock(adns_state ads, int fd); /* => errno value */
 
 /* From general.c: */
 
+const char *adns__sockaddr_ntoa(struct sockaddr *sa, size_t n);
 void adns__vlprintf(adns_state ads, const char *fmt, va_list al);
 void adns__lprintf(adns_state ads, const char *fmt,
                   ...) PRINTFFORMAT(2,3);
index 61f4f67c4b88a43d21273f9c73643062c1e4adca..67dcd1abf4f0e7fbdc988388d7d72fd488374ddf 100644 (file)
 
 static void readconfig(adns_state ads, const char *filename, int warnmissing);
 
-static void addserver(adns_state ads, struct in_addr addr) {
+static const afinfo *const transport_aftab[] = {
+  &adns__inet_afinfo,
+  &adns__inet6_afinfo,
+  0
+};
+
+static const afinfo *find_afinfo(int af)
+{
   int i;
-  struct server *ss;
+
+  for (i = 0; transport_aftab[i]; i++)
+    if (transport_aftab[i]->af == af) return transport_aftab[i];
+  return 0;
+}
+
+static void addserver(adns_state ads, struct sockaddr *sa, int n) {
+  int i;
+  adns_rr_addr *ss;
+  const afinfo *ai;
+
+  ai = find_afinfo(sa->sa_family);
+  if (!ai) {
+    adns__diag(ads,-1,0,
+              "nameserver %s for unknown address family %d ignored",
+              adns__sockaddr_ntoa(sa, n), sa->sa_family);
+  }
   
   for (i=0; i<ads->nservers; i++) {
-    if (ads->servers[i].addr.s_addr == addr.s_addr) {
-      adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr));
+    if (ads->servers[i].addr.sa.sa_family == sa->sa_family &&
+       ai->sockaddr_equalp(sa, &ads->servers[i].addr.sa)) {
+      adns__debug(ads,-1,0,"duplicate nameserver %s ignored",
+                 adns__sockaddr_ntoa(sa, n));
       return;
     }
   }
   
   if (ads->nservers>=MAXSERVERS) {
-    adns__diag(ads,-1,0,"too many nameservers, ignoring %s",inet_ntoa(addr));
+    adns__diag(ads,-1,0,"too many nameservers, ignoring %s",
+              adns__sockaddr_ntoa(sa, n));
     return;
   }
 
   ss= ads->servers+ads->nservers;
-  ss->addr= addr;
+  assert(n <= sizeof(ss->addr));
+  ss->len = n;
+  memcpy(&ss->addr, sa, n);
   ads->nservers++;
 }
 
@@ -105,14 +133,24 @@ static int nextword(const char **bufp_io, const char **word_r, int *l_r) {
 
 static void ccf_nameserver(adns_state ads, const char *fn,
                           int lno, const char *buf) {
-  struct in_addr ia;
-  
-  if (!inet_aton(buf,&ia)) {
-    configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
+  struct addrinfo *ai, ai_hint = { 0 };
+  int err;
+
+  ai_hint.ai_family = AF_UNSPEC;
+  ai_hint.ai_socktype = SOCK_DGRAM;
+  ai_hint.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+  err = getaddrinfo(buf, STRINGIFY(DNS_PORT), &ai_hint, &ai);
+  if (err) {
+    configparseerr(ads,fn,lno,"invalid nameserver address `%s' (%s)",
+                  buf, gai_strerror(err));
     return;
   }
-  adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia));
-  addserver(ads,ia);
+
+  adns__debug(ads,-1,0,"using nameserver %s",
+             adns__sockaddr_ntoa(ai->ai_addr, ai->ai_addrlen));
+  addserver(ads, ai->ai_addr, ai->ai_addrlen);
+  freeaddrinfo(ai);
 }
 
 static void ccf_search(adns_state ads, const char *fn,
@@ -534,7 +572,8 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
   LIST_INIT(ads->output);
   ads->forallnext= 0;
   ads->nextid= 0x311f;
-  ads->udpsocket= ads->tcpsocket= -1;
+  ads->nudp= 0;
+  ads->tcpsocket= -1;
   adns__vbuf_init(&ads->tcpsend);
   adns__vbuf_init(&ads->tcprecv);
   ads->tcprecv_skip= 0;
@@ -554,28 +593,46 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
 }
 
 static int init_finish(adns_state ads) {
-  struct in_addr ia;
+  struct sockaddr_in sin;
   struct protoent *proto;
+  struct udpsocket *udp;
+  int i, j;
   int r;
   
   if (!ads->nservers) {
     if (ads->logfn && ads->iflags & adns_if_debug)
-      adns__lprintf(ads,"adns: no nameservers, using localhost\n");
-    ia.s_addr= htonl(INADDR_LOOPBACK);
-    addserver(ads,ia);
+      adns__lprintf(ads,"adns: no nameservers, using IPv4 localhost\n");
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_port = htons(DNS_PORT);
+    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    addserver(ads,(struct sockaddr *)&sin, sizeof(sin));
   }
 
   proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
-  ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
-  if (ads->udpsocket<0) { r= errno; goto x_free; }
+  ads->nudp = 0;
+  for (i = 0; i < ads->nservers; i++) {
+    for (j = 0; j < ads->nudp; j++) {
+      if (ads->udpsocket[j].ai->af == ads->servers[i].addr.sa.sa_family)
+       goto afmatch;
+    }
 
-  r= adns__setnonblock(ads,ads->udpsocket);
-  if (r) { r= errno; goto x_closeudp; }
+    assert(ads->nudp < MAXUDP);
+    udp = &ads->udpsocket[ads->nudp];
+    udp->ai = find_afinfo(ads->servers[i].addr.sa.sa_family);
+    assert(udp->ai);
+    udp->fd = socket(udp->ai->af,SOCK_DGRAM,proto->p_proto);
+    if (udp->fd < 0) { r= errno; goto x_free; }
+    r= adns__setnonblock(ads,udp->fd);
+    if (r) { r= errno; goto x_closeudp; }
+    ads->nudp++;
+
+  afmatch:;
+  }
   
   return 0;
 
  x_closeudp:
-  close(ads->udpsocket);
+  for (j = 0; j < ads->nudp; j++) close(ads->udpsocket[j].fd);
  x_free:
   free(ads);
   return r;
@@ -682,6 +739,7 @@ int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
 }
 
 void adns_finish(adns_state ads) {
+  int i;
   adns__consistency(ads,0,cc_entex);
   for (;;) {
     if (ads->udpw.head) adns_cancel(ads->udpw.head);
@@ -690,7 +748,7 @@ void adns_finish(adns_state ads) {
     else if (ads->output.head) adns_cancel(ads->output.head);
     else break;
   }
-  close(ads->udpsocket);
+  for (i = 0; i < ads->nudp; i++) close(ads->udpsocket[i].fd);
   if (ads->tcpsocket >= 0) close(ads->tcpsocket);
   adns__vbuf_free(&ads->tcpsend);
   adns__vbuf_free(&ads->tcprecv);
index 7afb90f2bd6777dceb6bcce1642fa94be60079dd..3e5ef5f15c5af2e96a53d7d8f89a4d516298b646 100644 (file)
@@ -250,9 +250,11 @@ static void query_usetcp(adns_query qu, struct timeval now) {
 }
 
 void adns__query_send(adns_query qu, struct timeval now) {
-  struct sockaddr_in servaddr;
-  int serv, r;
+  int serv, r, i;
   adns_state ads;
+  int fd = -1;
+  struct udpsocket *udp;
+  adns_rr_addr *addr;
 
   assert(qu->state == query_tosend);
   if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) {
@@ -265,16 +267,17 @@ void adns__query_send(adns_query qu, struct timeval now) {
     return;
   }
 
-  serv= qu->udpnextserver;
-  memset(&servaddr,0,sizeof(servaddr));
-
   ads= qu->ads;
-  servaddr.sin_family= AF_INET;
-  servaddr.sin_addr= ads->servers[serv].addr;
-  servaddr.sin_port= htons(DNS_PORT);
+  serv= qu->udpnextserver;
+  addr= &ads->servers[serv];
+  for (i = 0; i < ads->nudp; i++) {
+    udp = &ads->udpsocket[i];
+    if (udp->ai->af == addr->addr.sa.sa_family) { fd = udp->fd; break; }
+  }
+  assert(fd >= 0);
   
-  r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0,
-           (const struct sockaddr*)&servaddr,sizeof(servaddr));
+  r= sendto(fd,qu->query_dgram,qu->query_dglen,0,
+           &addr->addr.sa,addr->len);
   if (r<0 && errno == EMSGSIZE) {
     qu->retries= 0;
     query_usetcp(qu,now);