diff -Naur src_150/addrfam.c src/addrfam.c
--- src_150/addrfam.c	2014-10-26 13:42:43 +0200
+++ src/addrfam.c	2016-05-27 19:18:09 +0300
@@ -24,19 +24,22 @@
  *  along with this program; if not, write to the Free Software Foundation.
  */
 
-#include <stdlib.h>
-#include <errno.h>
-#include <limits.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <stddef.h>
-#include <stdbool.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <net/if.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
+# include <stdlib.h>
+# include <errno.h>
+# include <limits.h>
+# include <unistd.h>
+# include <inttypes.h>
+# include <stddef.h>
+# include <stdbool.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <net/if.h>
+#endif
 
 #include "internal.h"
 
@@ -86,6 +89,7 @@
  *
  * Code blocks may not contain , outside parens.
  */
+#ifdef WITH_IPV6
 #define AF_IN_IN6_OTHER(af, for_inet, for_inet6, other)	\
   if ((af) == AF_INET) {					\
     for_inet							\
@@ -119,16 +123,48 @@
 	for_inet6							\
     });									\
   }while(0)
-
+#else
+#define AF_IN_IN6_OTHER(af, for_inet, other)	\
+  if ((af) == AF_INET) {					\
+    for_inet							\
+  } else {							\
+    other							\
+  }
+#define SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, other) \
+  AF_IN_IN6_OTHER((sa)->sa_family, {					\
+      cnst struct sockaddr_in *const sin = SIN(cnst,(sa));		\
+      for_inet								\
+  },									\
+    other								\
+  )
+#define SOCKADDR_IN_IN6(cnst, sa, sin, for_inet)		\
+  SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, {	\
+      unknown_af((sa)->sa_family);					\
+  })
+#define SOCKADDR_IN_IN6_PAIR(cnst, sa, sina, sb, sinb, for_inet) \
+  do{									\
+    assert((sa)->sa_family == (sb)->sa_family);				\
+    SOCKADDR_IN_IN6(cnst, sa, sina, {					\
+        cnst struct sockaddr_in *const sinb = SIN(cnst,(sb));		\
+	for_inet							\
+      });									\
+  }while(0)
+#endif
 int adns__addrs_equal_raw(const struct sockaddr *a,
 			 int bf, const void *b) {
   if (a->sa_family != bf) return 0;
 
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6(const, a, sin, {
     return sin->sin_addr.s_addr == ((const struct in_addr*)b)->s_addr;
   }, {
     return !memcmp(&sin6->sin6_addr, b, sizeof(struct in6_addr));
   });
+#else
+  SOCKADDR_IN_IN6(const, a, sin, {
+    return sin->sin_addr.s_addr == ((const struct in_addr*)b)->s_addr;
+  });
+#endif
 }
 
 int adns__addrs_equal(const adns_sockaddr *a, const adns_sockaddr *b) {
@@ -140,15 +176,22 @@
 			  const struct sockaddr *sb) {
   if (!adns__addrs_equal_raw(sa, sb->sa_family, adns__sockaddr_addr(sb)))
     return 0;
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6_PAIR(const, sa, sina, sb, sinb, {
       return sina->sin_port == sinb->sin_port;
     }, {
       return sina6->sin6_port == sinb6->sin6_port &&
              sina6->sin6_scope_id == sinb6->sin6_scope_id;
     });
+#else
+  SOCKADDR_IN_IN6_PAIR(const, sa, sina, sb, sinb, {
+      return sina->sin_port == sinb->sin_port;
+    });
+#endif
 }
 
 int adns__addr_width(int af) {
+#ifdef WITH_IPV6
   AF_IN_IN6_OTHER(af, {
       return 32;
     }, {
@@ -156,9 +199,17 @@
     }, {
       unknown_af(af);
     });
+#else
+  AF_IN_IN6_OTHER(af, {
+      return 32;
+    }, {
+      unknown_af(af);
+    });
+#endif
 }
 
 void adns__prefix_mask(adns_sockaddr *sa, int len) {
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6(, &sa->sa, sin, {
       assert(len <= 32);
       sin->sin_addr.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len));
@@ -171,9 +222,16 @@
       if (j) m[i++]= (0xff << (8-j)) & 0xff;
       memset(m+i, 0, 16-i);
     });
+#else
+  SOCKADDR_IN_IN6(, &sa->sa, sin, {
+      assert(len <= 32);
+      sin->sin_addr.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len));
+    });
+#endif
 }
 
 int adns__guess_prefix_length(const adns_sockaddr *sa) {
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6(const, &sa->sa, sin, {
       unsigned a= (ntohl(sin->sin_addr.s_addr) >> 24) & 0xff;
       if (a < 128) return 8;
@@ -184,12 +242,22 @@
       (void)sin6;
       return 64;
     });
+#else
+  SOCKADDR_IN_IN6(const, &sa->sa, sin, {
+      unsigned a= (ntohl(sin->sin_addr.s_addr) >> 24) & 0xff;
+      if (a < 128) return 8;
+      else if (a < 192) return 16;
+      else if (a < 224) return 24;
+      else return -1;
+    });
+#endif
 }
 
 int adns__addr_matches(int af, const void *addr,
 		       const adns_sockaddr *base, const adns_sockaddr *mask)
 {
   if (af != base->sa.sa_family) return 0;
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6_PAIR(const, &base->sa, sbase, &mask->sa, smask, {
       const struct in_addr *v4 = addr;
       return (v4->s_addr & smask->sin_addr.s_addr)
@@ -203,29 +271,48 @@
 	if ((a[i] & m[i]) != b[i]) return 0;
       return 1;
     });
+#else
+  SOCKADDR_IN_IN6_PAIR(const, &base->sa, sbase, &mask->sa, smask, {
+      const struct in_addr *v4 = addr;
+      return (v4->s_addr & smask->sin_addr.s_addr)
+	== sbase->sin_addr.s_addr;
+    });
+#endif
 }
 
 const void *adns__sockaddr_addr(const struct sockaddr *sa) {
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6(const, sa, sin, {
       return &sin->sin_addr;
     }, {
       return &sin6->sin6_addr;
     });
+#else
+  SOCKADDR_IN_IN6(const, sa, sin, {
+      return &sin->sin_addr;
+    });
+#endif
 }
 
 void adns__addr_inject(const void *a, adns_sockaddr *sa) {
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6( , &sa->sa, sin, {
       memcpy(&sin->sin_addr, a, sizeof(sin->sin_addr));
     }, {
       memcpy(&sin6->sin6_addr, a, sizeof(sin6->sin6_addr));
     });
+#else
+  SOCKADDR_IN_IN6( , &sa->sa, sin, {
+      memcpy(&sin->sin_addr, a, sizeof(sin->sin_addr));
+    });
+#endif
 }
 
 /*
  * addr2text and text2addr
  */
 
-#define ADDRFAM_DEBUG
+//#define ADDRFAM_DEBUG
 #ifdef ADDRFAM_DEBUG
 static void af_debug_func(const char *fmt, ...) {
   int esave= errno;
@@ -249,12 +336,14 @@
     e==ENOSYS;
 }
 
+#ifdef WITH_IPV6
 static bool addrtext_scope_use_ifname(const struct sockaddr *sa) {
   const struct in6_addr *in6= &SIN6(const,sa)->sin6_addr;
   return
     IN6_IS_ADDR_LINKLOCAL(in6) ||
     IN6_IS_ADDR_MC_LINKLOCAL(in6);
 }
+#endif
 
 static int textaddr_check_qf(adns_queryflags flags) {
   if (flags & ~(adns_queryflags)(adns_qf_addrlit_scope_forbid|
@@ -268,7 +357,9 @@
 int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
 		   struct sockaddr *sa, socklen_t *salen_io) {
   int r, af;
+#ifdef WITH_IPV6
   char copybuf[INET6_ADDRSTRLEN];
+#endif
   const char *parse=text;
   const char *scopestr=0;
   socklen_t needlen;
@@ -282,6 +373,7 @@
   return EINVAL;				\
 }while(0)
 
+#ifdef WITH_IPV6
 #define AFCORE(INETx,SINx,sinx)			\
     af= AF_##INETx;				\
     dst = &SINx(,sa)->sinx##_addr;		\
@@ -310,6 +402,19 @@
     }
 
   }
+#else
+#define AFCORE(INETx,SINx,sinx)			\
+    af= AF_##INETx;				\
+    dst = &SINx(,sa)->sinx##_addr;		\
+    portp = &SINx(,sa)->sinx##_port;		\
+    needlen= sizeof(*SINx(,sa));
+
+  if (!strchr(text, ':')) { /* INET */
+
+    AFCORE(INET,SIN,sin);
+
+  }
+#endif
 
 #undef AFCORE
 
@@ -336,10 +441,11 @@
     assert(r>0);
   }
 
+#ifdef WITH_IPV6
   if (scopestr) {
-    errno=0;
     char *ep;
     unsigned long scope= strtoul(scopestr,&ep,10);
+    errno=0;
     if (errno==ERANGE) INVAL("numeric scope id too large for unsigned long");
     assert(!errno);
     if (!*ep) {
@@ -392,7 +498,7 @@
 
     SIN6(,sa)->sin6_scope_id= scope;
   } /* if (scopestr) */
-
+#endif
   *salen_io = needlen;
   return 0;
 }
@@ -401,6 +507,7 @@
 		   char *buffer, int *buflen_io, int *port_r) {
   const void *src;
   int r, port;
+  const char *ok;
 
   r= textaddr_check_qf(flags);  if (r) return r;
 
@@ -409,6 +516,7 @@
     return ENOSPC;
   }
 
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6_OTHER(const, sa, sin, {
       src= &sin->sin_addr;    port= sin->sin_port;
     }, {
@@ -416,10 +524,18 @@
     }, {
       return EAFNOSUPPORT;
     });
+#else
+  SOCKADDR_IN_IN6_OTHER(const, sa, sin, {
+      src= &sin->sin_addr;    port= sin->sin_port;
+    }, {
+      return EAFNOSUPPORT;
+    });
+#endif
 
-  const char *ok= inet_ntop(sa->sa_family, src, buffer, *buflen_io);
+  ok= inet_ntop(sa->sa_family, src, buffer, *buflen_io);
   assert(ok);
 
+#ifdef WITH_IPV6
   if (sa->sa_family == AF_INET6) {
     uint32_t scope = SIN6(const,sa)->sin6_scope_id;
     if (scope) {
@@ -460,6 +576,7 @@
       af_debug("printed scoped addr `%s'", buffer);
     }
   }
+#endif
 
   if (port_r) *port_r= ntohs(port);
   return 0;
@@ -485,11 +602,15 @@
 			      char **buf_free_r) {
   size_t req;
   char *p;
+#ifdef WITH_IPV6
   unsigned c, y;
-  unsigned long aa;
   const unsigned char *ap;
-  int i, j;
+  int j;
+#endif
+  unsigned long aa;
+  int i;
 
+#ifdef WITH_IPV6
   AF_IN_IN6_OTHER(sa->sa_family, {
       req= 4 * 4;
       if (!zone) zone= "in-addr.arpa";
@@ -499,6 +620,14 @@
     }, {
       return ENOSYS;
     });
+#else
+  AF_IN_IN6_OTHER(sa->sa_family, {
+      req= 4 * 4;
+      if (!zone) zone= "in-addr.arpa";
+    }, {
+      return ENOSYS;
+    });
+#endif
 
   req += strlen(zone) + 1;
   if (req <= bufsz)
@@ -509,6 +638,7 @@
   }
 
   *buf_io= p;
+#ifdef WITH_IPV6
   SOCKADDR_IN_IN6(const, sa, sin, {
       aa= ntohl(sin->sin_addr.s_addr);
       for (i=0; i<4; i++) {
@@ -528,6 +658,16 @@
 	}
       }
     });
+#else
+  SOCKADDR_IN_IN6(const, sa, sin, {
+      aa= ntohl(sin->sin_addr.s_addr);
+      for (i=0; i<4; i++) {
+	p += sprintf(p, "%d", (int)(aa & 0xff));
+	*p++= '.';
+	aa >>= 8;
+      }
+    });
+#endif
 
   strcpy(p, zone);
   return 0;
@@ -563,9 +703,9 @@
 
 static bool revparse_atoi(const char *p, int l, int base,
 			  unsigned max, unsigned *v_r) {
+  unsigned v=0;
   if (l>3) return 0;
   if (l>1 && p[0]=='0') return 0;
-  unsigned v=0;
   while (l-- > 0) {
     int tv;
     int c= ctype_toupper(*p++);
@@ -584,10 +724,10 @@
 static bool revparse_inet(struct revparse_state *rps,
 			  const char *dgram, int nlabels,
 			  adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
-  if (!revparse_check_tail(rps,dgram,nlabels,4,"in-addr")) return 0;
-
   uint32_t a=0;
   int i;
+  if (!revparse_check_tail(rps,dgram,nlabels,4,"in-addr")) return 0;
+
   for (i=3; i>=0; i--) {
     REVPARSE_P_L(i);
     unsigned v;
@@ -601,6 +741,7 @@
   return 1;
 }
 
+#ifdef WITH_IPV6
 static bool revparse_inet6(struct revparse_state *rps,
 			   const char *dgram, int nlabels,
 			   adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
@@ -624,6 +765,7 @@
   addr_r->inet.sin_family= AF_INET6;
   return 1;
 }
+#endif
 
 bool adns__revparse_label(struct revparse_state *rps, int labnum,
 			  const char *dgram, int labstart, int lablen) {
@@ -641,6 +783,9 @@
 			 const char *dgram, int nlabels,
 			 adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
   return
-    revparse_inet(rps,dgram,nlabels,rrtype_r,addr_r) ||
-    revparse_inet6(rps,dgram,nlabels,rrtype_r,addr_r);
+    revparse_inet(rps,dgram,nlabels,rrtype_r,addr_r)
+#ifdef WITH_IPV6
+    || revparse_inet6(rps,dgram,nlabels,rrtype_r,addr_r)
+#endif
+    ;
 }
diff -Naur src_150/adns.h src/adns.h
--- src_150/adns.h	2014-10-26 13:45:58 +0200
+++ src/adns.h	2016-05-27 19:39:15 +0300
@@ -55,6 +55,9 @@
 #ifndef ADNS_H_INCLUDED
 #define ADNS_H_INCLUDED
 
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
 #include <stdio.h>
 #include <stdarg.h>
 
@@ -64,6 +67,7 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include <net/if.h>
+#endif
 
 #ifdef __cplusplus
 extern "C" { /* I really dislike this - iwj. */
@@ -346,11 +350,15 @@
   struct sockaddr_in inet;
 } adns_sockaddr_v4only;
 
+#ifdef WITH_IPV6
 typedef union {
   struct sockaddr sa;
   struct sockaddr_in inet;
   struct sockaddr_in6 inet6;
 } adns_sockaddr;
+#else
+typedef adns_sockaddr_v4only adns_sockaddr;
+#endif
 
 typedef struct {
   int len;
@@ -491,6 +499,9 @@
 int adns_init(adns_state *newstate_r, adns_initflags flags,
 	      FILE *diagfile /*0=>stderr*/);
 
+/* ReactOS addition */
+void adns_addserver(adns_state state, struct sockaddr *server, int salen);
+
 int adns_init_strcfg(adns_state *newstate_r, adns_initflags flags,
 		     FILE *diagfile /*0=>discard*/, const char *configtext);
 
diff -Naur src_150/config.h src/config.h
--- src_150/config.h	1970-01-01 02:00:00 +0200
+++ src/config.h	2013-04-10 18:39:39 +0300
@@ -0,0 +1,106 @@
+/* src/config.h.  Generated automatically by configure.  */
+/* src/config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if inline functions a la GCC are available.  */
+#define HAVE_INLINE 1
+
+/* Define if function attributes a la GCC 2.5 and higher are available.  */
+/* #define HAVE_GNUC25_ATTRIB 1 */
+
+/* Define if constant functions a la GCC 2.5 and higher are available.  */
+/* #define HAVE_GNUC25_CONST 1 */
+
+/* Define if nonreturning functions a la GCC 2.5 and higher are available.  */
+/* #define HAVE_GNUC25_NORETURN 1 */
+
+/* Define if printf-format argument lists a la GCC are available.  */
+/* #define HAVE_GNUC25_PRINTFFORMAT 1 */
+
+/* Define if we want to include rpc/types.h.  Crap BSDs put INADDR_LOOPBACK there. */
+/* #undef HAVEUSE_RPCTYPES_H */
+
+/* Define if you have the poll function.  */
+/* #define HAVE_POLL 1 */
+
+/* Define if you have the <sys/select.h> header file.  */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define if you have the nsl library (-lnsl).  */
+/* #undef HAVE_LIBNSL */
+
+/* Define if you have the socket library (-lsocket).  */
+/* #undef HAVE_LIBSOCKET */
+
+/* Use the definitions: */
+
+#ifndef HAVE_INLINE
+#define inline
+#endif
+
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+/* kludge it up */
+struct pollfd { int fd; short events; short revents; };
+#define POLLIN  1
+#define POLLPRI 2
+#define POLLOUT 4
+#endif
+
+/* GNU C attributes. */
+#ifndef FUNCATTR
+#ifdef HAVE_GNUC25_ATTRIB
+#define FUNCATTR(x) __attribute__(x)
+#else
+#define FUNCATTR(x)
+#endif
+#endif
+
+/* GNU C printf formats, or null. */
+#ifndef ATTRPRINTF
+#ifdef HAVE_GNUC25_PRINTFFORMAT
+#define ATTRPRINTF(si,tc) format(printf,si,tc)
+#else
+#define ATTRPRINTF(si,tc)
+#endif
+#endif
+#ifndef PRINTFFORMAT
+#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc)))
+#endif
+
+/* GNU C nonreturning functions, or null. */
+#ifndef ATTRNORETURN
+#ifdef HAVE_GNUC25_NORETURN
+#define ATTRNORETURN noreturn
+#else
+#define ATTRNORETURN
+#endif
+#endif
+#ifndef NONRETURNING
+#define NONRETURNING FUNCATTR((ATTRNORETURN))
+#endif
+
+/* Combination of both the above. */
+#ifndef NONRETURNPRINTFFORMAT
+#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN))
+#endif
+
+/* GNU C constant functions, or null. */
+#ifndef ATTRCONST
+#ifdef HAVE_GNUC25_CONST
+#define ATTRCONST const
+#else
+#define ATTRCONST
+#endif
+#endif
+#ifndef CONSTANT
+#define CONSTANT FUNCATTR((ATTRCONST))
+#endif
+
+#ifdef HAVEUSE_RPCTYPES_H
+#include <rpc/types.h>
+#endif
+
+#if !defined(ADNS_JGAA_WIN32) && defined(HAVE_SYS_SELECT_H)
+#include <sys/select.h>
+#endif
diff -Naur src_150/event.c src/event.c
--- src_150/event.c	2014-10-20 02:07:03 +0300
+++ src/event.c	2016-05-27 16:37:41 +0300
@@ -28,14 +28,18 @@
 
 #include <errno.h>
 #include <stdlib.h>
-#include <unistd.h>
 
-#include <sys/types.h>
-#include <sys/time.h>
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/time.h>
+# include <netdb.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
 
 #include "internal.h"
 #include "tvarith.h"
@@ -43,7 +47,7 @@
 /* TCP connection management. */
 
 static void tcp_close(adns_state ads) {
-  close(ads->tcpsocket);
+  adns_socket_close(ads->tcpsocket);
   ads->tcpsocket= -1;
   ads->tcprecv.used= ads->tcprecv_skip= ads->tcpsend.used= 0;
 }
@@ -130,7 +134,7 @@
     if (r) {
       adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:"
 		 " %s",strerror(r));
-      close(fd);
+      adns_socket_close(fd);
       return;
     }
     r= connect(fd,&addr->addr.sa,addr->len);
@@ -378,7 +382,7 @@
       if (!adns__vbuf_ensure(&ads->tcprecv,want)) { r= ENOMEM; goto xit; }
       assert(ads->tcprecv.used <= ads->tcprecv.avail);
       if (ads->tcprecv.used == ads->tcprecv.avail) continue;
-      r= read(ads->tcpsocket,
+      r= adns_socket_read(ads->tcpsocket,
 	      ads->tcprecv.buf+ads->tcprecv.used,
 	      ads->tcprecv.avail-ads->tcprecv.used);
       if (r>0) {
@@ -444,7 +448,7 @@
     assert(ads->tcprecv_skip==0);
     for (;;) {
       if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; }
-      r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
+      r= adns_socket_read(ads->tcpsocket,&ads->tcprecv.buf,1);
       if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
 	tcp_connected(ads,*now);
 	r= 0; goto xit;
@@ -462,7 +466,7 @@
     if (fd != ads->tcpsocket) break;
     while (ads->tcpsend.used) {
       adns__sigpipe_protect(ads);
-      r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
+      r= adns_socket_write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
       adns__sigpipe_unprotect(ads);
       if (r<0) {
 	if (errno==EINTR) continue;
diff -Naur src_150/general.c src/general.c
--- src_150/general.c	2014-10-20 02:07:03 +0300
+++ src/general.c	2016-05-27 16:41:39 +0300
@@ -26,12 +26,16 @@
  */
 
 #include <stdlib.h>
-#include <unistd.h>
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
 
 #include "internal.h"
 
@@ -355,8 +359,10 @@
 }
 
 /* SIGPIPE protection. */
+/* Not required under Win32 with MSVC */
 
 void adns__sigpipe_protect(adns_state ads) {
+#ifndef ADNS_JGAA_WIN32
   sigset_t toblock;
   struct sigaction sa;
   int r;
@@ -372,13 +378,16 @@
   
   r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
   r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
+#endif
 }
 
 void adns__sigpipe_unprotect(adns_state ads) {
+#ifndef ADNS_JGAA_WIN32
   int r;
 
   if (ads->iflags & adns_if_nosigpipe) return;
 
   r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
   r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
+#endif
 }
diff -Naur src_150/internal.h src/internal.h
--- src_150/internal.h	2014-10-26 13:45:58 +0200
+++ src/internal.h	2016-05-25 20:25:52 +0300
@@ -30,18 +30,26 @@
 #define ADNS_INTERNAL_H_INCLUDED
 
 #include "config.h"
+
+#ifndef ADNS_JGAA_WIN32
 typedef unsigned char byte;
+#endif
 
 #include <stdarg.h>
 #include <assert.h>
-#include <unistd.h>
+
+#ifndef ADNS_JGAA_WIN32
+# include <unistd.h>
+#endif
 #include <signal.h>
 #include <errno.h>
 #include <string.h>
 #include <stdlib.h>
-#include <stdbool.h>
 
-#include <sys/time.h>
+#ifndef ADNS_JGAA_WIN32
+#  include <stdbool.h>
+#  include <sys/time.h>
+#endif
 
 #define ADNS_FEATURE_MANYAF
 #include "adns.h"
@@ -386,8 +394,10 @@
    * we are idle (ie, tcpw queue is empty), in which case it is the
    * absolute time when we will close the connection.
    */
+#ifndef ADNS_JGAA_WIN32
   struct sigaction stdsigpipe;
   sigset_t stdsigmask;
+#endif
   struct pollfd pollfds_buf[MAX_POLLFDS];
   adns_rr_addr servers[MAXSERVERS];
   struct sortlist {
diff -Naur src_150/query.c src/query.c
--- src_150/query.c	2014-10-26 13:42:43 +0200
+++ src/query.c	2016-05-25 20:33:39 +0300
@@ -26,13 +26,15 @@
  *  along with this program; if not, write to the Free Software Foundation.
  */
 
-#include "internal.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
 
-#include <sys/time.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
+# include <stdlib.h>
+# include <unistd.h>
+# include <errno.h>
+# include <sys/time.h>
+#endif
 
 #include "internal.h"
 
diff -Naur src_150/setup.c src/setup.c
--- src_150/setup.c	2014-10-26 14:03:40 +0200
+++ src/setup.c	2016-05-27 19:31:49 +0300
@@ -25,17 +25,21 @@
  *  along with this program; if not, write to the Free Software Foundation.
  */
 
-#include <stdlib.h>
-#include <errno.h>
-#include <limits.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <sys/types.h>
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+# include <iphlpapi.h>
+#else
+# include <stdlib.h>
+# include <errno.h>
+# include <limits.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/types.h>
+# include <netdb.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
 
 #include "internal.h"
 
@@ -591,12 +595,17 @@
 
 
 int adns__setnonblock(adns_state ads, int fd) {
+#ifdef ADNS_JGAA_WIN32
+   unsigned long Val = 1;
+   return (ioctlsocket (fd, FIONBIO, &Val) == 0) ? 0 : -1;
+#else
   int r;
   
   r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
   r |= O_NONBLOCK;
   r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
   return 0;
+#endif
 }
 
 static int init_begin(adns_state *ads_r, adns_initflags flags,
@@ -677,7 +686,7 @@
   return 0;
 
  x_closeudp:
-  for (i=0; i<ads->nudpsockets; i++) close(ads->udpsockets[i].fd);
+  for (i=0; i<ads->nudpsockets; i++) adns_socket_close(ads->udpsockets[i].fd);
  x_free:
   free(ads);
   return r;
@@ -794,8 +803,8 @@
     else if (ads->intdone.head) adns__cancel(ads->output.head);
     else break;
   }
-  for (i=0; i<ads->nudpsockets; i++) close(ads->udpsockets[i].fd);
-  if (ads->tcpsocket >= 0) close(ads->tcpsocket);
+  for (i=0; i<ads->nudpsockets; i++) adns_socket_close(ads->udpsockets[i].fd);
+  if (ads->tcpsocket >= 0) adns_socket_close(ads->tcpsocket);
   adns__vbuf_free(&ads->tcpsend);
   adns__vbuf_free(&ads->tcprecv);
   freesearchlist(ads);
@@ -841,3 +850,8 @@
   if (context_r) *context_r= qu->ctx.ext;
   return qu;
 }
+
+/* ReactOS addition */
+void adns_addserver(adns_state ads, struct sockaddr *server, int salen) {
+    addserver(ads, server, salen);
+}
diff -Naur src_150/src.patch src/src.patch
--- src_150/src.patch	1970-01-01 02:00:00 +0200
+++ src/src.patch	2016-05-27 19:08:05 +0300
@@ -0,0 +1,7995 @@
+Index: addrfam.c
+===================================================================
+--- addrfam.c	(revision 0)
++++ addrfam.c	(working copy)
+@@ -0,0 +1,791 @@
++/*
++ * addrfam.c
++ * - address-family specific code
++ */
++/*
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; either version 3, or (at your option)
++ *  any later version.
++ *  
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *  
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program; if not, write to the Free Software Foundation.
++ */
++
++#ifdef ADNS_JGAA_WIN32
++# include "adns_win32.h"
++#else
++# include <stdlib.h>
++# include <errno.h>
++# include <limits.h>
++# include <unistd.h>
++# include <inttypes.h>
++# include <stddef.h>
++# include <stdbool.h>
++# include <sys/types.h>
++# include <sys/socket.h>
++# include <netinet/in.h>
++# include <arpa/inet.h>
++# include <net/if.h>
++#endif
++
++#include "internal.h"
++
++/*
++ * General address-family operations.
++ */
++
++#define SIN(cnst, sa) ((void)(sa)->sa_family, (cnst struct sockaddr_in *)(sa))
++#define SIN6(cnst, sa) ((void)(sa)->sa_family, (cnst struct sockaddr_in6 *)(sa))
++
++static void unknown_af(int af) NONRETURNING;
++static void unknown_af(int af) {
++  fprintf(stderr, "ADNS INTERNAL: unknown address family %d\n", af);
++  abort();
++}
++
++/*
++ * SOCKADDR_IN_IN6(CNST, struct sockaddr *sa, SIN, {
++ *     // struct sockaddr_in *const SIN; // implicitly
++ *     code for inet;
++ *   }, {
++ *     // struct sockaddr_in6 *const SIN6; // implicitly
++ *     code for inet6;
++ *   })
++ *
++ * SOCKADDR_IN_IN6_PAIR(CNST, struct sockaddr *sa, SINA,
++ *                            struct sockaddr *sb, SINB, {
++ *     // struct sockaddr_in *const SINA; // implicitly
++ *     // struct sockaddr_in *const SINB; // implicitly
++ *     code for inet;
++ *   },{
++ *     // struct sockaddr_in6 *const SINA6; // implicitly
++ *     // struct sockaddr_in6 *const SINB6; // implicitly
++ *     code for inet6;
++ *   });
++ *
++ * SOCKADDR_IN_IN6_OTHER(CNST, struct sockaddr *sa, SIN, { in }, { in6 }, {
++ *     code for other address family
++ *   })
++ *
++ * AF_IN_IN6_OTHER(af, { in }, { in6 }, { other })
++ *
++ * Executes the first or second block according to the AF in sa.  CNST
++ * may be `const' or empty.  For _PAIR, sa and sb must be same AF.
++ *
++ * All except _OTHER handle unknown AFs with unknown_af.
++ *
++ * Code blocks may not contain , outside parens.
++ */
++#ifdef WITH_IPV6
++#define AF_IN_IN6_OTHER(af, for_inet, for_inet6, other)	\
++  if ((af) == AF_INET) {					\
++    for_inet							\
++  } else if ((af) == AF_INET6) {				\
++    for_inet6							\
++  } else {							\
++    other							\
++  }
++#define SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, for_inet6, other) \
++  AF_IN_IN6_OTHER((sa)->sa_family, {					\
++      cnst struct sockaddr_in *const sin = SIN(cnst,(sa));		\
++      for_inet								\
++  }, {									\
++      cnst struct sockaddr_in6 *const sin##6 = SIN6(cnst,(sa));		\
++      for_inet6								\
++  },									\
++    other								\
++  )
++#define SOCKADDR_IN_IN6(cnst, sa, sin, for_inet, for_inet6)		\
++  SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, for_inet6, {	\
++      unknown_af((sa)->sa_family);					\
++  })
++#define SOCKADDR_IN_IN6_PAIR(cnst, sa, sina, sb, sinb, for_inet, for_inet6) \
++  do{									\
++    assert((sa)->sa_family == (sb)->sa_family);				\
++    SOCKADDR_IN_IN6(cnst, sa, sina, {					\
++        cnst struct sockaddr_in *const sinb = SIN(cnst,(sb));		\
++	for_inet							\
++      }, {								\
++        cnst struct sockaddr_in6 *const sinb##6 = SIN6(cnst,(sb));	\
++	for_inet6							\
++    });									\
++  }while(0)
++#else
++#define AF_IN_IN6_OTHER(af, for_inet, other)	\
++  if ((af) == AF_INET) {					\
++    for_inet							\
++  } else {							\
++    other							\
++  }
++#define SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, other) \
++  AF_IN_IN6_OTHER((sa)->sa_family, {					\
++      cnst struct sockaddr_in *const sin = SIN(cnst,(sa));		\
++      for_inet								\
++  },									\
++    other								\
++  )
++#define SOCKADDR_IN_IN6(cnst, sa, sin, for_inet)		\
++  SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, {	\
++      unknown_af((sa)->sa_family);					\
++  })
++#define SOCKADDR_IN_IN6_PAIR(cnst, sa, sina, sb, sinb, for_inet) \
++  do{									\
++    assert((sa)->sa_family == (sb)->sa_family);				\
++    SOCKADDR_IN_IN6(cnst, sa, sina, {					\
++        cnst struct sockaddr_in *const sinb = SIN(cnst,(sb));		\
++	for_inet							\
++      });									\
++  }while(0)
++#endif
++int adns__addrs_equal_raw(const struct sockaddr *a,
++			 int bf, const void *b) {
++  if (a->sa_family != bf) return 0;
++
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6(const, a, sin, {
++    return sin->sin_addr.s_addr == ((const struct in_addr*)b)->s_addr;
++  }, {
++    return !memcmp(&sin6->sin6_addr, b, sizeof(struct in6_addr));
++  });
++#else
++  SOCKADDR_IN_IN6(const, a, sin, {
++    return sin->sin_addr.s_addr == ((const struct in_addr*)b)->s_addr;
++  });
++#endif
++}
++
++int adns__addrs_equal(const adns_sockaddr *a, const adns_sockaddr *b) {
++  return adns__addrs_equal_raw(&a->sa, b->sa.sa_family,
++			       adns__sockaddr_addr(&b->sa));
++}
++
++int adns__sockaddrs_equal(const struct sockaddr *sa,
++			  const struct sockaddr *sb) {
++  if (!adns__addrs_equal_raw(sa, sb->sa_family, adns__sockaddr_addr(sb)))
++    return 0;
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6_PAIR(const, sa, sina, sb, sinb, {
++      return sina->sin_port == sinb->sin_port;
++    }, {
++      return sina6->sin6_port == sinb6->sin6_port &&
++             sina6->sin6_scope_id == sinb6->sin6_scope_id;
++    });
++#else
++  SOCKADDR_IN_IN6_PAIR(const, sa, sina, sb, sinb, {
++      return sina->sin_port == sinb->sin_port;
++    });
++#endif
++}
++
++int adns__addr_width(int af) {
++#ifdef WITH_IPV6
++  AF_IN_IN6_OTHER(af, {
++      return 32;
++    }, {
++      return 128;
++    }, {
++      unknown_af(af);
++    });
++#else
++  AF_IN_IN6_OTHER(af, {
++      return 32;
++    }, {
++      unknown_af(af);
++    });
++#endif
++}
++
++void adns__prefix_mask(adns_sockaddr *sa, int len) {
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6(, &sa->sa, sin, {
++      assert(len <= 32);
++      sin->sin_addr.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len));
++    }, {
++      int i= len/8;
++      int j= len%8;
++      unsigned char *m= sin6->sin6_addr.s6_addr;
++      assert(len <= 128);
++      memset(m, 0xff, i);
++      if (j) m[i++]= (0xff << (8-j)) & 0xff;
++      memset(m+i, 0, 16-i);
++    });
++#else
++  SOCKADDR_IN_IN6(, &sa->sa, sin, {
++      assert(len <= 32);
++      sin->sin_addr.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len));
++    });
++#endif
++}
++
++int adns__guess_prefix_length(const adns_sockaddr *sa) {
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6(const, &sa->sa, sin, {
++      unsigned a= (ntohl(sin->sin_addr.s_addr) >> 24) & 0xff;
++      if (a < 128) return 8;
++      else if (a < 192) return 16;
++      else if (a < 224) return 24;
++      else return -1;
++    }, {
++      (void)sin6;
++      return 64;
++    });
++#else
++  SOCKADDR_IN_IN6(const, &sa->sa, sin, {
++      unsigned a= (ntohl(sin->sin_addr.s_addr) >> 24) & 0xff;
++      if (a < 128) return 8;
++      else if (a < 192) return 16;
++      else if (a < 224) return 24;
++      else return -1;
++    });
++#endif
++}
++
++int adns__addr_matches(int af, const void *addr,
++		       const adns_sockaddr *base, const adns_sockaddr *mask)
++{
++  if (af != base->sa.sa_family) return 0;
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6_PAIR(const, &base->sa, sbase, &mask->sa, smask, {
++      const struct in_addr *v4 = addr;
++      return (v4->s_addr & smask->sin_addr.s_addr)
++	== sbase->sin_addr.s_addr;
++    }, {
++      int i;
++      const char *a= addr;
++      const char *b= sbase6->sin6_addr.s6_addr;
++      const char *m= smask6->sin6_addr.s6_addr;
++      for (i = 0; i < 16; i++)
++	if ((a[i] & m[i]) != b[i]) return 0;
++      return 1;
++    });
++#else
++  SOCKADDR_IN_IN6_PAIR(const, &base->sa, sbase, &mask->sa, smask, {
++      const struct in_addr *v4 = addr;
++      return (v4->s_addr & smask->sin_addr.s_addr)
++	== sbase->sin_addr.s_addr;
++    });
++#endif
++}
++
++const void *adns__sockaddr_addr(const struct sockaddr *sa) {
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6(const, sa, sin, {
++      return &sin->sin_addr;
++    }, {
++      return &sin6->sin6_addr;
++    });
++#else
++  SOCKADDR_IN_IN6(const, sa, sin, {
++      return &sin->sin_addr;
++    });
++#endif
++}
++
++void adns__addr_inject(const void *a, adns_sockaddr *sa) {
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6( , &sa->sa, sin, {
++      memcpy(&sin->sin_addr, a, sizeof(sin->sin_addr));
++    }, {
++      memcpy(&sin6->sin6_addr, a, sizeof(sin6->sin6_addr));
++    });
++#else
++  SOCKADDR_IN_IN6( , &sa->sa, sin, {
++      memcpy(&sin->sin_addr, a, sizeof(sin->sin_addr));
++    });
++#endif
++}
++
++/*
++ * addr2text and text2addr
++ */
++
++//#define ADDRFAM_DEBUG
++#ifdef ADDRFAM_DEBUG
++static void af_debug_func(const char *fmt, ...) {
++  int esave= errno;
++  va_list al;
++  va_start(al,fmt);
++  vfprintf(stderr,fmt,al);
++  va_end(al);
++  errno= esave;
++}
++# define af_debug(fmt,...) \
++  (af_debug_func("%s: " fmt "\n", __func__, __VA_ARGS__))
++#else
++# define af_debug(fmt,...) ((void)("" fmt "", __VA_ARGS__))
++#endif
++
++static bool addrtext_our_errno(int e) {
++  return
++    e==EAFNOSUPPORT ||
++    e==EINVAL ||
++    e==ENOSPC ||
++    e==ENOSYS;
++}
++
++#ifdef WITH_IPV6
++static bool addrtext_scope_use_ifname(const struct sockaddr *sa) {
++  const struct in6_addr *in6= &SIN6(const,sa)->sin6_addr;
++  return
++    IN6_IS_ADDR_LINKLOCAL(in6) ||
++    IN6_IS_ADDR_MC_LINKLOCAL(in6);
++}
++#endif
++
++static int textaddr_check_qf(adns_queryflags flags) {
++  if (flags & ~(adns_queryflags)(adns_qf_addrlit_scope_forbid|
++				 adns_qf_addrlit_scope_numeric|
++				 adns_qf_addrlit_ipv4_quadonly|
++				 0x40000000))
++    return ENOSYS;
++  return 0;
++}
++
++int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
++		   struct sockaddr *sa, socklen_t *salen_io) {
++  int r, af;
++#ifdef WITH_IPV6
++  char copybuf[INET6_ADDRSTRLEN];
++#endif
++  const char *parse=text;
++  const char *scopestr=0;
++  socklen_t needlen;
++  void *dst;
++  uint16_t *portp;
++
++  r= textaddr_check_qf(flags);  if (r) return r;
++
++#define INVAL(how) do{				\
++  af_debug("invalid: %s: `%s'", how, text);	\
++  return EINVAL;				\
++}while(0)
++
++#ifdef WITH_IPV6
++#define AFCORE(INETx,SINx,sinx)			\
++    af= AF_##INETx;				\
++    dst = &SINx(,sa)->sinx##_addr;		\
++    portp = &SINx(,sa)->sinx##_port;		\
++    needlen= sizeof(*SINx(,sa));
++
++  if (!strchr(text, ':')) { /* INET */
++
++    AFCORE(INET,SIN,sin);
++
++  } else { /* INET6 */
++
++    AFCORE(INET6,SIN6,sin6);
++
++    const char *percent= strchr(text, '%');
++    if (percent) {
++      ptrdiff_t lhslen = percent - text;
++      if (lhslen >= INET6_ADDRSTRLEN) INVAL("scoped addr lhs too long");
++      memcpy(copybuf, text, lhslen);
++      copybuf[lhslen]= 0;
++
++      parse= copybuf;
++      scopestr= percent+1;
++
++      af_debug("will parse scoped addr `%s' %% `%s'", parse, scopestr);
++    }
++
++  }
++#else
++#define AFCORE(INETx,SINx,sinx)			\
++    af= AF_##INETx;				\
++    dst = &SINx(,sa)->sinx##_addr;		\
++    portp = &SINx(,sa)->sinx##_port;		\
++    needlen= sizeof(*SINx(,sa));
++
++  if (!strchr(text, ':')) { /* INET */
++
++    AFCORE(INET,SIN,sin);
++
++  }
++#endif
++
++#undef AFCORE
++
++  if (scopestr && (flags & adns_qf_addrlit_scope_forbid))
++    INVAL("scoped addr but _scope_forbid");
++
++  if (*salen_io < needlen) {
++    *salen_io = needlen;
++    return ENOSPC;
++  }
++
++  memset(sa, 0, needlen);
++
++  sa->sa_family= af;
++  *portp = htons(port);
++
++  if (af == AF_INET && !(flags & adns_qf_addrlit_ipv4_quadonly)) {
++    /* we have to use inet_aton to deal with non-dotted-quad literals */
++    int r= inet_aton(parse,&SIN(,sa)->sin_addr);
++    if (!r) INVAL("inet_aton rejected");
++  } else {
++    int r= inet_pton(af,parse,dst);
++    if (!r) INVAL("inet_pton rejected");
++    assert(r>0);
++  }
++
++#ifdef WITH_IPV6
++  if (scopestr) {
++    char *ep;
++    unsigned long scope= strtoul(scopestr,&ep,10);
++    errno=0;
++    if (errno==ERANGE) INVAL("numeric scope id too large for unsigned long");
++    assert(!errno);
++    if (!*ep) {
++      if (scope > ~(uint32_t)0)
++	INVAL("numeric scope id too large for uint32_t");
++    } else { /* !!*ep */
++      if (flags & adns_qf_addrlit_scope_numeric)
++	INVAL("non-numeric scope but _scope_numeric");
++      if (!addrtext_scope_use_ifname(sa)) {
++	af_debug("cannot convert non-numeric scope"
++		 " in non-link-local addr `%s'", text);
++	return ENOSYS;
++      }
++      errno= 0;
++      scope= if_nametoindex(scopestr);
++      if (!scope) {
++	/* RFC3493 says "No errors are defined".  It's not clear
++	 * whether that is supposed to mean if_nametoindex "can't
++	 * fail" (other than by the supplied name not being that of an
++	 * interface) which seems unrealistic, or that it conflates
++	 * all its errors together by failing to set errno, or simply
++	 * that they didn't bother to document the errors.
++	 *
++	 * glibc, FreeBSD and OpenBSD all set errno (to ENXIO when
++	 * appropriate).  See Debian bug #749349.
++	 *
++	 * We attempt to deal with this by clearing errno to start
++	 * with, and then perhaps mapping the results. */
++	af_debug("if_nametoindex rejected scope name (errno=%s)",
++		 strerror(errno));
++	if (errno==0) {
++	  return ENXIO;
++	} else if (addrtext_our_errno(errno)) {
++	  /* we use these for other purposes, urgh. */
++	  perror("adns: adns_text2addr: if_nametoindex"
++		 " failed with unexpected error");
++	  return EIO;
++	} else {
++	  return errno;
++	}
++      } else { /* ix>0 */
++	if (scope > ~(uint32_t)0) {
++	  fprintf(stderr,"adns: adns_text2addr: if_nametoindex"
++		  " returned an interface index >=2^32 which will not fit"
++		  " in sockaddr_in6.sin6_scope_id");
++	  return EIO;
++	}
++      }
++    } /* else; !!*ep */
++
++    SIN6(,sa)->sin6_scope_id= scope;
++  } /* if (scopestr) */
++#endif
++  *salen_io = needlen;
++  return 0;
++}
++
++int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
++		   char *buffer, int *buflen_io, int *port_r) {
++  const void *src;
++  int r, port;
++  const char *ok;
++
++  r= textaddr_check_qf(flags);  if (r) return r;
++
++  if (*buflen_io < ADNS_ADDR2TEXT_BUFLEN) {
++    *buflen_io = ADNS_ADDR2TEXT_BUFLEN;
++    return ENOSPC;
++  }
++
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6_OTHER(const, sa, sin, {
++      src= &sin->sin_addr;    port= sin->sin_port;
++    }, {
++      src= &sin6->sin6_addr;  port= sin6->sin6_port;
++    }, {
++      return EAFNOSUPPORT;
++    });
++#else
++  SOCKADDR_IN_IN6_OTHER(const, sa, sin, {
++      src= &sin->sin_addr;    port= sin->sin_port;
++    }, {
++      return EAFNOSUPPORT;
++    });
++#endif
++
++  ok= inet_ntop(sa->sa_family, src, buffer, *buflen_io);
++  assert(ok);
++
++#ifdef WITH_IPV6
++  if (sa->sa_family == AF_INET6) {
++    uint32_t scope = SIN6(const,sa)->sin6_scope_id;
++    if (scope) {
++      if (flags & adns_qf_addrlit_scope_forbid)
++	return EINVAL;
++      int scopeoffset = strlen(buffer);
++      int remain = *buflen_io - scopeoffset;
++      char *scopeptr =  buffer + scopeoffset;
++      assert(remain >= IF_NAMESIZE+1/*%*/);
++      *scopeptr++= '%'; remain--;
++      bool parsedname = 0;
++      af_debug("will print scoped addr `%.*s' %% %"PRIu32"",
++	       scopeoffset,buffer, scope);
++      if (scope <= UINT_MAX /* so we can pass it to if_indextoname */
++	  && !(flags & adns_qf_addrlit_scope_numeric)
++	  && addrtext_scope_use_ifname(sa)) {
++	parsedname = if_indextoname(scope, scopeptr);
++	if (!parsedname) {
++	  af_debug("if_indextoname rejected scope (errno=%s)",
++		   strerror(errno));
++	  if (errno==ENXIO) {
++	    /* fair enough, show it as a number then */
++	  } else if (addrtext_our_errno(errno)) {
++	    /* we use these for other purposes, urgh. */
++	    perror("adns: adns_addr2text: if_indextoname"
++		   " failed with unexpected error");
++	    return EIO;
++	  } else {
++	    return errno;
++	  }
++	}
++      }
++      if (!parsedname) {
++	int r = snprintf(scopeptr, remain,
++			 "%"PRIu32"", scope);
++	assert(r < *buflen_io - scopeoffset);
++      }
++      af_debug("printed scoped addr `%s'", buffer);
++    }
++  }
++#endif
++
++  if (port_r) *port_r= ntohs(port);
++  return 0;
++}
++
++char *adns__sockaddr_ntoa(const struct sockaddr *sa, char *buf) {
++  int err;
++  int len= ADNS_ADDR2TEXT_BUFLEN;
++
++  err= adns_addr2text(sa, 0, buf, &len, 0);
++  if (err == EIO)
++    err= adns_addr2text(sa, adns_qf_addrlit_scope_numeric, buf, &len, 0);
++  assert(!err);
++  return buf;
++}
++
++/*
++ * Reverse-domain parsing and construction.
++ */
++
++int adns__make_reverse_domain(const struct sockaddr *sa, const char *zone,
++			      char **buf_io, size_t bufsz,
++			      char **buf_free_r) {
++  size_t req;
++  char *p;
++#ifdef WITH_IPV6
++  unsigned c, y;
++  const unsigned char *ap;
++  int j;
++#endif
++  unsigned long aa;
++  int i;
++
++#ifdef WITH_IPV6
++  AF_IN_IN6_OTHER(sa->sa_family, {
++      req= 4 * 4;
++      if (!zone) zone= "in-addr.arpa";
++    }, {
++      req = 2 * 32;
++      if (!zone) zone= "ip6.arpa";
++    }, {
++      return ENOSYS;
++    });
++#else
++  AF_IN_IN6_OTHER(sa->sa_family, {
++      req= 4 * 4;
++      if (!zone) zone= "in-addr.arpa";
++    }, {
++      return ENOSYS;
++    });
++#endif
++
++  req += strlen(zone) + 1;
++  if (req <= bufsz)
++    p= *buf_io;
++  else {
++    p= malloc(req); if (!p) return errno;
++    *buf_free_r = p;
++  }
++
++  *buf_io= p;
++#ifdef WITH_IPV6
++  SOCKADDR_IN_IN6(const, sa, sin, {
++      aa= ntohl(sin->sin_addr.s_addr);
++      for (i=0; i<4; i++) {
++	p += sprintf(p, "%d", (int)(aa & 0xff));
++	*p++= '.';
++	aa >>= 8;
++      }
++    }, {
++      ap= sin6->sin6_addr.s6_addr + 16;
++      for (i=0; i<16; i++) {
++	c= *--ap;
++	for (j=0; j<2; j++) {
++	  y= c & 0xf;
++	  *p++= (y < 10) ? y + '0' : y - 10 + 'a';
++	  c >>= 4;
++	  *p++= '.';
++	}
++      }
++    });
++#else
++  SOCKADDR_IN_IN6(const, sa, sin, {
++      aa= ntohl(sin->sin_addr.s_addr);
++      for (i=0; i<4; i++) {
++	p += sprintf(p, "%d", (int)(aa & 0xff));
++	*p++= '.';
++	aa >>= 8;
++      }
++    });
++#endif
++
++  strcpy(p, zone);
++  return 0;
++}
++
++
++#define REVPARSE_P_L(labnum)			\
++  const char *p= dgram + rps->labstart[labnum];	\
++  int l= rps->lablen[labnum]
++  /*
++   * REVPARSE_P_L(int labnum);
++   *   expects:
++   *     const char *dgram;
++   *     const struct revparse_state *rps;
++   *   produces:
++   *     const char *p; // start of label labnum in dgram
++   *     int l; // length of label in dgram
++   */
++
++static bool revparse_check_tail(struct revparse_state *rps,
++				const char *dgram, int nlabels,
++				int bodylen, const char *inarpa) {
++  int i;
++
++  if (nlabels != bodylen+2) return 0;
++  for (i=0; i<2; i++) {
++    REVPARSE_P_L(bodylen+i);
++    const char *want= !i ? inarpa : "arpa";
++    if (!adns__labels_equal(p,l, want,strlen(want))) return 0;
++  }
++  return 1;
++}
++
++static bool revparse_atoi(const char *p, int l, int base,
++			  unsigned max, unsigned *v_r) {
++  unsigned v=0;
++  if (l>3) return 0;
++  if (l>1 && p[0]=='0') return 0;
++  while (l-- > 0) {
++    int tv;
++    int c= ctype_toupper(*p++);
++    if ('0'<=c && c<='9') tv = c-'0';
++    else if ('A'<=c && c<='Z') tv = c-'A'+10;
++    else return 0;
++    if (tv >= base) return 0;
++    v *= base;
++    v += tv;
++  }
++  if (v>max) return 0;
++  *v_r= v;
++  return 1;
++}
++
++static bool revparse_inet(struct revparse_state *rps,
++			  const char *dgram, int nlabels,
++			  adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
++  uint32_t a=0;
++  int i;
++  if (!revparse_check_tail(rps,dgram,nlabels,4,"in-addr")) return 0;
++
++  for (i=3; i>=0; i--) {
++    REVPARSE_P_L(i);
++    unsigned v;
++    if (!revparse_atoi(p,l,10,255,&v)) return 0;
++    a <<= 8;
++    a |= v;
++  }
++  *rrtype_r= adns_r_a;
++  addr_r->inet.sin_family= AF_INET;
++  addr_r->inet.sin_addr.s_addr= htonl(a);
++  return 1;
++}
++
++#ifdef WITH_IPV6
++static bool revparse_inet6(struct revparse_state *rps,
++			   const char *dgram, int nlabels,
++			   adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
++  if (!revparse_check_tail(rps,dgram,nlabels,32,"ip6")) return 0;
++
++  int i, j;
++  memset(addr_r,0,sizeof(*addr_r));
++  unsigned char *a= addr_r->inet6.sin6_addr.s6_addr+16;
++  for (i=0; i<32; ) { /* i incremented in inner loop */
++    unsigned b=0;
++    for (j=0; j<2; j++, i++) {
++      REVPARSE_P_L(i);
++      unsigned v;
++      if (!revparse_atoi(p,l,16,15,&v)) return 0;
++      b >>= 4;
++      b |= v << 4;
++    }
++    *--a= b;
++  }
++  *rrtype_r= adns_r_aaaa;
++  addr_r->inet.sin_family= AF_INET6;
++  return 1;
++}
++#endif
++
++bool adns__revparse_label(struct revparse_state *rps, int labnum,
++			  const char *dgram, int labstart, int lablen) {
++  if (labnum >= MAXREVLABELS)
++    return 0;
++
++  assert(labstart <= 65535);
++  assert(lablen <= 255);
++  rps->labstart[labnum] = labstart;
++  rps->lablen[labnum] = lablen;
++  return 1;
++}
++
++bool adns__revparse_done(struct revparse_state *rps,
++			 const char *dgram, int nlabels,
++			 adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
++  return
++    revparse_inet(rps,dgram,nlabels,rrtype_r,addr_r)
++#ifdef WITH_IPV6
++    || revparse_inet6(rps,dgram,nlabels,rrtype_r,addr_r)
++#endif
++    ;
++}
+Index: adns.h
+===================================================================
+--- adns.h	(revision 71177)
++++ adns.h	(working copy)
+@@ -1,39 +1,40 @@
+ /*
+  * adns.h
+- * - adns user-visible API (single-threaded, without any locking)
++ * - adns user-visible API
+  */
+ /*
+  *
+  *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
++ *    Copyright (C) 1997-2000,2003,2006,2014 Ian Jackson
+  *
+  *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
++ *    Copyright (C) 1997-2000,2003,2006,2014 Ian Jackson
++ *    Copyright (C) 1999-2000,2003,2006 Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
+  *
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+  *
+- *
++ * 
+  *  For the benefit of certain LGPL'd `omnibus' software which
+  *  provides a uniform interface to various things including adns, I
+  *  make the following additional licence.  I do this because the GPL
+  *  would otherwise force either the omnibus software to be GPL'd or
+  *  the adns-using part to be distributed separately.
+- *
++ *  
+  *  So: you may also redistribute and/or modify adns.h (but only the
+  *  public header file adns.h and not any other part of adns) under the
+  *  terms of the GNU Library General Public License as published by the
+  *  Free Software Foundation; either version 2 of the License, or (at
+  *  your option) any later version.
+- *
++ *  
+  *  Note that adns itself is GPL'd.  Authors of adns-using applications
+  *  with GPL-incompatible licences, and people who distribute adns with
+  *  applications where the whole distribution is not GPL'd, are still
+@@ -47,10 +48,8 @@
+  *
+  *  You should have received a copy of the GNU General Public License,
+  *  or the GNU Library General Public License, as appropriate, along
+- *  with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  with this program; if not, write to the Free Software Foundation.
+  *
+- *
+  */
+ 
+ #ifndef ADNS_H_INCLUDED
+@@ -59,20 +58,15 @@
+ #ifdef ADNS_JGAA_WIN32
+ # include "adns_win32.h"
+ #else
+-# include <stdio.h>
+-# include <sys/socket.h>
+-# include <netinet/in.h>
+-# include <sys/types.h>
+-# include <sys/time.h>
+-# include <unistd.h>
++#include <stdio.h>
++#include <stdarg.h>
+ 
+-# define ADNS_API
+-# define ADNS_SOCKET int
+-# define adns_socket_close(sck) close(sck)
+-# define adns_socket_read(sck, data, len) read(sck, data, len)
+-# define adns_socket_write(sck, data, len) write(sck, data, len)
+-# define ADNS_CAPTURE_ERRNO {}
+-# define ADNS_CLEAR_ERRNO {}
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <sys/time.h>
++#include <unistd.h>
++#include <net/if.h>
+ #endif
+ 
+ #ifdef __cplusplus
+@@ -79,69 +73,156 @@
+ extern "C" { /* I really dislike this - iwj. */
+ #endif
+ 
++/* Whether to support address families other than IPv4 in responses which use
++ * the `adns_rr_addr' structure.  This is a source-compatibility issue: old
++ * clients may not expect to find address families other than AF_INET in
++ * their query results.  There's a separate binary compatibility issue to do
++ * with the size of the `adns_rr_addr' structure, but we'll assume you can
++ * cope with that because you have this header file.  Define
++ * `ADNS_FEATURE_IPV4ONLY' if you only want to see AF_INET addresses by
++ * default, or `ADNS_FEATURE_MANYAF' to allow multiple address families; the
++ * default is currently to stick with AF_INET only, but this is likely to
++ * change in a later release of ADNS.  Note that any adns_qf_want_... flags
++ * in your query are observed: this setting affects only the default address
++ * families.
++ */
++#if !defined(ADNS_FEATURE_IPV4ONLY) && !defined(ADNS_FEATURE_MANYAF)
++#  define ADNS_FEATURE_IPV4ONLY
++#elif defined(ADNS_FEATURE_IPV4ONLY) && defined(ADNS_FEATURE_MANYAF)
++#  error "Feature flags ADNS_FEATURE_IPV4ONLY and ..._MANYAF are incompatible"
++#endif
++
+ /* All struct in_addr anywhere in adns are in NETWORK byte order. */
+ 
+ typedef struct adns__state *adns_state;
+ typedef struct adns__query *adns_query;
+ 
+-typedef enum {
+-  adns_if_noenv=        0x0001, /* do not look at environment */
+-  adns_if_noerrprint=   0x0002, /* never print output to stderr (_debug overrides) */
+-  adns_if_noserverwarn= 0x0004, /* do not warn to stderr about duff nameservers etc */
+-  adns_if_debug=        0x0008, /* enable all output to stderr plus debug msgs */
+-  adns_if_logpid=       0x0080, /* include pid in diagnostic output */
+-  adns_if_noautosys=    0x0010, /* do not make syscalls at every opportunity */
+-  adns_if_eintr=        0x0020, /* allow _wait and _synchronous to return EINTR */
+-  adns_if_nosigpipe=    0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */
+-  adns_if_checkc_entex= 0x0100, /* do consistency checks on entry/exit to adns funcs */
+-  adns_if_checkc_freq=  0x0300  /* do consistency checks very frequently (slow!) */
++typedef enum { /* In general, or together the desired flags: */
++ adns_if_none=        0x0000,/* no flags.  nicer than 0 for some compilers */
++ adns_if_noenv=       0x0001,/* do not look at environment */
++ adns_if_noerrprint=  0x0002,/* never print to stderr (_debug overrides) */
++ adns_if_noserverwarn=0x0004,/* do not warn to stderr about duff servers etc */
++ adns_if_debug=       0x0008,/* enable all output to stderr plus debug msgs */
++ adns_if_logpid=      0x0080,/* include pid in diagnostic output */
++ adns_if_noautosys=   0x0010,/* do not make syscalls at every opportunity */
++ adns_if_eintr=       0x0020,/* allow _wait and _synchronous to return EINTR */
++ adns_if_nosigpipe=   0x0040,/* applic has SIGPIPE ignored, do not protect */
++ adns_if_checkc_entex=0x0100,/* consistency checks on entry/exit to adns fns */
++ adns_if_checkc_freq= 0x0300,/* consistency checks very frequently (slow!) */
++
++ adns_if_permit_ipv4= 0x0400,/* allow _addr queries to return IPv4 addresses  */
++ adns_if_permit_ipv6= 0x0800,/* allow _addr queries to return IPv6 addresses */
++ adns_if_afmask=      0x0c00,
++   /* These are policy flags, and overridden by the adns_af:... option in
++    * resolv.conf.  If the adns_qf_want_... query flags are incompatible with
++    * these settings (in the sense that no address families are permitted at
++    * all) then the query flags take precedence; otherwise only records which
++    * satisfy all of the stated requirements are allowed.
++    */
++ adns__if_sizeforce= 0x7fff,
+ } adns_initflags;
+ 
+-typedef enum {
+-  adns_qf_search=          0x00000001, /* use the searchlist */
+-  adns_qf_usevc=           0x00000002, /* use a virtual circuit (TCP connection) */
+-  adns_qf_owner=           0x00000004, /* fill in the owner field in the answer */
+-  adns_qf_quoteok_query=   0x00000010, /* allow special chars in query domain */
+-  adns_qf_quoteok_cname=   0x00000000, /* allow ... in CNAME we go via - now default */
+-  adns_qf_quoteok_anshost= 0x00000040, /* allow ... in things supposed to be hostnames */
+-  adns_qf_quotefail_cname= 0x00000080, /* refuse if quote-req chars in CNAME we go via */
+-  adns_qf_cname_loose=     0x00000100, /* allow refs to CNAMEs - without, get _s_cname */
+-  adns_qf_cname_forbid=    0x00000200, /* don't follow CNAMEs, instead give _s_cname */
+-  adns__qf_internalmask=   0x0ff00000
+-} adns_queryflags;
++typedef enum { /* In general, or together the desired flags: */
++ adns_qf_none=           0x00000000,/* no flags */
++ adns_qf_search=         0x00000001,/* use the searchlist */
++ adns_qf_usevc=          0x00000002,/* use a virtual circuit (TCP conn) */
++ adns_qf_owner=          0x00000004,/* fill in the owner field in the answer */
++ adns_qf_quoteok_query=  0x00000010,/* allow special chars in query domain */
++ adns_qf_quoteok_cname=  0x00000000,/*  ... in CNAME we go via (now default) */
++ adns_qf_quoteok_anshost=0x00000040,/*  ... in things supposedly hostnames */
++ adns_qf_quotefail_cname=0x00000080,/* refuse if quote-req chars in CNAME we go via */
++ adns_qf_cname_loose=    0x00000100,/* allow refs to CNAMEs - without, get _s_cname */
++ adns_qf_cname_strict=   0x00010000,/* forbid CNAME refs (default, currently) */
++ adns_qf_cname_forbid=   0x00000200,/* don't follow CNAMEs, instead give _s_cname */
+ 
+-typedef enum {
+-  adns__rrt_typemask=  0x0ffff,
+-  adns__qtf_deref=     0x10000, /* dereference domains and perhaps produce extra data */
+-  adns__qtf_mail822=   0x20000, /* make mailboxes be in RFC822 rcpt field format */
++ adns_qf_want_ipv4=	 0x00000400,/* try to return IPv4 addresses */
++ adns_qf_want_ipv6=	 0x00000800,/* try to return IPv6 addresses */
++ adns_qf_want_allaf=	 0x00000c00,/* all the above flag bits */
++   /* Without any of the _qf_want_... flags, _qtf_deref queries try to return
++    * all address families permitted by _if_permit_... (as overridden by the
++    * `adns_af:...'  configuration option).  Set flags to restrict the
++    * returned address families to the ones selected.
++    */
++ adns_qf_ipv6_mapv4=	 0x00001000,/*  ... return IPv4 addresses as v6-mapped */
+ 
+-  adns_r_none=               0,
++ adns_qf_addrlit_scope_forbid=0x00002000,/* forbid %<scope> in IPv6 literals */
++ adns_qf_addrlit_scope_numeric=0x00004000,/* %<scope> may only be numeric */
++ adns_qf_addrlit_ipv4_quadonly=0x00008000,/* reject non-dotted-quad ipv4 */
+ 
+-  adns_r_a=                  1,
++ adns__qf_internalmask=  0x0ff00000,
++ adns__qf_sizeforce=     0x7fffffff
++} adns_queryflags;
+ 
+-  adns_r_ns_raw=             2,
+-  adns_r_ns=                    adns_r_ns_raw|adns__qtf_deref,
++typedef enum {
++ adns_rrt_typemask=  0x0ffff,
++ adns_rrt_reprmask= 0xffffff,
++ adns__qtf_deref_bit=0x10000,/* internal version of ..._deref below */
++ adns__qtf_mail822=  0x20000,/* return mailboxes in RFC822 rcpt field fmt   */
+ 
+-  adns_r_cname=              5,
++ adns__qtf_bigaddr=0x1000000,/* use the new larger sockaddr union */
++ adns__qtf_manyaf= 0x2000000,/* permitted to return multiple address families */
+ 
+-  adns_r_soa_raw=            6,
+-  adns_r_soa=                   adns_r_soa_raw|adns__qtf_mail822,
++ adns__qtf_deref=    adns__qtf_deref_bit|adns__qtf_bigaddr
++#ifdef ADNS_FEATURE_MANYAF
++		     |adns__qtf_manyaf
++#endif
++			    ,/* dereference domains; perhaps get extra data */
+ 
+-  adns_r_ptr_raw=           12,
+-  adns_r_ptr=                   adns_r_ptr_raw|adns__qtf_deref,
++ adns_r_unknown=     0x40000,
++   /* To use this, ask for records of type   <rr-type-code>|adns_r_unknown.
++    * adns will not process the RDATA - you'll get adns_rr_byteblocks,
++    * where the int is the length and the unsigned char* points to the
++    * data.  String representation of the RR data (by adns_rrinfo) is as in
++    * RFC3597.  adns_rr_info will not return the type name in *rrtname_r
++    * (due to memory management problems); *fmtname_r will be set to
++    * "unknown".
++    *
++    * Do not specify adns_r_unknown along with a known RR type which
++    * requires domain name uncompression (see RFC3597 s4); domain names
++    * will not be uncompressed and the resulting data would be useless.
++    * Asking for meta-RR types via adns_r_unknown will not work properly
++    * either and may make adns complain about server misbehaviour, so don't
++    * do that.
++    *
++    * Don't forget adns_qf_quoteok if that's what you want. */
+ 
+-  adns_r_hinfo=             13,
++ adns_r_none=             0,
++ 		     
++ adns_r_a=                1,
++ 		     
++ adns_r_ns_raw=           2,
++ adns_r_ns=                  adns_r_ns_raw|adns__qtf_deref,
++ 		     
++ adns_r_cname=            5,
++ 		     
++ adns_r_soa_raw=          6,
++ adns_r_soa=                 adns_r_soa_raw|adns__qtf_mail822, 
++ 		     
++ adns_r_ptr_raw=         12, /* do not mind PTR with wrong or missing addr */
++ adns_r_ptr=                 adns_r_ptr_raw|adns__qtf_deref,
++ 		     
++ adns_r_hinfo=           13,  
++ 		     
++ adns_r_mx_raw=          15,
++ adns_r_mx=                  adns_r_mx_raw|adns__qtf_deref,
++ 		     
++ adns_r_txt=             16,
++ 		     
++ adns_r_rp_raw=          17,
++ adns_r_rp=                  adns_r_rp_raw|adns__qtf_mail822,
+ 
+-  adns_r_mx_raw=            15,
+-  adns_r_mx=                    adns_r_mx_raw|adns__qtf_deref,
++ adns_r_aaaa=		 28,
+ 
+-  adns_r_txt=               16,
++ /* For SRV records, query domain without _qf_quoteok_query must look
++  * as expected from SRV RFC with hostname-like Name.  _With_
++  * _quoteok_query, any query domain is allowed. */
++ adns_r_srv_raw=         33,
++ adns_r_srv=                 adns_r_srv_raw|adns__qtf_deref,
++		     
++ adns_r_addr=                adns_r_a|adns__qtf_deref,
+ 
+-  adns_r_rp_raw=            17,
+-  adns_r_rp=                    adns_r_rp_raw|adns__qtf_mail822,
+-
+-  adns_r_addr=                  adns_r_a|adns__qtf_deref
+-
++ adns__rrt_sizeforce= 0x7fffffff,
++ 
+ } adns_rrtype;
+ 
+ /*
+@@ -149,7 +230,7 @@
+  * legal syntax, or you get adns_s_querydomainvalid (if the query
+  * domain contains bad characters) or adns_s_answerdomaininvalid (if
+  * the answer contains bad characters).
+- *
++ * 
+  * In queries _with_ qf_quoteok_*, domains in the query or response
+  * may contain any characters, quoted according to RFC1035 5.1.  On
+  * input to adns, the char* is a pointer to the interior of a "
+@@ -213,66 +294,83 @@
+  */
+ 
+ typedef enum {
+-  adns_s_ok,
++ adns_s_ok,
+ 
+-  /* locally induced errors */
+-  adns_s_nomemory,
+-  adns_s_unknownrrtype,
+-  adns_s_systemfail,
++ /* locally induced errors */
++ adns_s_nomemory,
++ adns_s_unknownrrtype,
++ adns_s_systemfail,
+ 
+-  adns_s_max_localfail= 29,
++ adns_s_max_localfail= 29,
++ 
++ /* remotely induced errors, detected locally */
++ adns_s_timeout,
++ adns_s_allservfail,
++ adns_s_norecurse,
++ adns_s_invalidresponse,
++ adns_s_unknownformat,
+ 
+-  /* remotely induced errors, detected locally */
+-  adns_s_timeout,
+-  adns_s_allservfail,
+-  adns_s_norecurse,
+-  adns_s_invalidresponse,
+-  adns_s_unknownformat,
++ adns_s_max_remotefail= 59,
++ 
++ /* remotely induced errors, reported by remote server to us */
++ adns_s_rcodeservfail,
++ adns_s_rcodeformaterror,
++ adns_s_rcodenotimplemented,
++ adns_s_rcoderefused,
++ adns_s_rcodeunknown,
+ 
+-  adns_s_max_remotefail= 59,
++ adns_s_max_tempfail= 99,
+ 
+-  /* remotely induced errors, reported by remote server to us */
+-  adns_s_rcodeservfail,
+-  adns_s_rcodeformaterror,
+-  adns_s_rcodenotimplemented,
+-  adns_s_rcoderefused,
+-  adns_s_rcodeunknown,
++ /* remote configuration errors */
++ adns_s_inconsistent, /* PTR gives domain whose addr is missing or mismatch */
++ adns_s_prohibitedcname, /* CNAME, but eg A expected (not if _qf_cname_loose) */
++ adns_s_answerdomaininvalid,
++ adns_s_answerdomaintoolong,
++ adns_s_invaliddata,
++ 
++ adns_s_max_misconfig= 199,
+ 
+-  adns_s_max_tempfail= 99,
++ /* permanent problems with the query */
++ adns_s_querydomainwrong,
++ adns_s_querydomaininvalid,
++ adns_s_querydomaintoolong,
++ 
++ adns_s_max_misquery= 299,
+ 
+-  /* remote configuration errors */
+-  adns_s_inconsistent, /* PTR gives domain whose A does not exist and match */
+-  adns_s_prohibitedcname, /* CNAME found where eg A expected (not if _qf_loosecname) */
+-  adns_s_answerdomaininvalid,
+-  adns_s_answerdomaintoolong,
+-  adns_s_invaliddata,
++ /* permanent errors */
++ adns_s_nxdomain,
++ adns_s_nodata,
+ 
+-  adns_s_max_misconfig= 199,
++ adns_s_max_permfail= 499
++ 
++} adns_status;
+ 
+-  /* permanent problems with the query */
+-  adns_s_querydomainwrong,
+-  adns_s_querydomaininvalid,
+-  adns_s_querydomaintoolong,
++typedef union {
++  struct sockaddr sa;
++  struct sockaddr_in inet;
++} adns_sockaddr_v4only;
+ 
+-  adns_s_max_misquery= 299,
++typedef union {
++  struct sockaddr sa;
++  struct sockaddr_in inet;
++#ifdef WITH_IPV6
++  struct sockaddr_in6 inet6;
++#endif
++} adns_sockaddr;
+ 
+-  /* permanent errors */
+-  adns_s_nxdomain,
+-  adns_s_nodata,
+-
+-  adns_s_max_permfail= 499
+-
+-} adns_status;
+-
+ typedef struct {
+   int len;
+-  union {
+-    struct sockaddr sa;
+-    struct sockaddr_in inet;
+-  } addr;
++  adns_sockaddr addr;
+ } adns_rr_addr;
+ 
+ typedef struct {
++  /* the old v4-only structure; handy if you have complicated binary
++   * compatibility problems. */
++  int len;
++  adns_sockaddr_v4only addr;
++} adns_rr_addr_v4only;
++
++typedef struct {
+   char *host;
+   adns_status astatus;
+   int naddrs; /* temp fail => -1, perm fail => 0, s_ok => >0 */
+@@ -289,10 +387,11 @@
+ } adns_rr_inthostaddr;
+ 
+ typedef struct {
+-  /* Used both for mx_raw, in which case i is the preference and str the domain,
+-   * and for txt, in which case each entry has i for the `text' length,
+-   * and str for the data (which will have had an extra nul appended
+-   * so that if it was plain text it is now a null-terminated string).
++  /* Used both for mx_raw, in which case i is the preference and str
++   * the domain, and for txt, in which case each entry has i for the
++   * `text' length, and str for the data (which will have had an extra
++   * nul appended so that if it was plain text it is now a
++   * null-terminated string).
+    */
+   int i;
+   char *str;
+@@ -308,25 +407,44 @@
+ } adns_rr_soa;
+ 
+ typedef struct {
++  int priority, weight, port;
++  char *host;
++} adns_rr_srvraw;
++
++typedef struct {
++  int priority, weight, port;
++  adns_rr_hostaddr ha;
++} adns_rr_srvha;
++
++typedef struct {
++  int len;
++  unsigned char *data;
++} adns_rr_byteblock;
++
++typedef struct {
+   adns_status status;
+   char *cname; /* always NULL if query was for CNAME records */
+-  char *owner; /* only set if requested in query flags, and may be 0 on error anyway */
++  char *owner; /* only set if req'd in query flags; maybe 0 on error anyway */
+   adns_rrtype type; /* guaranteed to be same as in query */
+-  time_t expires; /* expiry time, defined only if _s_ok, nxdomain or nodata. NOT TTL! */
++  time_t expires;/*abs time.  def only if _s_ok, nxdomain or nodata. NOT TTL!*/
+   int nrrs, rrsz; /* nrrs is 0 if an error occurs */
+   union {
+     void *untyped;
+     unsigned char *bytes;
+-    char *(*str);                     /* ns_raw, cname, ptr, ptr_raw */
+-    adns_rr_intstr *(*manyistr);      /* txt (list of strings ends with i=-1, str=0) */
+-    adns_rr_addr *addr;               /* addr */
+-    struct in_addr *inaddr;           /* a */
+-    adns_rr_hostaddr *hostaddr;       /* ns */
+-    adns_rr_intstrpair *intstrpair;   /* hinfo */
+-    adns_rr_strpair *strpair;         /* rp, rp_raw */
+-    adns_rr_inthostaddr *inthostaddr; /* mx */
+-    adns_rr_intstr *intstr;           /* mx_raw */
+-    adns_rr_soa *soa;                 /* soa, soa_raw */
++    char *(*str);                    /* ns_raw, cname, ptr, ptr_raw */
++    adns_rr_intstr *(*manyistr);     /* txt (list strs ends with i=-1, str=0)*/
++    adns_rr_addr *addr;              /* addr */
++    struct in_addr *inaddr;          /* a */
++    struct in6_addr *in6addr;	     /* aaaa */
++    adns_rr_hostaddr *hostaddr;      /* ns */
++    adns_rr_intstrpair *intstrpair;  /* hinfo */
++    adns_rr_strpair *strpair;        /* rp, rp_raw */
++    adns_rr_inthostaddr *inthostaddr;/* mx */
++    adns_rr_intstr *intstr;          /* mx_raw */
++    adns_rr_soa *soa;                /* soa, soa_raw */
++    adns_rr_srvraw *srvraw;          /* srv_raw */
++    adns_rr_srvha *srvha;/* srv */
++    adns_rr_byteblock *byteblock;    /* ...|unknown */
+   } rrs;
+ } adns_answer;
+ 
+@@ -346,8 +464,9 @@
+  *
+  *  For _init, _init_strcfg, _submit and _synchronous, system errors
+  *  (eg, failure to create sockets, malloc failure, etc.) return errno
+- *  values.
+- *
++ *  values.  EINVAL from _init et al means the configuration file
++ *  is erroneous and cannot be parsed.
++ * 
+  *  For _wait and _check failures are reported in the answer
+  *  structure, and only 0, ESRCH or (for _check) EAGAIN is
+  *  returned: if no (appropriate) requests are done adns_check returns
+@@ -363,15 +482,40 @@
+  *  requested.
+  */
+ 
+-ADNS_API int adns_init(adns_state *newstate_r, adns_initflags flags,
++/* Threads:
++ *  adns does not use any static modifiable state, so it
++ *   is safe to call adns_init several times and then use the
++ *   resulting adns_states concurrently.
++ *  However, it is NOT safe to make simultaneous calls into
++ *   adns using the same adns_state; a single adns_state must be used
++ *   only by one thread at a time.  You can solve this problem by
++ *   having one adns_state per thread, or if that isn't feasible, you
++ *   could maintain a pool of adns_states.  Unfortunately neither of
++ *   these approaches has optimal performance.
++ */
++
++int adns_init(adns_state *newstate_r, adns_initflags flags,
+ 	      FILE *diagfile /*0=>stderr*/);
+ 
+ /* ReactOS addition */
+ ADNS_API void adns_addserver(adns_state state, struct in_addr server);
+ 
+-ADNS_API int adns_init_strcfg(adns_state *newstate_r, adns_initflags flags,
++int adns_init_strcfg(adns_state *newstate_r, adns_initflags flags,
+ 		     FILE *diagfile /*0=>discard*/, const char *configtext);
+ 
++typedef void adns_logcallbackfn(adns_state ads, void *logfndata,
++				const char *fmt, va_list al);
++  /* Will be called perhaps several times for each message; when the
++   * message is complete, the string implied by fmt and al will end in
++   * a newline.  Log messages start with `adns debug:' or `adns
++   * warning:' or `adns:' (for errors), or `adns debug [PID]:'
++   * etc. if adns_if_logpid is set. */
++
++int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
++		    const char *configtext /*0=>use default config files*/,
++		    adns_logcallbackfn *logfn /*0=>logfndata is a FILE* */,
++		    void *logfndata /*0 with logfn==0 => discard*/);
++
+ /* Configuration:
+  *  adns_init reads /etc/resolv.conf, which is expected to be (broadly
+  *  speaking) in the format expected by libresolv, and then
+@@ -381,7 +525,7 @@
+  *  is set later overrides any that is set earlier.
+  *
+  * Standard directives understood in resolv[-adns].conf:
+- *
++ * 
+  *  nameserver <address>
+  *   Must be followed by the IP address of a nameserver.  Several
+  *   nameservers may be specified, and they will be tried in the order
+@@ -446,6 +590,21 @@
+  *   setting of adns_if_check_entex, adns_if_check_freq, or neither,
+  *   in the flags passed to adns_init.
+  *
++ *  adns_af:{ipv4,ipv6},...  adns_af:any
++ *   Determines which address families ADNS looks up (either as an
++ *   adns_r_addr query, or when dereferencing an answer yielding hostnames
++ *   (e.g., adns_r_mx).  The argument is a comma-separated list: only the
++ *   address families listed will be looked up.  The default is `any'.
++ *   Lookups occur (logically) concurrently; use the `sortlist' directive to
++ *   control the relative order of addresses in answers.  This option
++ *   overrides the corresponding init flags (covered by adns_if_afmask).
++ *
++ *  adns_ignoreunkcfg
++ *   Ignore unknown options and configuration directives, rather than
++ *   logging them.  To be effective, appear in the configuration
++ *   before the unknown options.  ADNS_RES_OPTIONS is generally early
++ *   enough.
++ * 
+  * There are a number of environment variables which can modify the
+  * behaviour of adns.  They take effect only if adns_init is used, and
+  * the caller of adns_init can disable them using adns_if_noenv.  In
+@@ -472,7 +631,7 @@
+  *   line in resolv.conf.
+  */
+ 
+-ADNS_API int adns_synchronous(adns_state ads,
++int adns_synchronous(adns_state ads,
+ 		     const char *owner,
+ 		     adns_rrtype type,
+ 		     adns_queryflags flags,
+@@ -483,7 +642,7 @@
+  * processing functions to actually get things to happen.
+  */
+ 
+-ADNS_API int adns_submit(adns_state ads,
++int adns_submit(adns_state ads,
+ 		const char *owner,
+ 		adns_rrtype type,
+ 		adns_queryflags flags,
+@@ -492,23 +651,23 @@
+ 
+ /* The owner should be quoted in master file format. */
+ 
+-ADNS_API int adns_check(adns_state ads,
++int adns_check(adns_state ads,
+ 	       adns_query *query_io,
+ 	       adns_answer **answer_r,
+ 	       void **context_r);
+ 
+-ADNS_API int adns_wait(adns_state ads,
++int adns_wait(adns_state ads,
+ 	      adns_query *query_io,
+ 	      adns_answer **answer_r,
+ 	      void **context_r);
+ 
+ /* same as adns_wait but uses poll(2) internally */
+-ADNS_API int adns_wait_poll(adns_state ads,
++int adns_wait_poll(adns_state ads,
+ 		   adns_query *query_io,
+ 		   adns_answer **answer_r,
+ 		   void **context_r);
+ 
+-ADNS_API void adns_cancel(adns_query query);
++void adns_cancel(adns_query query);
+ 
+ /* The adns_query you get back from _submit is valid (ie, can be
+  * legitimately passed into adns functions) until it is returned by
+@@ -522,7 +681,7 @@
+  * query type.
+  */
+ 
+-ADNS_API int adns_submit_reverse(adns_state ads,
++int adns_submit_reverse(adns_state ads,
+ 			const struct sockaddr *addr,
+ 			adns_rrtype type,
+ 			adns_queryflags flags,
+@@ -532,7 +691,7 @@
+  * addr->sa_family must be AF_INET or you get ENOSYS.
+  */
+ 
+-ADNS_API int adns_submit_reverse_any(adns_state ads,
++int adns_submit_reverse_any(adns_state ads,
+ 			    const struct sockaddr *addr,
+ 			    const char *rzone,
+ 			    adns_rrtype type,
+@@ -545,14 +704,69 @@
+  * addr->sa_family must be AF_INET or you get ENOSYS.
+  */
+ 
+-ADNS_API void adns_finish(adns_state ads);
++void adns_finish(adns_state ads);
+ /* You may call this even if you have queries outstanding;
+  * they will be cancelled.
+  */
+ 
++#define ADNS_ADDR2TEXT_BUFLEN					\
++  (INET6_ADDRSTRLEN + 1/*%*/					\
++  + ((IF_NAMESIZE-1) > 9 ? (IF_NAMESIZE-1) : 9/*uint32*/)	\
++  + 1/* nul; included in IF_NAMESIZE */)
+ 
+-ADNS_API void adns_forallqueries_begin(adns_state ads);
+-ADNS_API adns_query adns_forallqueries_next(adns_state ads, void **context_r);
++int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
++		   struct sockaddr *sa_r,
++		   socklen_t *salen_io /* updated iff OK or ENOSPC */);
++int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
++		   char *buffer, int *buflen_io /* updated ONLY on ENOSPC */,
++		   int *port_r /* may be 0 */);
++  /*
++   * port is always in host byte order and is simply copied to and
++   * from the appropriate sockaddr field (byteswapped as necessary).
++   *
++   * The only flags supported are adns_qf_addrlit_...
++   *
++   * Error return values are:
++   *
++   *  ENOSPC    Output buffer is too small.  Can only happen if
++   *            *buflen_io < ADNS_ADDR2TEXT_BUFLEN or
++   *            *salen_io < sizeof(adns_sockaddr).  On return,
++   *            *buflen_io or *salen_io has been updated by adns.
++   *
++   *  EINVAL    text has invalid syntax.
++   *
++   *            text represents an address family not supported by
++   *            this version of adns.
++   *
++   *            Scoped address supplied (text contained "%" or
++   *            sin6_scope_id nonzero) but caller specified
++   *            adns_qf_addrlit_scope_forbid.
++   *
++   *            Scope name (rather than number) supplied in text but
++   *            caller specified adns_qf_addrlit_scope_numeric.
++   *
++   *  EAFNOSUPPORT   sa->sa_family is not supported (addr2text only).
++   *
++   *  ENOSYS    Unsupported flags set.
++   *
++   * Only if neither adns_qf_addrlit_scope_forbid nor
++   * adns_qf_addrlit_scope_numeric are set:
++   *
++   *  ENOSYS    Scope name supplied in text but IPv6 address part of
++   *            sockaddr is not a link local address.
++   *
++   *  ENXIO     Scope name supplied in text but if_nametoindex
++   *            said it wasn't a valid local interface name.
++   *
++   *  EIO       Scoped address supplied but if_nametoindex failed
++   *            in an unexpected way; adns has printed a message to
++   *            stderr.
++   *
++   *  any other   if_nametoindex failed in a more-or-less expected way.
++   */
++
++void adns_forallqueries_begin(adns_state ads);
++adns_query adns_forallqueries_next(adns_state ads, void **context_r);
+ /* Iterator functions, which you can use to loop over the outstanding
+  * (submitted but not yet successfuly checked/waited) queries.
+  *
+@@ -568,7 +782,7 @@
+  * context_r may be 0.  *context_r may not be set when _next returns 0.
+  */
+ 
+-ADNS_API  void adns_checkconsistency(adns_state ads, adns_query qu);
++void adns_checkconsistency(adns_state ads, adns_query qu);
+ /* Checks the consistency of adns's internal data structures.
+  * If any error is found, the program will abort().
+  * You may pass 0 for qu; if you pass non-null then additional checks
+@@ -600,7 +814,7 @@
+  * blocking, or you may not have an up-to-date list of it's fds.
+  */
+ 
+-ADNS_API int adns_processany(adns_state ads);
++int adns_processany(adns_state ads);
+ /* Gives adns flow-of-control for a bit.  This will never block, and
+  * can be used with any threading/asynch-io model.  If some error
+  * occurred which might cause an event loop to spin then the errno
+@@ -607,9 +821,9 @@
+  * value is returned.
+  */
+ 
+-ADNS_API int adns_processreadable(adns_state ads, ADNS_SOCKET fd, const struct timeval *now);
+-ADNS_API int adns_processwriteable(adns_state ads, ADNS_SOCKET fd, const struct timeval *now);
+-ADNS_API int adns_processexceptional(adns_state ads, ADNS_SOCKET fd, const struct timeval *now);
++int adns_processreadable(adns_state ads, int fd, const struct timeval *now);
++int adns_processwriteable(adns_state ads, int fd, const struct timeval *now);
++int adns_processexceptional(adns_state ads, int fd, const struct timeval *now);
+ /* Gives adns flow-of-control so that it can process incoming data
+  * from, or send outgoing data via, fd.  Very like _processany.  If it
+  * returns zero then fd will no longer be readable or writeable
+@@ -628,7 +842,7 @@
+  * then the errno value is returned.
+  */
+ 
+-ADNS_API void adns_processtimeouts(adns_state ads, const struct timeval *now);
++void adns_processtimeouts(adns_state ads, const struct timeval *now);
+ /* Gives adns flow-of-control so that it can process any timeouts
+  * which might have happened.  Very like _processreadable/writeable.
+  *
+@@ -636,12 +850,12 @@
+  * obtained from gettimeofday.
+  */
+ 
+-ADNS_API void adns_firsttimeout(adns_state ads,
++void adns_firsttimeout(adns_state ads,
+ 		       struct timeval **tv_mod, struct timeval *tv_buf,
+ 		       struct timeval now);
+ /* Asks adns when it would first like the opportunity to time
+  * something out.  now must be the current time, from gettimeofday.
+- *
++ * 
+  * If tv_mod points to 0 then tv_buf must be non-null, and
+  * _firsttimeout will fill in *tv_buf with the time until the first
+  * timeout, and make *tv_mod point to tv_buf.  If adns doesn't have
+@@ -655,7 +869,7 @@
+  * is using.  It always succeeds and never blocks.
+  */
+ 
+-ADNS_API void adns_globalsystemfailure(adns_state ads);
++void adns_globalsystemfailure(adns_state ads);
+ /* If serious problem(s) happen which globally affect your ability to
+  * interact properly with adns, or adns's ability to function
+  * properly, you or adns can call this function.
+@@ -674,7 +888,7 @@
+  * Entrypoints for select-loop based asynch io:
+  */
+ 
+-ADNS_API void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds,
++void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds,
+ 		       fd_set *writefds, fd_set *exceptfds,
+ 		       struct timeval **tv_mod, struct timeval *tv_buf,
+ 		       const struct timeval *now);
+@@ -684,13 +898,13 @@
+  * for adns_firsttimeout.  readfds, writefds, exceptfds and maxfd_io may
+  * not be 0.
+  *
+- * If now is not 0 then this will never actually do any I/O, or change
+- * the fds that adns is using or the timeouts it wants.  In any case
+- * it won't block, and it will set the timeout to zero if a query
+- * finishes in _beforeselect.
++ * If tv_mod is 0 on entry then this will never actually do any I/O,
++ * or change the fds that adns is using or the timeouts it wants.  In
++ * any case it won't block, and it will set the timeout to zero if a
++ * query finishes in _beforeselect.
+  */
+ 
+-ADNS_API void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
++void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
+ 		      const fd_set *writefds, const fd_set *exceptfds,
+ 		      const struct timeval *now);
+ /* Gives adns flow-of-control for a bit; intended for use after
+@@ -724,12 +938,13 @@
+  * entrypoints will not be defined in libadns.  Sorry !
+  */
+ 
+-ADNS_API int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
++int adns_beforepoll(adns_state ads, struct pollfd *fds,
++		    int *nfds_io, int *timeout_io,
+ 		    const struct timeval *now);
+ /* Finds out which fd's adns is interested in, and when it would like
+  * to be able to time things out.  This is in a form suitable for use
+  * with poll(2).
+- *
++ * 
+  * On entry, usually fds should point to at least *nfds_io structs.
+  * adns will fill up to that many structs will information for poll,
+  * and record in *nfds_io how many structs it filled.  If it wants to
+@@ -769,7 +984,7 @@
+  * 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
+@@ -776,7 +991,7 @@
+  * require more space than this.
+  */
+ 
+-ADNS_API void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
++void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
+ 		    const struct timeval *now);
+ /* Gives adns flow-of-control for a bit; intended for use after
+  * poll(2).  fds and nfds should be the results from poll().  pollfd
+@@ -784,7 +999,7 @@
+  */
+ 
+ 
+-ADNS_API adns_status adns_rr_info(adns_rrtype type,
++adns_status adns_rr_info(adns_rrtype type,
+ 			 const char **rrtname_r, const char **fmtname_r,
+ 			 int *len_r,
+ 			 const void *datap, char **data_r);
+@@ -844,9 +1059,9 @@
+  *  dns2.spong.dyn.ml.org timeout "DNS query timed out" ?
+  */
+ 
+-ADNS_API const char *adns_strerror(adns_status st);
+-ADNS_API const char *adns_errabbrev(adns_status st);
+-ADNS_API const char *adns_errtypeabbrev(adns_status st);
++const char *adns_strerror(adns_status st);
++const char *adns_errabbrev(adns_status st);
++const char *adns_errtypeabbrev(adns_status st);
+ /* Like strerror but for adns_status values.  adns_errabbrev returns
+  * the abbreviation of the error - eg, for adns_s_timeout it returns
+  * "timeout".  adns_errtypeabbrev returns the abbreviation of the
+Index: adns.make
+===================================================================
+--- adns.make	(revision 71177)
++++ adns.make	(working copy)
+@@ -1,15 +1,15 @@
+ # src/adns.make - library definitions, including list of object files
+ # 
+-#  This file is
+-#    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+-#
+-#  It is part of adns, which is
+-#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+-#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
++#  This file is part of adns, which is
++#    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++#    Copyright (C) 2014  Mark Wooding
++#    Copyright (C) 1999-2000,2003,2006  Tony Finch
++#    Copyright (C) 1991 Massachusetts Institute of Technology
++#  (See the file INSTALL for full details.)
+ #  
+ #  This program is free software; you can redistribute it and/or modify
+ #  it under the terms of the GNU General Public License as published by
+-#  the Free Software Foundation; either version 2, or (at your option)
++#  the Free Software Foundation; either version 3, or (at your option)
+ #  any later version.
+ #  
+ #  This program is distributed in the hope that it will be useful,
+@@ -18,8 +18,7 @@
+ #  GNU General Public License for more details.
+ #  
+ #  You should have received a copy of the GNU General Public License
+-#  along with this program; if not, write to the Free Software Foundation,
+-#  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
++#  along with this program; if not, write to the Free Software Foundation.
+ 
+ LIBOBJS=	types.o event.o query.o reply.o general.o setup.o transmit.o \
+-		parse.o poll.o check.o
++		parse.o poll.o check.o addrfam.o
+Index: check.c
+===================================================================
+--- check.c	(revision 71177)
++++ check.c	(working copy)
+@@ -3,26 +3,25 @@
+  * - consistency checks
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #include "internal.h"
+@@ -31,56 +30,32 @@
+   adns__consistency(ads,qu,cc_user);
+ }
+ 
+-/* The original macro. Too gnuish for other compilers */
+-#if 0
+-#define DLIST_CHECK(list, nodevar, part, body)					\
+-  if ((list).head) {								\
+-    assert(! (list).head->part back);						\
+-    for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->part next) {	\
+-      assert((nodevar)->part next						\
+-	     ? (nodevar) == (nodevar)->part next->part back			\
+-	     : (nodevar) == (list).tail);					\
+-      body									\
+-    }										\
++#define DLIST_CHECK(list, nodevar, part, body)			\
++  if ((list).head) {						\
++    assert(! (list).head->part back);				\
++    for ((nodevar)= (list).head;				\
++	 (nodevar);						\
++	 (nodevar)= (nodevar)->part next) {			\
++      assert((nodevar)->part next				\
++	     ? (nodevar) == (nodevar)->part next->part back	\
++	     : (nodevar) == (list).tail);			\
++      body							\
++    }								\
+   }
+-#endif /* 0 */
+ 
+-#define DLIST_CHECK1(list, nodevar, body)					\
+-  if ((list).head) {								\
+-    assert(! (list).head->back);						\
+-    for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->next) {	\
+-      assert((nodevar)->next						\
+-	     ? (nodevar) == (nodevar)->next->back			\
+-	     : (nodevar) == (list).tail);					\
+-      body									\
+-    }										\
+-  }
+-
+-#define DLIST_CHECK2(list, nodevar, part, body)					\
+-  if ((list).head) {								\
+-    assert(! (list).head->part.back);						\
+-    for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->part.next) {	\
+-      assert((nodevar)->part.next						\
+-	     ? (nodevar) == (nodevar)->part.next->part.back			\
+-	     : (nodevar) == (list).tail);					\
+-      body									\
+-    }										\
+-  }
+-
+-#define DLIST_ASSERTON(node, nodevar, list, part)				\
+-  do {										\
+-    for ((nodevar)= (list).head;						\
+-	 (nodevar) != (node);							\
+-	 (nodevar)= (nodevar)->part next) {					\
+-      assert((nodevar));							\
+-    }										\
++#define DLIST_ASSERTON(node, nodevar, list, part)	\
++  do {							\
++    for ((nodevar)= (list).head;			\
++	 (nodevar) != (node);				\
++	 (nodevar)= (nodevar)->part next) {		\
++      assert((nodevar));				\
++    }							\
+   } while(0)
+ 
+ static void checkc_query_alloc(adns_state ads, adns_query qu) {
+   allocnode *an;
+ 
+-
+-  DLIST_CHECK1(qu->allocations, an, {
++  DLIST_CHECK(qu->allocations, an, , {
+   });
+ }
+ 
+@@ -100,15 +75,20 @@
+ }
+ 
+ static void checkc_global(adns_state ads) {
++  const struct sortlist *sl;
+   int i;
+   
+-  assert(ads->udpsocket >= 0);
++  assert(ads->udpsockets >= 0);
+ 
+-  for (i=0; i<ads->nsortlist; i++)
+-    assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr));
++  for (i=0; i<ads->nsortlist; i++) {
++    sl= &ads->sortlist[i];
++    assert(adns__addr_matches(sl->base.sa.sa_family,
++			      adns__sockaddr_addr(&sl->base.sa),
++			      &sl->base,&sl->mask));
++  }
+ 
+   assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers);
+-
++  
+   switch (ads->tcpstate) {
+   case server_connecting:
+     assert(ads->tcpsocket >= 0);
+@@ -132,8 +112,8 @@
+ 
+ static void checkc_queue_udpw(adns_state ads) {
+   adns_query qu;
+-
+-  DLIST_CHECK1(ads->udpw, qu, {
++  
++  DLIST_CHECK(ads->udpw, qu, , {
+     assert(qu->state==query_tosend);
+     assert(qu->retries <= UDPMAXRETRIES);
+     assert(qu->udpsent);
+@@ -145,8 +125,8 @@
+ 
+ static void checkc_queue_tcpw(adns_state ads) {
+   adns_query qu;
+-
+-  DLIST_CHECK1(ads->tcpw, qu, {
++  
++  DLIST_CHECK(ads->tcpw, qu, , {
+     assert(qu->state==query_tcpw);
+     assert(!qu->children.head && !qu->children.tail);
+     assert(qu->retries <= ads->nservers+1);
+@@ -158,10 +138,10 @@
+ static void checkc_queue_childw(adns_state ads) {
+   adns_query parent, child;
+ 
+-  DLIST_CHECK1(ads->childw, parent, {
++  DLIST_CHECK(ads->childw, parent, , {
+     assert(parent->state == query_childw);
+     assert(parent->children.head);
+-    DLIST_CHECK2(parent->children, child, siblings, {
++    DLIST_CHECK(parent->children, child, siblings., {
+       assert(child->parent == parent);
+       assert(child->state != query_done);
+     });
+@@ -170,26 +150,41 @@
+   });
+ }
+ 
++static void checkc_query_done(adns_state ads, adns_query qu) {
++  assert(qu->state == query_done);
++  assert(!qu->children.head && !qu->children.tail);
++  checkc_query(ads,qu);
++}
++
+ static void checkc_queue_output(adns_state ads) {
+   adns_query qu;
+-
+-  DLIST_CHECK1(ads->output, qu, {
+-    assert(qu->state == query_done);
+-    assert(!qu->children.head && !qu->children.tail);
++  
++  DLIST_CHECK(ads->output, qu, , {
+     assert(!qu->parent);
+     assert(!qu->allocations.head && !qu->allocations.tail);
+-    checkc_query(ads,qu);
++    checkc_query_done(ads,qu);
+   });
+ }
+ 
++static void checkc_queue_intdone(adns_state ads) {
++  adns_query qu;
++  
++  DLIST_CHECK(ads->intdone, qu, , {
++    assert(qu->parent);
++    assert(qu->ctx.callback);
++    checkc_query_done(ads,qu);
++  });
++}
++
+ void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc) {
+   adns_query search;
+-
++  
+   switch (cc) {
+   case cc_user:
+     break;
+   case cc_entex:
+     if (!(ads->iflags & adns_if_checkc_entex)) return;
++    assert(!ads->intdone.head);
+     break;
+   case cc_freq:
+     if ((ads->iflags & adns_if_checkc_freq) != adns_if_checkc_freq) return;
+@@ -203,6 +198,7 @@
+   checkc_queue_tcpw(ads);
+   checkc_queue_childw(ads);
+   checkc_queue_output(ads);
++  checkc_queue_intdone(ads);
+ 
+   if (qu) {
+     switch (qu->state) {
+@@ -216,7 +212,10 @@
+       DLIST_ASSERTON(qu, search, ads->childw, );
+       break;
+     case query_done:
+-      DLIST_ASSERTON(qu, search, ads->output, );
++      if (qu->parent)
++	DLIST_ASSERTON(qu, search, ads->intdone, );
++      else
++	DLIST_ASSERTON(qu, search, ads->output, );
+       break;
+     default:
+       assert(!"specific query state");
+Index: config.h.in
+===================================================================
+--- config.h.in	(revision 71177)
++++ config.h.in	(working copy)
+@@ -1,4 +1,4 @@
+-/* src/config.h.in.  Generated automatically from configure.in by autoheader.  */
++/* src/config.h.in.  Generated automatically from configure.in by autoheader 2.13.  */
+ 
+ /* Define if inline functions a la GCC are available.  */
+ #undef HAVE_INLINE
+@@ -21,9 +21,6 @@
+ /* Define if you have the poll function.  */
+ #undef HAVE_POLL
+ 
+-/* Define if you have the <sys/select.h> header file.  */
+-#undef HAVE_SYS_SELECT_H
+-
+ /* Define if you have the nsl library (-lnsl).  */
+ #undef HAVE_LIBNSL
+ 
+@@ -99,7 +96,3 @@
+ #ifdef HAVEUSE_RPCTYPES_H
+ #include <rpc/types.h>
+ #endif
+-
+-#ifdef HAVE_SYS_SELECT_H
+-#include <sys/select.h>
+-#endif
+Index: dlist.h
+===================================================================
+--- dlist.h	(revision 71177)
++++ dlist.h	(working copy)
+@@ -3,26 +3,25 @@
+  * - macros for handling doubly linked lists
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #ifndef ADNS_DLIST_H_INCLUDED
+@@ -31,8 +30,8 @@
+ #define LIST_INIT(list) ((list).head= (list).tail= 0)
+ #define LINK_INIT(link) ((link).next= (link).back= 0)
+ 
+-#define LIST_UNLINK_PART(list,node,part) \
+-  do { \
++#define LIST_UNLINK_PART(list,node,part)				    \
++  do {									    \
+     if ((node)->part back) (node)->part back->part next= (node)->part next; \
+       else                                  (list).head= (node)->part next; \
+     if ((node)->part next) (node)->part next->part back= (node)->part back; \
+@@ -39,12 +38,13 @@
+       else                                  (list).tail= (node)->part back; \
+   } while(0)
+ 
+-#define LIST_LINK_TAIL_PART(list,node,part) \
+-  do { \
+-    (node)->part next= 0; \
+-    (node)->part back= (list).tail; \
+-    if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \
+-    (list).tail= (node); \
++#define LIST_LINK_TAIL_PART(list,node,part)		\
++  do {							\
++    (node)->part next= 0;				\
++    (node)->part back= (list).tail;			\
++    if ((list).tail) (list).tail->part next= (node);	\
++    else (list).head= (node);				\
++    (list).tail= (node);				\
+   } while(0)
+ 
+ #define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
+Index: event.c
+===================================================================
+--- event.c	(revision 71177)
++++ event.c	(working copy)
+@@ -5,26 +5,25 @@
+  * - user-visible check/wait and event-loop-related functions
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, adns_socket_write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #include <errno.h>
+@@ -48,9 +47,6 @@
+ /* TCP connection management. */
+ 
+ static void tcp_close(adns_state ads) {
+-  int serv;
+-
+-  serv= ads->tcpserver;
+   adns_socket_close(ads->tcpsocket);
+   ads->tcpsocket= -1;
+   ads->tcprecv.used= ads->tcprecv_skip= ads->tcpsend.used= 0;
+@@ -59,7 +55,7 @@
+ void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
+   int serv;
+   adns_query qu;
+-
++  
+   assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
+   serv= ads->tcpserver;
+   if (what) adns__warn(ads,serv,0,"TCP connection failed: %s: %s",what,why);
+@@ -77,7 +73,7 @@
+ 
+ static void tcp_connected(adns_state ads, struct timeval now) {
+   adns_query qu, nqu;
+-
++  
+   adns__debug(ads,ads->tcpserver,0,"TCP connected");
+   ads->tcpstate= server_ok;
+   for (qu= ads->tcpw.head; qu && ads->tcpstate == server_ok; qu= nqu) {
+@@ -87,10 +83,24 @@
+   }
+ }
+ 
++static void tcp_broken_events(adns_state ads) {
++  adns_query qu, nqu;
++  
++  assert(ads->tcpstate == server_broken);
++  for (qu= ads->tcpw.head; qu; qu= nqu) {
++    nqu= qu->next;
++    assert(qu->state == query_tcpw);
++    if (qu->retries > ads->nservers) {
++      LIST_UNLINK(ads->tcpw,qu);
++      adns__query_fail(qu,adns_s_allservfail);
++    }
++  }
++  ads->tcpstate= server_disconnected;
++}
++
+ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+-  int r, tries;
+-  ADNS_SOCKET fd;
+-  struct sockaddr_in addr;
++  int r, fd, tries;
++  adns_rr_addr *addr;
+   struct protoent *proto;
+ 
+   for (tries=0; tries<ads->nservers; tries++) {
+@@ -104,16 +114,18 @@
+     default:
+       abort();
+     }
+-
++    
+     assert(!ads->tcpsend.used);
+     assert(!ads->tcprecv.used);
+     assert(!ads->tcprecv_skip);
+ 
+     proto= getprotobyname("tcp");
+-    if (!proto) { adns__diag(ads,-1,0,"unable to find protocol no. for TCP !"); return; }
+-	ADNS_CLEAR_ERRNO
+-    fd= socket(AF_INET,SOCK_STREAM,proto->p_proto);
+-	ADNS_CAPTURE_ERRNO;
++    if (!proto) {
++      adns__diag(ads,-1,0,"unable to find protocol no. for TCP !");
++      return;
++    }
++    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;
+@@ -120,17 +132,12 @@
+     }
+     r= adns__setnonblock(ads,fd);
+     if (r) {
+-      adns__diag(ads,-1,0,"cannot make TCP socket nonblocking: %s",strerror(r));
++      adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:"
++		 " %s",strerror(r));
+       adns_socket_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;
+-    ADNS_CLEAR_ERRNO;
+-    r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
+-    ADNS_CAPTURE_ERRNO;
++    r= connect(fd,&addr->addr.sa,addr->len);
+     ads->tcpsocket= fd;
+     ads->tcpstate= server_connecting;
+     if (r==0) { tcp_connected(ads,now); return; }
+@@ -140,7 +147,7 @@
+       return;
+     }
+     adns__tcp_broken(ads,"connect",strerror(errno));
+-    ads->tcpstate= server_disconnected;
++    tcp_broken_events(ads);
+   }
+ }
+ 
+@@ -169,7 +176,7 @@
+ 
+   timerclear(rbuf);
+ }
+-
++    
+ static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
+ 			struct timeval maxto) {
+   struct timeval *rbuf;
+@@ -206,7 +213,7 @@
+ 			   struct timeval **tv_io, struct timeval *tvbuf,
+ 			   struct timeval now, struct query_queue *queue) {
+   adns_query qu, nqu;
+-
++  
+   for (qu= queue->head; qu; qu= nqu) {
+     nqu= qu->next;
+     if (!timercmp(&now,&qu->timeout,>)) {
+@@ -227,21 +234,11 @@
+ static void tcp_events(adns_state ads, int act,
+ 		       struct timeval **tv_io, struct timeval *tvbuf,
+ 		       struct timeval now) {
+-  adns_query qu, nqu;
+-
+   for (;;) {
+     switch (ads->tcpstate) {
+     case server_broken:
+       if (!act) { inter_immed(tv_io,tvbuf); return; }
+-      for (qu= ads->tcpw.head; qu; qu= nqu) {
+-	nqu= qu->next;
+-	assert(qu->state == query_tcpw);
+-	if (qu->retries > ads->nservers) {
+-	  LIST_UNLINK(ads->tcpw,qu);
+-	  adns__query_fail(qu,adns_s_allservfail);
+-	}
+-      }
+-      ads->tcpstate= server_disconnected;
++      tcp_broken_events(ads);
+     case server_disconnected: /* fall through */
+       if (!ads->tcpw.head) return;
+       if (!act) { inter_immed(tv_io,tvbuf); return; }
+@@ -293,7 +290,7 @@
+ 		       struct timeval now) {
+   adns__consistency(ads,0,cc_entex);
+   adns__timeouts(ads, 0, tv_io,tvbuf, now);
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+ }
+ 
+ void adns_processtimeouts(adns_state ads, const struct timeval *now) {
+@@ -302,7 +299,7 @@
+   adns__consistency(ads,0,cc_entex);
+   adns__must_gettimeofday(ads,&now,&tv_buf);
+   if (now) adns__timeouts(ads, 1, 0,0, *now);
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+ }
+ 
+ /* fd handling functions.  These are the top-level of the real work of
+@@ -311,35 +308,47 @@
+ 
+ int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
+   /* Returns the number of entries filled in.  Always zeroes revents. */
++  int nwanted=0;
++#define ADD_POLLFD(wantfd, wantevents) do{	\
++    pollfds_buf[nwanted].fd= (wantfd);		\
++    pollfds_buf[nwanted].events= (wantevents);	\
++    pollfds_buf[nwanted].revents= 0;		\
++    nwanted++;					\
++  }while(0)
+ 
+-  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->nudpsockets; i++)
++    ADD_POLLFD(ads->udpsockets[i].fd, POLLIN);
++
+   switch (ads->tcpstate) {
+   case server_disconnected:
+   case server_broken:
+-    return 1;
++    break;
+   case server_connecting:
+-    pollfds_buf[1].events= POLLOUT;
++    ADD_POLLFD(ads->tcpsocket, POLLOUT);
+     break;
+   case server_ok:
+-    pollfds_buf[1].events= ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
++    ADD_POLLFD(ads->tcpsocket,
++	       ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI);
+     break;
+   default:
+     abort();
+   }
+-  pollfds_buf[1].fd= ads->tcpsocket;
+-  return 2;
++  assert(nwanted<=MAX_POLLFDS);
++#undef ADD_POLLFD
++  return nwanted;
+ }
+ 
+-int adns_processreadable(adns_state ads, ADNS_SOCKET fd, const struct timeval *now) {
+-  int want, dgramlen, r, udpaddrlen, serv, old_skip;
++int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
++  int want, dgramlen, r, i, udpaddrlen, serv, old_skip;
+   byte udpbuf[DNS_MAXUDP];
+-  struct sockaddr_in udpaddr;
+-
++  char addrbuf[ADNS_ADDR2TEXT_BUFLEN];
++  struct udpsocket *udp;
++  adns_sockaddr udpaddr;
++  
+   adns__consistency(ads,0,cc_entex);
+ 
+   switch (ads->tcpstate) {
+@@ -367,16 +376,15 @@
+ 	want= 2;
+       }
+       ads->tcprecv.used -= ads->tcprecv_skip;
+-      memmove(ads->tcprecv.buf,ads->tcprecv.buf+ads->tcprecv_skip, (size_t) ads->tcprecv.used);
++      memmove(ads->tcprecv.buf, ads->tcprecv.buf+ads->tcprecv_skip,
++	      ads->tcprecv.used);
+       ads->tcprecv_skip= 0;
+       if (!adns__vbuf_ensure(&ads->tcprecv,want)) { r= ENOMEM; goto xit; }
+       assert(ads->tcprecv.used <= ads->tcprecv.avail);
+       if (ads->tcprecv.used == ads->tcprecv.avail) continue;
+-	  ADNS_CLEAR_ERRNO;
+       r= adns_socket_read(ads->tcpsocket,
+ 	      ads->tcprecv.buf+ads->tcprecv.used,
+ 	      ads->tcprecv.avail-ads->tcprecv.used);
+-	  ADNS_CAPTURE_ERRNO;
+       if (r>0) {
+ 	ads->tcprecv.used+= r;
+       } else {
+@@ -385,7 +393,7 @@
+ 	  if (errno==EINTR) continue;
+ 	  if (errno_resources(errno)) { r= errno; goto xit; }
+ 	}
+-	adns__tcp_broken(ads,"adns_socket_read",r?strerror(errno):"closed");
++	adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
+       }
+     } while (ads->tcpstate == server_ok);
+     r= 0; goto xit;
+@@ -392,57 +400,42 @@
+   default:
+     abort();
+   }
+-  if (fd == ads->udpsocket) {
++  for (i=0; i<ads->nudpsockets; i++) {
++    udp= &ads->udpsockets[i];
++    if (fd != udp->fd) continue;
+     for (;;) {
+       udpaddrlen= sizeof(udpaddr);
+-	  ADNS_CLEAR_ERRNO;
+-      r= recvfrom(ads->udpsocket,(char*)udpbuf,sizeof(udpbuf),0,
+-		  (struct sockaddr*)&udpaddr,&udpaddrlen);
+-	  ADNS_CAPTURE_ERRNO;
++      r= recvfrom(fd,udpbuf,sizeof(udpbuf),0, &udpaddr.sa,&udpaddrlen);
+       if (r<0) {
+-	if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNRESET) { r= 0; goto xit; }
++	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 (%d)",strerror(errno), errno);
++	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;
++	     !adns__sockaddrs_equal(&udpaddr.sa,
++				    &ads->servers[serv].addr.sa);
+ 	   serv++);
+       if (serv >= ads->nservers) {
+ 	adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
+-		   inet_ntoa(udpaddr.sin_addr));
++		   adns__sockaddr_ntoa(&udpaddr.sa, addrbuf));
+ 	continue;
+       }
+       adns__procdgram(ads,udpbuf,r,serv,0,*now);
+     }
++    break;
+   }
+   r= 0;
+ xit:
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+ 
+-int adns_processwriteable(adns_state ads, ADNS_SOCKET fd, const struct timeval *now) {
++int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
+   int r;
+-
++  
+   adns__consistency(ads,0,cc_entex);
+ 
+   switch (ads->tcpstate) {
+@@ -455,20 +448,18 @@
+     assert(ads->tcprecv_skip==0);
+     for (;;) {
+       if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; }
+-	  ADNS_CLEAR_ERRNO;
+       r= adns_socket_read(ads->tcpsocket,&ads->tcprecv.buf,1);
+-	  ADNS_CAPTURE_ERRNO;
+       if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
+ 	tcp_connected(ads,*now);
+ 	r= 0; goto xit;
+       }
+       if (r>0) {
+-	adns__tcp_broken(ads,"connect/adns_socket_read","sent data before first request");
++	adns__tcp_broken(ads,"connect/read","sent data before first request");
+ 	r= 0; goto xit;
+       }
+       if (errno==EINTR) continue;
+       if (errno_resources(errno)) { r= errno; goto xit; }
+-      adns__tcp_broken(ads,"connect/adns_socket_read",strerror(errno));
++      adns__tcp_broken(ads,"connect/read",strerror(errno));
+       r= 0; goto xit;
+     } /* not reached */
+   case server_ok:
+@@ -475,19 +466,17 @@
+     if (fd != ads->tcpsocket) break;
+     while (ads->tcpsend.used) {
+       adns__sigpipe_protect(ads);
+-	  ADNS_CLEAR_ERRNO;
+       r= adns_socket_write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
+-	  ADNS_CAPTURE_ERRNO;
+       adns__sigpipe_unprotect(ads);
+       if (r<0) {
+ 	if (errno==EINTR) continue;
+ 	if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; }
+ 	if (errno_resources(errno)) { r= errno; goto xit; }
+-	adns__tcp_broken(ads,"adns_socket_write",strerror(errno));
++	adns__tcp_broken(ads,"write",strerror(errno));
+ 	r= 0; goto xit;
+       } else if (r>0) {
+ 	ads->tcpsend.used -= r;
+-	memmove(ads->tcpsend.buf,ads->tcpsend.buf+r, (size_t) ads->tcpsend.used);
++	memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
+       }
+     }
+     r= 0;
+@@ -497,11 +486,12 @@
+   }
+   r= 0;
+ xit:
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+-
+-int adns_processexceptional(adns_state ads, ADNS_SOCKET fd, const struct timeval *now) {
++  
++int adns_processexceptional(adns_state ads, int fd,
++			    const struct timeval *now) {
+   adns__consistency(ads,0,cc_entex);
+   switch (ads->tcpstate) {
+   case server_disconnected:
+@@ -515,25 +505,27 @@
+   default:
+     abort();
+   }
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return 0;
+ }
+ 
+-static void fd_event(adns_state ads, ADNS_SOCKET fd,
++static void fd_event(adns_state ads, int fd,
+ 		     int revent, int pollflag,
+ 		     int maxfd, const fd_set *fds,
+-		     int (*func)(adns_state, ADNS_SOCKET fd, const struct timeval *now),
++		     int (*func)(adns_state, int fd,
++				 const struct timeval *now),
+ 		     struct timeval now, int *r_r) {
+   int r;
+-
++  
+   if (!(revent & pollflag)) return;
+-  if (fds && !((int)fd<maxfd && FD_ISSET(fd,fds))) return;
++  if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return;
+   r= func(ads,fd,&now);
+   if (r) {
+     if (r_r) {
+       *r_r= r;
+     } else {
+-      adns__diag(ads,-1,0,"process fd failed after select: %s",strerror(errno));
++      adns__diag(ads,-1,0,"process fd failed after select:"
++		 " %s",strerror(errno));
+       adns_globalsystemfailure(ads);
+     }
+   }
+@@ -544,16 +536,18 @@
+ 		    int maxfd, const fd_set *readfds,
+ 		    const fd_set *writefds, const fd_set *exceptfds,
+ 		    struct timeval now, int *r_r) {
+-  int i, revents;
+-  ADNS_SOCKET fd;
++  int i, fd, revents;
+ 
+   for (i=0; i<npollfds; i++) {
+     fd= pollfds[i].fd;
+-    if ((int)fd >= maxfd) maxfd= fd+1;
++    if (fd >= maxfd) maxfd= fd+1;
+     revents= pollfds[i].revents;
+-    fd_event(ads,fd, revents,POLLIN, maxfd,readfds, adns_processreadable,now,r_r);
+-    fd_event(ads,fd, revents,POLLOUT, maxfd,writefds, adns_processwriteable,now,r_r);
+-    fd_event(ads,fd, revents,POLLPRI, maxfd,exceptfds, adns_processexceptional,now,r_r);
++#define EV(pollfl,fds,how)  \
++    fd_event(ads,fd, revents,pollfl, maxfd,fds, adns_process##how,now,r_r)
++    EV( POLLIN,  readfds,   readable    );
++    EV( POLLOUT, writefds,  writeable   );
++    EV( POLLPRI, exceptfds, exceptional );
++#undef EV
+   }
+ }
+ 
+@@ -565,9 +559,8 @@
+ 		       const struct timeval *now) {
+   struct timeval tv_nowbuf;
+   struct pollfd pollfds[MAX_POLLFDS];
+-  int i, maxfd, npollfds;
+-  ADNS_SOCKET fd;
+-
++  int i, fd, maxfd, npollfds;
++  
+   adns__consistency(ads,0,cc_entex);
+ 
+   if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
+@@ -581,7 +574,7 @@
+   maxfd= *maxfd_io;
+   for (i=0; i<npollfds; i++) {
+     fd= pollfds[i].fd;
+-    if ((int)fd >= maxfd) maxfd= fd+1;
++    if (fd >= maxfd) maxfd= fd+1;
+     if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io);
+     if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io);
+     if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io);
+@@ -589,7 +582,7 @@
+   *maxfd_io= maxfd;
+ 
+ xit:
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+ }
+ 
+ void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
+@@ -611,7 +604,7 @@
+ 		 maxfd,readfds,writefds,exceptfds,
+ 		 *now, 0);
+ xit:
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+ }
+ 
+ /* General helpful functions. */
+@@ -621,7 +614,7 @@
+ 
+   while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail);
+   while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail);
+-
++  
+   switch (ads->tcpstate) {
+   case server_connecting:
+   case server_ok:
+@@ -633,7 +626,7 @@
+   default:
+     abort();
+   }
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+ }
+ 
+ int adns_processany(adns_state ads) {
+@@ -649,7 +642,7 @@
+ 
+   /* We just use adns__fdevents to loop over the fd's trying them.
+    * This seems more sensible than calling select, since we're most
+-   * likely just to want to do a adns_socket_read on one or two fds anyway.
++   * likely just to want to do a read on one or two fds anyway.
+    */
+   npollfds= adns__pollfds(ads,pollfds);
+   for (i=0; i<npollfds; i++) pollfds[i].revents= pollfds[i].events & ~POLLPRI;
+@@ -658,7 +651,7 @@
+ 		 0,0,0,0,
+ 		 now,&r);
+ 
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return 0;
+ }
+ 
+@@ -700,7 +693,7 @@
+   int r, maxfd, rsel;
+   fd_set readfds, writefds, exceptfds;
+   struct timeval tvbuf, *tvp;
+-
++  
+   adns__consistency(ads,*query_io,cc_entex);
+   for (;;) {
+     r= adns__internal_check(ads,query_io,answer_r,context_r);
+@@ -709,9 +702,7 @@
+     FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
+     adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0);
+     assert(tvp);
+-	ADNS_CLEAR_ERRNO;
+     rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
+-	ADNS_CAPTURE_ERRNO;
+     if (rsel==-1) {
+       if (errno == EINTR) {
+ 	if (ads->iflags & adns_if_eintr) { r= EINTR; break; }
+@@ -724,7 +715,7 @@
+       adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0);
+     }
+   }
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+ 
+@@ -734,12 +725,12 @@
+ 	       void **context_r) {
+   struct timeval now;
+   int r;
+-
++  
+   adns__consistency(ads,*query_io,cc_entex);
+   r= gettimeofday(&now,0);
+   if (!r) adns__autosys(ads,now);
+ 
+   r= adns__internal_check(ads,query_io,answer_r,context_r);
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+Index: general.c
+===================================================================
+--- general.c	(revision 71177)
++++ general.c	(working copy)
+@@ -4,26 +4,25 @@
+  * - vbuf handling
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #include <stdlib.h>
+@@ -42,22 +41,35 @@
+ 
+ /* Core diagnostic functions */
+ 
++void adns__vlprintf(adns_state ads, const char *fmt, va_list al) {
++  ads->logfn(ads,ads->logfndata,fmt,al);
++}
++
++void adns__lprintf(adns_state ads, const char *fmt, ...) {
++  va_list al;
++  va_start(al,fmt);
++  adns__vlprintf(ads,fmt,al);
++  va_end(al);
++}
++
+ void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
+ 		 int serv, adns_query qu, const char *fmt, va_list al) {
++  char buf[ADNS_ADDR2TEXT_BUFLEN];
+   const char *bef, *aft;
+   vbuf vb;
+-
+-  if (!ads->diagfile ||
+-      (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent))))
++  
++  if (!ads->logfn ||
++      (!(ads->iflags & adns_if_debug)
++       && (!prevent || (ads->iflags & prevent))))
+     return;
+ 
+   if (ads->iflags & adns_if_logpid) {
+-    fprintf(ads->diagfile,"adns%s [%ld]: ",pfx,(long)getpid());
++    adns__lprintf(ads,"adns%s [%ld]: ",pfx,(long)getpid());
+   } else {
+-    fprintf(ads->diagfile,"adns%s: ",pfx);
++    adns__lprintf(ads,"adns%s: ",pfx);
+   }
+ 
+-  vfprintf(ads->diagfile,fmt,al);
++  adns__vlprintf(ads,fmt,al);
+ 
+   bef= " (";
+   aft= "\n";
+@@ -64,26 +76,28 @@
+ 
+   if (qu && qu->query_dgram) {
+     adns__vbuf_init(&vb);
+-    fprintf(ads->diagfile,"%sQNAME=%s, QTYPE=%s",
++    adns__lprintf(ads,"%sQNAME=%s, QTYPE=%s",
+ 	    bef,
+ 	    adns__diag_domain(qu->ads,-1,0, &vb,
+ 			      qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
+ 	    qu->typei ? qu->typei->rrtname : "<unknown>");
+     if (qu->typei && qu->typei->fmtname)
+-      fprintf(ads->diagfile,"(%s)",qu->typei->fmtname);
++      adns__lprintf(ads,"(%s)",qu->typei->fmtname);
+     bef=", "; aft=")\n";
+     adns__vbuf_free(&vb);
+   }
+-
++  
+   if (serv>=0) {
+-    fprintf(ads->diagfile,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
++    adns__lprintf(ads,"%sNS=%s",bef,
++		  adns__sockaddr_ntoa(&ads->servers[serv].addr.sa, buf));
+     bef=", "; aft=")\n";
+   }
+ 
+-  fputs(aft,ads->diagfile);
++  adns__lprintf(ads,"%s",aft);
+ }
+ 
+-void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
++void adns__debug(adns_state ads, int serv, adns_query qu,
++		 const char *fmt, ...) {
+   va_list al;
+ 
+   va_start(al,fmt);
+@@ -91,15 +105,18 @@
+   va_end(al);
+ }
+ 
+-void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
++void adns__warn(adns_state ads, int serv, adns_query qu,
++		const char *fmt, ...) {
+   va_list al;
+ 
+   va_start(al,fmt);
+-  adns__vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,qu,fmt,al);
++  adns__vdiag(ads," warning",
++	      adns_if_noerrprint|adns_if_noserverwarn, serv,qu,fmt,al);
+   va_end(al);
+ }
+ 
+-void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
++void adns__diag(adns_state ads, int serv, adns_query qu,
++		const char *fmt, ...) {
+   va_list al;
+ 
+   va_start(al,fmt);
+@@ -115,16 +132,16 @@
+ 
+ int adns__vbuf_ensure(vbuf *vb, int want) {
+   void *nb;
+-
++  
+   if (vb->avail >= want) return 1;
+-  nb= realloc(vb->buf, (size_t) want); if (!nb) return 0;
++  nb= realloc(vb->buf,want); if (!nb) return 0;
+   vb->buf= nb;
+   vb->avail= want;
+   return 1;
+ }
+-
++  
+ void adns__vbuf_appendq(vbuf *vb, const byte *data, int len) {
+-  memcpy(vb->buf+vb->used,data, (size_t) len);
++  memcpy(vb->buf+vb->used,data,len);
+   vb->used+= len;
+ }
+ 
+@@ -136,8 +153,8 @@
+   if (vb->avail < newlen) {
+     if (newlen<20) newlen= 20;
+     newlen <<= 1;
+-    nb= realloc(vb->buf,(size_t) newlen);
+-    if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf, (size_t) newlen); }
++    nb= realloc(vb->buf,newlen);
++    if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); }
+     if (!nb) return 0;
+     vb->buf= nb;
+     vb->avail= newlen;
+@@ -149,7 +166,7 @@
+ int adns__vbuf_appendstr(vbuf *vb, const char *data) {
+   int l;
+   l= strlen(data);
+-  return adns__vbuf_append(vb,(byte*)data,l);
++  return adns__vbuf_append(vb,data,l);
+ }
+ 
+ void adns__vbuf_free(vbuf *vb) {
+@@ -160,10 +177,12 @@
+ /* Additional diagnostic functions */
+ 
+ const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
+-			      vbuf *vb, const byte *dgram, int dglen, int cbyte) {
++			      vbuf *vb, const byte *dgram,
++			      int dglen, int cbyte) {
+   adns_status st;
+ 
+-  st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok, dgram,dglen,&cbyte,dglen);
++  st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok,
++			 dgram,dglen,&cbyte,dglen);
+   if (st == adns_s_nomemory) {
+     return "<cannot report domain... out of memory>";
+   }
+@@ -172,17 +191,20 @@
+     if (!(adns__vbuf_appendstr(vb,"<bad format... ") &&
+ 	  adns__vbuf_appendstr(vb,adns_strerror(st)) &&
+ 	  adns__vbuf_appendstr(vb,">") &&
+-	  adns__vbuf_append(vb,(byte*)"",1))) {
++	  adns__vbuf_append(vb,"",1))) {
+       return "<cannot report bad format... out of memory>";
+     }
+   }
+   if (!vb->used) {
+     adns__vbuf_appendstr(vb,"<truncated ...>");
+-    adns__vbuf_append(vb,(byte*)"",1);
++    adns__vbuf_append(vb,"",1);
+   }
+-  return (char*)vb->buf;
++  return vb->buf;
+ }
+ 
++int adns__getrrsz_default(const typeinfo *typei, adns_rrtype type)
++  { return typei->fixed_rrsz; }
++
+ adns_status adns_rr_info(adns_rrtype type,
+ 			 const char **rrtname_r, const char **fmtname_r,
+ 			 int *len_r,
+@@ -196,17 +218,17 @@
+ 
+   if (rrtname_r) *rrtname_r= typei->rrtname;
+   if (fmtname_r) *fmtname_r= typei->fmtname;
+-  if (len_r) *len_r= typei->rrsz;
++  if (len_r) *len_r= typei->getrrsz(typei, type);
+ 
+   if (!datap) return adns_s_ok;
+-
++  
+   adns__vbuf_init(&vb);
+   st= typei->convstring(&vb,datap);
+   if (st) goto x_freevb;
+-  if (!adns__vbuf_append(&vb,(byte*)"",1)) { st= adns_s_nomemory; goto x_freevb; }
+-  assert((int)strlen((char*)vb.buf) == vb.used-1);
+-  *data_r= realloc(vb.buf, (size_t) vb.used);
+-  if (!*data_r) *data_r= (char*)vb.buf;
++  if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
++  assert(strlen(vb.buf) == vb.used-1);
++  *data_r= realloc(vb.buf,vb.used);
++  if (!*data_r) *data_r= vb.buf;
+   return adns_s_ok;
+ 
+  x_freevb:
+@@ -222,36 +244,36 @@
+   const char *abbrev;
+   const char *string;
+ } sinfos[]= {
+-  SINFO(  ok,                  "OK"                                            ),
+-
+-  SINFO(  nomemory,            "Out of memory"                                 ),
+-  SINFO(  unknownrrtype,       "Query not implemented in DNS library"          ),
+-  SINFO(  systemfail,          "General resolver or system failure"            ),
+-
+-  SINFO(  timeout,             "DNS query timed out"                           ),
+-  SINFO(  allservfail,         "All nameservers failed"                        ),
+-  SINFO(  norecurse,           "Recursion denied by nameserver"                ),
+-  SINFO(  invalidresponse,     "Nameserver sent bad response"                  ),
+-  SINFO(  unknownformat,       "Nameserver used unknown format"                ),
+-
+-  SINFO(  rcodeservfail,       "Nameserver reports failure"                    ),
+-  SINFO(  rcodeformaterror,    "Query not understood by nameserver"            ),
+-  SINFO(  rcodenotimplemented, "Query not implemented by nameserver"           ),
+-  SINFO(  rcoderefused,        "Query refused by nameserver"                   ),
+-  SINFO(  rcodeunknown,        "Nameserver sent unknown response code"         ),
+-
+-  SINFO(  inconsistent,        "Inconsistent resource records in DNS"          ),
+-  SINFO(  prohibitedcname,     "DNS alias found where canonical name wanted"   ),
+-  SINFO(  answerdomaininvalid, "Found syntactically invalid domain name"       ),
+-  SINFO(  answerdomaintoolong, "Found overly-long domain name"                 ),
+-  SINFO(  invaliddata,         "Found invalid DNS data"                        ),
+-
+-  SINFO(  querydomainwrong,    "Domain invalid for particular DNS query type"  ),
+-  SINFO(  querydomaininvalid,  "Domain name is syntactically invalid"          ),
+-  SINFO(  querydomaintoolong,  "Domain name or component is too long"          ),
+-
+-  SINFO(  nxdomain,            "No such domain"                                ),
+-  SINFO(  nodata,              "No such data"                                  )
++  SINFO( ok,                  "OK"                                           ),
++									      
++  SINFO( nomemory,            "Out of memory"                                ),
++  SINFO( unknownrrtype,       "Query not implemented in DNS library"         ),
++  SINFO( systemfail,          "General resolver or system failure"           ),
++									      
++  SINFO( timeout,             "DNS query timed out"                          ),
++  SINFO( allservfail,         "All nameservers failed"                       ),
++  SINFO( norecurse,           "Recursion denied by nameserver"               ),
++  SINFO( invalidresponse,     "Nameserver sent bad response"                 ),
++  SINFO( unknownformat,       "Nameserver used unknown format"               ),
++									      
++  SINFO( rcodeservfail,       "Nameserver reports failure"                   ),
++  SINFO( rcodeformaterror,    "Query not understood by nameserver"           ),
++  SINFO( rcodenotimplemented, "Query not implemented by nameserver"          ),
++  SINFO( rcoderefused,        "Query refused by nameserver"                  ),
++  SINFO( rcodeunknown,        "Nameserver sent unknown response code"        ),
++  									      
++  SINFO( inconsistent,        "Inconsistent resource records in DNS"         ),
++  SINFO( prohibitedcname,     "DNS alias found where canonical name wanted"  ),
++  SINFO( answerdomaininvalid, "Found syntactically invalid domain name"      ),
++  SINFO( answerdomaintoolong, "Found overly-long domain name"                ),
++  SINFO( invaliddata,         "Found invalid DNS data"                       ),
++									      
++  SINFO( querydomainwrong,    "Domain invalid for particular DNS query type" ),
++  SINFO( querydomaininvalid,  "Domain name is syntactically invalid"         ),
++  SINFO( querydomaintoolong,  "Domain name or component is too long"         ),
++									      
++  SINFO( nxdomain,            "No such domain"                               ),
++  SINFO( nodata,              "No such data"                                 )
+ };
+ 
+ static int si_compar(const void *key, const void *elem) {
+@@ -262,7 +284,8 @@
+ }
+ 
+ static const struct sinfo *findsinfo(adns_status st) {
+-  return bsearch(&st,sinfos,sizeof(sinfos)/sizeof(*sinfos),sizeof(*sinfos),si_compar);
++  return bsearch(&st,sinfos, sizeof(sinfos)/sizeof(*sinfos),
++		 sizeof(*sinfos), si_compar);
+ }
+ 
+ const char *adns_strerror(adns_status st) {
+@@ -304,7 +327,7 @@
+   here= *st;
+   min= (sti==stinfos) ? 0 : sti[-1].stmax+1;
+   max= sti->stmax;
+-
++  
+   return here < min  ? -1 : here > max ? 1 : 0;
+ }
+ 
+@@ -311,7 +334,8 @@
+ const char *adns_errtypeabbrev(adns_status st) {
+   const struct stinfo *sti;
+ 
+-  sti= bsearch(&st,stinfos,sizeof(stinfos)/sizeof(*stinfos),sizeof(*stinfos),sti_compar);
++  sti= bsearch(&st,stinfos, sizeof(stinfos)/sizeof(*stinfos),
++	       sizeof(*stinfos), sti_compar);
+   return sti->abbrev;
+ }
+ 
+@@ -327,9 +351,9 @@
+ 	 place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
+ 	 place--);
+     if (place != i) {
+-      memcpy(tempbuf, data + i*sz, (size_t) sz);
+-      memmove(data + (place+1)*sz, data + place*sz, (size_t) (i-place)*sz);
+-      memcpy(data + place*sz, tempbuf, (size_t) sz);
++      memcpy(tempbuf, data + i*sz, sz);
++      memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
++      memcpy(data + place*sz, tempbuf, sz);
+     }
+   }
+ }
+@@ -351,7 +375,7 @@
+   sa.sa_handler= SIG_IGN;
+   sigfillset(&sa.sa_mask);
+   sa.sa_flags= 0;
+-
++  
+   r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
+   r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
+ #endif
+Index: internal.h
+===================================================================
+--- internal.h	(revision 71177)
++++ internal.h	(working copy)
+@@ -5,16 +5,16 @@
+  * - comments regarding library data structures
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
+  *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+  *
+  *  This program is distributed in the hope that it will be useful,
+@@ -23,8 +23,7 @@
+  *  GNU General Public License for more details.
+  *
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #ifndef ADNS_INTERNAL_H_INCLUDED
+@@ -36,7 +35,6 @@
+ typedef unsigned char byte;
+ #endif
+ 
+-
+ #include <stdarg.h>
+ #include <assert.h>
+ 
+@@ -46,11 +44,14 @@
+ #include <signal.h>
+ #include <errno.h>
+ #include <string.h>
++#include <stdlib.h>
+ 
+ #ifndef ADNS_JGAA_WIN32
++#  include <stdbool.h>
+ #  include <sys/time.h>
+ #endif
+ 
++#define ADNS_FEATURE_MANYAF
+ #include "adns.h"
+ #include "dlist.h"
+ 
+@@ -77,10 +78,29 @@
+ #define DNS_IDOFFSET 0
+ #define DNS_CLASS_IN 1
+ 
+-#define DNS_INADDR_ARPA "in-addr", "arpa"
+-
+ #define MAX_POLLFDS  ADNS_POLLFDS_RECOMMENDED
+ 
++/* Some preprocessor hackery */
++
++#define GLUE(x, y) GLUE_(x, y)
++#define GLUE_(x, y) x##y
++
++/* C99 macro `...' must match at least one argument, so the naive definition
++ * `#define CAR(car, ...) car' won't work.  But it's easy to arrange for the
++ * tail to be nonempty if we're just going to discard it anyway. */
++#define CAR(...) CAR_(__VA_ARGS__, _)
++#define CAR_(car, ...) car
++
++/* Extracting the tail of an argument list is rather more difficult.  The
++ * following trick is based on one by Laurent Deniau to count the number of
++ * arguments to a macro, simplified in two ways: (a) it only handles up to
++ * eight arguments, and (b) it only needs to distinguish the one-argument
++ * case from many arguments. */
++#define CDR(...) CDR_(__VA_ARGS__, m, m, m, m, m, m, m, 1, _)(__VA_ARGS__)
++#define CDR_(_1, _2, _3, _4, _5, _6, _7, _8, n, ...) CDR_##n
++#define CDR_1(_)
++#define CDR_m(_, ...) __VA_ARGS__
++
+ typedef enum {
+   cc_user,
+   cc_entex,
+@@ -96,17 +116,13 @@
+   rcode_refused
+ } dns_rcode;
+ 
++enum {
++  adns__qf_addr_answer= 0x01000000,/* addr query received an answer */
++  adns__qf_addr_cname = 0x02000000 /* addr subquery performed on cname */
++};
++
+ /* Shared data structures */
+ 
+-typedef union {
+-  adns_status status;
+-  char *cp;
+-  adns_rrtype type;
+-  int i;
+-  struct in_addr ia;
+-  unsigned long ul;
+-} rr_align;
+-
+ typedef struct {
+   int used, avail;
+   byte *buf;
+@@ -121,11 +137,41 @@
+   struct timeval now;
+ } parseinfo;
+ 
++#define MAXREVLABELS 34		/* keep in sync with addrfam! */
++struct revparse_state {
++  uint16_t labstart[MAXREVLABELS];
++  uint8_t lablen[MAXREVLABELS];
++};
++
++union checklabel_state {
++  struct revparse_state ptr;
++};
++
+ typedef struct {
+-  adns_rrtype type;
++  void *ext;
++  void (*callback)(adns_query parent, adns_query child);
++
++  union {
++    struct {
++      adns_rrtype rev_rrtype;
++      adns_sockaddr addr;
++    } ptr;
++    struct {
++      unsigned want, have;
++    } addr;
++  } tinfo; /* type-specific state for the query itself: zero-init if you
++	    * don't know better. */
++
++  union {
++    adns_rr_hostaddr *hostaddr;
++  } pinfo; /* state for use by parent's callback function */
++} qcontext;
++
++typedef struct typeinfo {
++  adns_rrtype typekey;
+   const char *rrtname;
+   const char *fmtname;
+-  int rrsz;
++  int fixed_rrsz;
+ 
+   void (*makefinal)(adns_query qu, void *data);
+   /* Change memory management of *data.
+@@ -138,11 +184,12 @@
+    * and will not be null-terminated by convstring.
+    */
+ 
+-  adns_status (*parse)(const parseinfo *pai, int cbyte, int max, void *store_r);
++  adns_status (*parse)(const parseinfo *pai, int cbyte,
++		       int max, void *store_r);
+   /* Parse one RR, in dgram of length dglen, starting at cbyte and
+    * extending until at most max.
+    *
+-   * The RR should be stored at *store_r, of length qu->typei->rrsz.
++   * The RR should be stored at *store_r, of length qu->typei->getrrsz().
+    *
+    * If there is an overrun which might indicate truncation, it should set
+    * *rdstart to -1; otherwise it may set it to anything else positive.
+@@ -150,14 +197,54 @@
+    * nsstart is the offset of the authority section.
+    */
+ 
+-  int (*diff_needswap)(adns_state ads, const void *datap_a, const void *datap_b);
++  int (*diff_needswap)(adns_state ads,const void *datap_a,const void *datap_b);
+   /* Returns !0 if RR a should be strictly after RR b in the sort order,
+    * 0 otherwise.  Must not fail.
+    */
++
++  adns_status (*checklabel)(adns_state ads, adns_queryflags flags,
++			    union checklabel_state *cls, qcontext *ctx,
++			    int labnum, const char *dgram,
++			    int labstart, int lablen);
++  /* Check a label from the query domain string.  The label is not
++   * necessarily null-terminated.  The hook can refuse the query's submission
++   * by returning a nonzero status.  State can be stored in *cls between
++   * calls, and useful information can be stashed in ctx->tinfo, to be stored
++   * with the query (e.g., it will be available to the parse hook).  This
++   * hook can detect a first call because labnum is zero, and a final call
++   * because lablen is zero.
++   */
++
++  void (*postsort)(adns_state ads, void *array, int nrrs, int rrsz,
++		   const struct typeinfo *typei);
++  /* Called immediately after the RRs have been sorted, and may rearrange
++   * them.  (This is really for the benefit of SRV's bizarre weighting
++   * stuff.)  May be 0 to mean nothing needs to be done.
++   */
++
++  int (*getrrsz)(const struct typeinfo *typei, adns_rrtype type);
++  /* Return the output resource-record element size; if this is null, then
++   * the rrsz member can be used.
++   */
++
++  void (*query_send)(adns_query qu, struct timeval now);
++  /* Send the query to nameservers, and hook it into the appropriate queue.
++   * Normal behaviour is to call adns__query_send, but this can be overridden
++   * for special effects.
++   */
+ } typeinfo;
+ 
++adns_status adns__ckl_hostname(adns_state ads, adns_queryflags flags,
++			       union checklabel_state *cls,
++			       qcontext *ctx, int labnum,
++			       const char *dgram, int labstart, int lablen);
++  /* implemented in query.c, used by types.c as default
++   * and as part of implementation for some fancier types
++   * doesn't require any state */
++
+ typedef struct allocnode {
+   struct allocnode *next, *back;
++  size_t sz;
+ } allocnode;
+ 
+ union maxalign {
+@@ -167,17 +254,8 @@
+   void *p;
+   void (*fp)(void);
+   union maxalign *up;
+-};
++} data;
+ 
+-typedef struct {
+-  void *ext;
+-  void (*callback)(adns_query parent, adns_query child);
+-  union {
+-    adns_rr_addr ptr_parent_addr;
+-    adns_rr_hostaddr *hostaddr;
+-  } info;
+-} qcontext;
+-
+ struct adns__query {
+   adns_state ads;
+   enum { query_tosend, query_tcpw, query_childw, query_done } state;
+@@ -250,6 +328,10 @@
+    * Queries in state tcpw/tcpw have been sent (or are in the to-send buffer)
+    * iff the tcp connection is in state server_ok.
+    *
++   * Internal queries (from adns__submit_internal) end up on intdone
++   * instead of output, and the callbacks are made on the way out of
++   * adns, to avoid reentrancy hazards.
++   *
+    *			      +------------------------+
+    *             START -----> |      tosend/NONE       |
+    *			      +------------------------+
+@@ -288,14 +370,18 @@
+ 
+ struct query_queue { adns_query head, tail; };
+ 
++#define MAXUDP 2
++
+ struct adns__state {
+   adns_initflags iflags;
+-  FILE *diagfile;
++  adns_logcallbackfn *logfn;
++  void *logfndata;
+   int configerrno;
+-  struct query_queue udpw, tcpw, childw, output;
++  struct query_queue udpw, tcpw, childw, output, intdone;
+   adns_query forallnext;
+-  int nextid;
+-  ADNS_SOCKET udpsocket, tcpsocket;
++  int nextid, tcpsocket;
++  struct udpsocket { int af; int fd; } udpsockets[MAXUDP];
++  int nudpsockets;
+   vbuf tcpsend, tcprecv;
+   int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip;
+   enum adns__tcpstate {
+@@ -313,21 +399,120 @@
+   sigset_t stdsigmask;
+ #endif
+   struct pollfd pollfds_buf[MAX_POLLFDS];
+-  struct server {
+-    struct in_addr addr;
+-  } servers[MAXSERVERS];
++  adns_rr_addr servers[MAXSERVERS];
+   struct sortlist {
+-    struct in_addr base, mask;
++    adns_sockaddr base, mask;
+   } sortlist[MAXSORTLIST];
+   char **searchlist;
++  unsigned config_report_unknown:1;
++  unsigned short rand48xsubi[3];
+ };
+ 
++/* From addrfam.c: */
++
++extern int adns__addrs_equal_raw(const struct sockaddr *a,
++				 int bf, const void *b);
++/* Returns nonzero a's family is bf and a's protocol address field
++ * refers to the same protocol address as that stored at ba.
++ */
++
++extern int adns__addrs_equal(const adns_sockaddr *a,
++			     const adns_sockaddr *b);
++/* Returns nonzero if the two refer to the same protocol address
++ * (disregarding port, IPv6 scope, etc).
++ */
++
++extern int adns__sockaddrs_equal(const struct sockaddr *sa,
++				 const struct sockaddr *sb);
++/* Return nonzero if the two socket addresses are equal (in all significant
++ * respects).
++ */
++
++extern int adns__addr_width(int af);
++/* Return the width of addresses of family af, in bits. */
++
++extern void adns__prefix_mask(adns_sockaddr *sa, int len);
++/* Stores in sa's protocol address field an address mask for address
++ * family af, whose first len bits are set and the remainder are
++ * clear.  On entry, sa's af field must be set.  This is what you want
++ * for converting a prefix length into a netmask.
++ */
++
++extern int adns__guess_prefix_length(const adns_sockaddr *addr);
++/* Given a network base address, guess the appropriate prefix length based on
++ * the appropriate rules for the address family (e.g., for IPv4, this uses
++ * the old address classes).
++ */
++
++extern int adns__addr_matches(int af, const void *addr,
++			      const adns_sockaddr *base,
++			      const adns_sockaddr *mask);
++/* Return nonzero if the protocol address specified by af and addr
++ * lies within the network specified by base and mask.
++ */
++
++extern void adns__addr_inject(const void *a, adns_sockaddr *sa);
++/* Injects the protocol address *a into the socket adress sa.  Assumes
++ * that sa->sa_family is already set correctly.
++ */
++
++extern const void *adns__sockaddr_addr(const struct sockaddr *sa);
++/* Returns the address of the protocol address field in sa.
++ */
++
++char *adns__sockaddr_ntoa(const struct sockaddr *sa, char *buf);
++/* Converts sa to a string, and writes it to buf, which must be at
++ * least ADNS_ADDR2TEXT_BUFLEN bytes long (unchecked).  Returns buf;
++ * can't fail.
++ */
++
++extern int adns__make_reverse_domain(const struct sockaddr *sa,
++				     const char *zone,
++				     char **buf_io, size_t bufsz,
++				     char **buf_free_r);
++/* Construct a reverse domain string, given a socket address and a parent
++ * zone.  If zone is null, then use the standard reverse-lookup zone for the
++ * address family.  If the length of the resulting string is no larger than
++ * bufsz, then the result is stored starting at *buf_io; otherwise a new
++ * buffer is allocated is used, and a pointer to it is stored in both *buf_io
++ * and *buf_free_r (the latter of which should be null on entry).  If
++ * something goes wrong, then an errno value is returned: ENOSYS if the
++ * address family of sa isn't recognized, or ENOMEM if the attempt to
++ * allocate an output buffer failed.
++ */
++
++extern bool adns__revparse_label(struct revparse_state *rps, int labnum,
++				 const char *dgram,
++				 int labstart, int lablen);
++/* Parse a label in a reverse-domain name, given its index labnum (starting
++ * from zero), a pointer to its contents (which need not be null-terminated),
++ * and its length.  The state in *rps is initialized implicitly when labnum
++ * is zero.
++ *
++ * Returns 1 if the parse is proceeding successfully, 0 if the domain
++ * name is definitely invalid and the parse must be abandoned.
++ */
++
++extern bool adns__revparse_done(struct revparse_state *rps,
++				const char *dgram, int nlabels,
++				adns_rrtype *rrtype_r, adns_sockaddr *addr_r);
++/* Finishes parsing a reverse-domain name, given the total number of
++ * labels in the name.  On success, fills in the af and protocol
++ * address in *addr_r, and the forward query type in *rrtype_r
++ * (because that turns out to be useful).  Returns 1 if the parse
++ * was successful.
++ */
++
+ /* From setup.c: */
+ 
+-int adns__setnonblock(adns_state ads, ADNS_SOCKET fd); /* => errno value */
++int adns__setnonblock(adns_state ads, int fd); /* => errno value */
+ 
+ /* From general.c: */
+ 
++void adns__vlprintf(adns_state ads, const char *fmt, va_list al);
++void adns__lprintf(adns_state ads, const char *fmt,
++		   ...) PRINTFFORMAT(2,3);
++
+ void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
+ 		 int serv, adns_query qu, const char *fmt, va_list al);
+ 
+@@ -339,7 +524,7 @@
+ 		const char *fmt, ...) PRINTFFORMAT(4,5);
+ 
+ int adns__vbuf_ensure(vbuf *vb, int want);
+-int adns__vbuf_appendstr(vbuf *vb, const char *data); /* does not include nul */
++int adns__vbuf_appendstr(vbuf *vb, const char *data); /* doesn't include nul */
+ int adns__vbuf_append(vbuf *vb, const byte *data, int len);
+ /* 1=>success, 0=>realloc failed */
+ void adns__vbuf_appendq(vbuf *vb, const byte *data, int len);
+@@ -347,7 +532,8 @@
+ void adns__vbuf_free(vbuf *vb);
+ 
+ const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
+-			      vbuf *vb, const byte *dgram, int dglen, int cbyte);
++			      vbuf *vb,
++			      const byte *dgram, int dglen, int cbyte);
+ /* Unpicks a domain in a datagram and returns a string suitable for
+  * printing it as.  Never fails - if an error occurs, it will
+  * return some kind of string describing the error.
+@@ -359,6 +545,11 @@
+  * vb before using the return value.
+  */
+ 
++int adns__getrrsz_default(const typeinfo *typei, adns_rrtype type);
++/* Default function for the `getrrsz' type hook; returns the `fixed_rrsz'
++ * value from the typeinfo entry.
++ */
++
+ void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
+ 		 int (*needswap)(void *context, const void *a, const void *b),
+ 		 void *context);
+@@ -381,12 +572,14 @@
+ 
+ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+ 			  const char *owner, int ol,
+-			  const typeinfo *typei, adns_queryflags flags);
++			  const typeinfo *typei, adns_rrtype type,
++			  adns_queryflags flags);
+ /* Assembles a query packet in vb.  A new id is allocated and returned.
+  */
+ 
+ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+-				  const byte *qd_dgram, int qd_dglen, int qd_begin,
++				  const byte *qd_dgram, int qd_dglen,
++				  int qd_begin,
+ 				  adns_rrtype type, adns_queryflags flags);
+ /* Same as adns__mkquery, but takes the owner domain from an existing datagram.
+  * That domain must be correct and untruncated.
+@@ -398,11 +591,20 @@
+  * might be broken, but no reconnect will be attempted.
+  */
+ 
++struct udpsocket *adns__udpsocket_by_af(adns_state ads, int af);
++/* Find the UDP socket structure in ads which has the given address family.
++ * Return null if there isn't one.
++ *
++ * This is used during initialization, so ads is only partially filled in.
++ * The requirements are that nudp is set, and that udpsocket[i].af are
++ * defined for 0<=i<nudp.
++ */
++
+ void adns__query_send(adns_query qu, struct timeval now);
+ /* Query must be in state tosend/NONE; it will be moved to a new state,
+  * and no further processing can be done on it for now.
+- * (Resulting state is one of udp/timew, tcpwait/timew (if server not connected),
+- *  tcpsent/timew, child/childw or done/output.)
++ * (Resulting state is one of udp/timew, tcpwait/timew (if server not
++ * connected), tcpsent/timew, child/childw or done/output.)
+  * __query_send may decide to use either UDP or TCP depending whether
+  * _qf_usevc is set (or has become set) and whether the query is too
+  * large.
+@@ -411,9 +613,11 @@
+ /* From query.c: */
+ 
+ adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+-				  const typeinfo *typei, vbuf *qumsg_vb, int id,
++				  adns_query parent,
++				  const typeinfo *typei, adns_rrtype type,
++				  vbuf *qumsg_vb, int id,
+ 				  adns_queryflags flags, struct timeval now,
+-				  const qcontext *ctx);
++				  qcontext *ctx);
+ /* Submits a query (for internal use, called during external submits).
+  *
+  * The new query is returned in *query_r, or we return adns_s_nomemory.
+@@ -422,7 +626,8 @@
+  * the memory for it is _taken over_ by this routine whether it
+  * succeeds or fails (if it succeeds, the vbuf is reused for qu->vb).
+  *
+- * *ctx is copied byte-for-byte into the query.
++ * *ctx is copied byte-for-byte into the query.  Before doing this, its tinfo
++ * field may be modified by type hooks.
+  *
+  * When the child query is done, ctx->callback will be called.  The
+  * child will already have been taken off both the global list of
+@@ -472,7 +677,7 @@
+  *  answer->cname and answer->owner are _preserved.
+  */
+ 
+-void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz);
++void adns__transfer_interim(adns_query from, adns_query to, void *block);
+ /* Transfers an interim allocation from one query to another, so that
+  * the `to' query will have room for the data when we get to makefinal
+  * and so that the free will happen when the `to' query is freed
+@@ -486,6 +691,10 @@
+  * TTLs get inherited by their parents.
+  */
+ 
++void adns__free_interim(adns_query qu, void *p);
++/* Forget about a block allocated by adns__alloc_interim.
++ */
++
+ void *adns__alloc_mine(adns_query qu, size_t sz);
+ /* Like _interim, but does not record the length for later
+  * copying into the answer.  This just ensures that the memory
+@@ -507,9 +716,19 @@
+  * in a datagram and discover that we need to retry the query.
+  */
+ 
++void adns__cancel(adns_query qu);
+ void adns__query_done(adns_query qu);
+-void adns__query_fail(adns_query qu, adns_status stat);
++void adns__query_fail(adns_query qu, adns_status st);
++void adns__cancel_children(adns_query qu);
+ 
++void adns__returning(adns_state ads, adns_query qu);
++/* Must be called before returning from adns any time that we have
++ * progressed (including made, finished or destroyed) queries.
++ *
++ * Might reenter adns via internal query callbacks, so
++ * external-faciing functions which call adns__returning should
++ * normally be avoided in internal code. */
++
+ /* From reply.c: */
+ 
+ void adns__procdgram(adns_state ads, const byte *dgram, int len,
+@@ -549,7 +768,8 @@
+  * serv may be -1, qu may be null - they are for error reporting.
+  */
+ 
+-adns_status adns__findlabel_next(findlabel_state *fls, int *lablen_r, int *labstart_r);
++adns_status adns__findlabel_next(findlabel_state *fls,
++				 int *lablen_r, int *labstart_r);
+ /* Then, call this one repeatedly.
+  *
+  * It will return adns_s_ok if all is well, and tell you the length
+@@ -579,8 +799,9 @@
+ } parsedomain_flags;
+ 
+ adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu,
+-			       vbuf *vb, adns_queryflags flags,
+-			       const byte *dgram, int dglen, int *cbyte_io, int max);
++			       vbuf *vb, parsedomain_flags flags,
++			       const byte *dgram, int dglen, int *cbyte_io,
++			       int max);
+ /* vb must already have been initialised; it will be reset if necessary.
+  * If there is truncation, vb->used will be set to 0; otherwise
+  * (if there is no error) vb will be null-terminated.
+@@ -590,7 +811,8 @@
+  */
+ 
+ adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads,
+-				    adns_query qu, vbuf *vb, parsedomain_flags flags,
++				    adns_query qu, vbuf *vb,
++				    parsedomain_flags flags,
+ 				    const byte *dgram);
+ /* Like adns__parse_domain, but you pass it a pre-initialised findlabel_state,
+  * for continuing an existing domain or some such of some kind.  Also, unlike
+@@ -629,10 +851,11 @@
+ 
+ adns_status adns__findrr_anychk(adns_query qu, int serv,
+ 				const byte *dgram, int dglen, int *cbyte_io,
+-				int *type_r, int *class_r, unsigned long *ttl_r,
++				int *type_r, int *class_r,
++				unsigned long *ttl_r,
+ 				int *rdlen_r, int *rdstart_r,
+-				const byte *eo_dgram, int eo_dglen, int eo_cbyte,
+-				int *eo_matched_r);
++				const byte *eo_dgram, int eo_dglen,
++				int eo_cbyte, int *eo_matched_r);
+ /* Like adns__findrr_checked, except that the datagram and
+  * owner to compare with can be specified explicitly.
+  *
+@@ -647,12 +870,13 @@
+  * untruncated.
+  */
+ 
+-void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now);
++void adns__update_expires(adns_query qu, unsigned long ttl,
++			  struct timeval now);
+ /* Updates the `expires' field in the query, so that it doesn't exceed
+  * now + ttl.
+  */
+ 
+-int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len);
++bool adns__labels_equal(const byte *a, int al, const byte *b, int bl);
+ 
+ /* From event.c: */
+ 
+@@ -669,6 +893,7 @@
+ 
+ void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
+ 			     struct timeval *tv_buf);
++/* Call with care - might reentrantly cause queries to be completed! */
+ 
+ int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]);
+ void adns__fdevents(adns_state ads,
+@@ -694,12 +919,19 @@
+ 
+ /* Useful static inline functions: */
+ 
+-static inline int ctype_whitespace(int c) { return c==' ' || c=='\n' || c=='\t'; }
++static inline int ctype_whitespace(int c) {
++  return c==' ' || c=='\n' || c=='\t';
++}
+ static inline int ctype_digit(int c) { return c>='0' && c<='9'; }
+ static inline int ctype_alpha(int c) {
+   return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ }
+-static inline int ctype_822special(int c) { return strchr("()<>@,;:\\\".[]",c) != 0; }
++static inline int ctype_toupper(int c) {
++  return ctype_alpha(c) ? (c & ~32) : c;
++}
++static inline int ctype_822special(int c) {
++  return strchr("()<>@,;:\\\".[]",c) != 0;
++}
+ static inline int ctype_domainunquoted(int c) {
+   return ctype_alpha(c) || ctype_digit(c) || (strchr("-_/+",c) != 0);
+ }
+@@ -708,18 +940,18 @@
+ 
+ /* Useful macros */
+ 
+-#define MEM_ROUND(sz) \
+-  (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \
++#define MEM_ROUND(sz)						\
++  (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) )	\
+    * sizeof(union maxalign) )
+ 
+ #define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff)
+ #define GET_B(cb,tv) ((tv)= GETIL_B((cb)))
+-#define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
+-#define GET_L(cb,tv) ( (tv)=0, \
+-		       (tv)|=(GETIL_B((cb))<<24), \
+-		       (tv)|=(GETIL_B((cb))<<16), \
+-		       (tv)|=(GETIL_B((cb))<<8), \
+-		       (tv)|=GETIL_B(cb), \
++#define GET_W(cb,tv) ((tv)=0,(tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
++#define GET_L(cb,tv) ( (tv)=0,				\
++		       (tv)|=(GETIL_B((cb))<<24),	\
++		       (tv)|=(GETIL_B((cb))<<16),	\
++		       (tv)|=(GETIL_B((cb))<<8),	\
++		       (tv)|=GETIL_B(cb),		\
+ 		       (tv) )
+ 
+ #endif
+Index: Makefile.in
+===================================================================
+--- Makefile.in	(revision 71177)
++++ Makefile.in	(working copy)
+@@ -1,15 +1,15 @@
+-# src/Makefile - library main Makefile
++# src/Makefile[.in] - library main Makefile
+ # 
+-#  This file is
+-#    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+-#
+-#  It is part of adns, which is
+-#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+-#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
++#  This file is part of adns, which is
++#    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++#    Copyright (C) 2014  Mark Wooding
++#    Copyright (C) 1999-2000,2003,2006  Tony Finch
++#    Copyright (C) 1991 Massachusetts Institute of Technology
++#  (See the file INSTALL for full details.)
+ #  
+ #  This program is free software; you can redistribute it and/or modify
+ #  it under the terms of the GNU General Public License as published by
+-#  the Free Software Foundation; either version 2, or (at your option)
++#  the Free Software Foundation; either version 3, or (at your option)
+ #  any later version.
+ #  
+ #  This program is distributed in the hope that it will be useful,
+@@ -18,24 +18,26 @@
+ #  GNU General Public License for more details.
+ #  
+ #  You should have received a copy of the GNU General Public License
+-#  along with this program; if not, write to the Free Software Foundation,
+-#  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
++#  along with this program; if not, write to the Free Software Foundation.
+ 
+ srcdir=		@srcdir@
+ VPATH=		@srcdir@
+ 
+ TARGETS=	libadns.a
+-include		$(srcdir)/../settings.make
+-include		adns.make
++include		../settings.make
++include		$(srcdir)/adns.make
+ 
++DIRCFLAGS=	-I. -I$(srcdir)
++
+ install:
++		mkdir -p $(libdir) $(includedir)
+ 		set -xe; for f in $(TARGETS); \
+-			do $(INSTALL_DATA) $$f $(lib_dir)/$$f; done
+-		$(INSTALL_DATA) $(srcdir)/../src/adns.h $(include_dir)/adns.h
++			do $(INSTALL_DATA) $$f $(libdir)/$$f; done
++		$(INSTALL_DATA) $(srcdir)/../src/adns.h $(includedir)/adns.h
+ 
+ uninstall:
+-		for f in $(TARGETS); do rm -f $(lib_dir)/$$f; done
+-		rm -f $(include_dir)/adns.h
++		for f in $(TARGETS); do rm -f $(libdir)/$$f; done
++		rm -f $(includedir)/adns.h
+ 
+ ALLOBJS=	$(LIBOBJS)
+ 
+Index: parse.c
+===================================================================
+--- parse.c	(revision 71177)
++++ parse.c	(working copy)
+@@ -3,38 +3,33 @@
+  * - parsing assistance functions (mainly for domains inside datagrams)
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+-#ifdef ADNS_JGAA_WIN32
+-# include "adns_win32.h"
+-#endif
+-
+ #include "internal.h"
+ 
+-int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len) {
++static int vbuf_append_quoted1035(vbuf *vb, const byte *buf, int len) {
+   char qbuf[10];
+   int i, ch;
+-
++  
+   while (len) {
+     qbuf[0]= 0;
+     for (i=0; i<len; i++) {
+@@ -47,7 +42,8 @@
+ 	break;
+       }
+     }
+-    if (!adns__vbuf_append(vb,buf,i) || !adns__vbuf_append(vb,(byte*)qbuf,(int)  strlen(qbuf)))
++    if (!adns__vbuf_append(vb,buf,i) ||
++	!adns__vbuf_append(vb,qbuf,strlen(qbuf)))
+       return 0;
+     if (i<len) i++;
+     buf+= i;
+@@ -76,7 +72,7 @@
+   int lablen, jumpto;
+   const char *dgram;
+ 
+-  dgram= (char*)fls->dgram;
++  dgram= fls->dgram;
+   for (;;) {
+     if (fls->cbyte >= fls->dglen) goto x_truncated;
+     if (fls->cbyte >= fls->max) goto x_badresponse;
+@@ -109,23 +105,27 @@
+   *lablen_r= -1;
+   return adns_s_ok;
+ 
+- x_badresponse:
+-  adns__diag(fls->ads,fls->serv,fls->qu,"label in domain runs beyond end of domain");
++ x_badresponse: 
++  adns__diag(fls->ads,fls->serv,fls->qu,
++	     "label in domain runs beyond end of domain");
+   return adns_s_invalidresponse;
+ }
+ 
+ adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu,
+-			       vbuf *vb, adns_queryflags flags,
+-			       const byte *dgram, int dglen, int *cbyte_io, int max) {
++			       vbuf *vb, parsedomain_flags flags,
++			       const byte *dgram, int dglen, int *cbyte_io,
++			       int max) {
+   findlabel_state fls;
+-
+-  adns__findlabel_start(&fls,ads, serv,qu, dgram,dglen,max, *cbyte_io,cbyte_io);
++  
++  adns__findlabel_start(&fls,ads, serv,qu, dgram,dglen,max,
++			*cbyte_io,cbyte_io);
+   vb->used= 0;
+   return adns__parse_domain_more(&fls,ads,qu, vb,flags,dgram);
+ }
+ 
+ adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads,
+-				    adns_query qu, vbuf *vb, parsedomain_flags flags,
++				    adns_query qu, vbuf *vb,
++				    parsedomain_flags flags,
+ 				    const byte *dgram) {
+   int lablen, labstart, i, ch, first;
+   adns_status st;
+@@ -139,14 +139,15 @@
+     if (first) {
+       first= 0;
+     } else {
+-      if (!adns__vbuf_append(vb,(byte*)".",1)) return adns_s_nomemory;
++      if (!adns__vbuf_append(vb,".",1)) return adns_s_nomemory;
+     }
+     if (flags & pdf_quoteok) {
+-      if (!vbuf__append_quoted1035(vb,dgram+labstart,lablen))
++      if (!vbuf_append_quoted1035(vb,dgram+labstart,lablen))
+ 	return adns_s_nomemory;
+     } else {
+       ch= dgram[labstart];
+-      if (!ctype_alpha(ch) && !ctype_digit(ch)) return adns_s_answerdomaininvalid;
++      if (!ctype_alpha(ch) && !ctype_digit(ch))
++	return adns_s_answerdomaininvalid;
+       for (i= labstart+1; i<labstart+lablen; i++) {
+ 	ch= dgram[i];
+ 	if (ch != '-' && !ctype_alpha(ch) && !ctype_digit(ch))
+@@ -156,23 +157,35 @@
+ 	return adns_s_nomemory;
+     }
+   }
+-  if (!adns__vbuf_append(vb,(byte*)"",1)) return adns_s_nomemory;
++  if (!adns__vbuf_append(vb,"",1)) return adns_s_nomemory;
+   return adns_s_ok;
+ }
+ 
++bool adns__labels_equal(const byte *a, int al, const byte *b, int bl) {
++  if (al != bl) return 0;
++  while (al-- > 0) {
++    int ac= ctype_toupper(*a++);
++    int bc= ctype_toupper(*b++);
++    if (ac != bc) return 0;
++  }
++  return 1;
++}
++
+ adns_status adns__findrr_anychk(adns_query qu, int serv,
+ 				const byte *dgram, int dglen, int *cbyte_io,
+-				int *type_r, int *class_r, unsigned long *ttl_r,
++				int *type_r, int *class_r,
++				unsigned long *ttl_r,
+ 				int *rdlen_r, int *rdstart_r,
+-				const byte *eo_dgram, int eo_dglen, int eo_cbyte,
+-				int *eo_matched_r) {
+-  findlabel_state fls, eo_fls;
++				const byte *eo_dgram, int eo_dglen,
++				int eo_cbyte, int *eo_matched_r) {
++  findlabel_state fls, eo_fls_buf;
++  findlabel_state *eo_fls; /* 0 iff we know it's not matching eo_... */
+   int cbyte;
+-
+-  int tmp, rdlen, mismatch;
++  
++  int tmp, rdlen;
+   unsigned long ttl;
+-  int lablen, labstart, ch;
+-  int eo_lablen, eo_labstart, eo_ch;
++  int lablen, labstart;
++  int eo_lablen, eo_labstart;
+   adns_status st;
+ 
+   cbyte= *cbyte_io;
+@@ -179,31 +192,29 @@
+ 
+   adns__findlabel_start(&fls,qu->ads, serv,qu, dgram,dglen,dglen,cbyte,&cbyte);
+   if (eo_dgram) {
+-    adns__findlabel_start(&eo_fls,qu->ads, -1,0, eo_dgram,eo_dglen,eo_dglen,eo_cbyte,0);
+-    mismatch= 0;
++    eo_fls= &eo_fls_buf;
++    adns__findlabel_start(eo_fls,qu->ads, -1,0,
++			  eo_dgram,eo_dglen,eo_dglen,eo_cbyte,0);
+   } else {
+-    mismatch= 1;
++    eo_fls= 0;
+   }
+-
++  
+   for (;;) {
+     st= adns__findlabel_next(&fls,&lablen,&labstart);
+     if (st) return st;
+     if (lablen<0) goto x_truncated;
+ 
+-    if (!mismatch) {
+-      st= adns__findlabel_next(&eo_fls,&eo_lablen,&eo_labstart);
++    if (eo_fls) {
++      st= adns__findlabel_next(eo_fls,&eo_lablen,&eo_labstart);
+       assert(!st); assert(eo_lablen>=0);
+-      if (lablen != eo_lablen) mismatch= 1;
+-      while (!mismatch && eo_lablen-- > 0) {
+-	ch= dgram[labstart++]; if (ctype_alpha(ch)) ch &= ~32;
+-	eo_ch= eo_dgram[eo_labstart++]; if (ctype_alpha(eo_ch)) eo_ch &= ~32;
+-	if (ch != eo_ch) mismatch= 1;
+-      }
++      if (!adns__labels_equal(dgram+labstart, lablen,
++			      eo_dgram+eo_labstart, eo_lablen))
++	eo_fls= 0;
+     }
+     if (!lablen) break;
+   }
+-  if (eo_matched_r) *eo_matched_r= !mismatch;
+-
++  if (eo_matched_r) *eo_matched_r= !!eo_fls;
++   
+   if (cbyte+10>dglen) goto x_truncated;
+   GET_W(cbyte,tmp); *type_r= tmp;
+   GET_W(cbyte,tmp); *class_r= tmp;
+@@ -211,7 +222,7 @@
+   GET_L(cbyte,ttl);
+   if (ttl > MAXTTLBELIEVE) ttl= MAXTTLBELIEVE;
+   *ttl_r= ttl;
+-
++  
+   GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= rdlen;
+   if (rdstart_r) *rdstart_r= cbyte;
+   cbyte+= rdlen;
+Index: poll.c
+===================================================================
+--- poll.c	(revision 71177)
++++ poll.c	(working copy)
+@@ -3,26 +3,25 @@
+  * - wrappers for poll(2)
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #include <limits.h>
+@@ -32,8 +31,8 @@
+ 
+ #ifdef HAVE_POLL
+ 
+-int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
+-		    const struct timeval *now) {
++int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io,
++		    int *timeout_io, const struct timeval *now) {
+   struct timeval tv_nowbuf, tv_tobuf, *tv_to;
+   int space, found, timeout_ms, r;
+   struct pollfd fds_tmp[MAX_POLLFDS];
+@@ -65,7 +64,7 @@
+     }
+     *timeout_io= timeout_ms;
+   }
+-
++  
+   space= *nfds_io;
+   if (space >= MAX_POLLFDS) {
+     found= adns__pollfds(ads,fds);
+@@ -78,7 +77,7 @@
+   }
+   r= 0;
+ xit:
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+ 
+@@ -92,7 +91,7 @@
+     adns__timeouts(ads, 1, 0,0, *now);
+     adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
+   }
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+ }
+ 
+ int adns_wait_poll(adns_state ads,
+@@ -101,7 +100,7 @@
+ 		   void **context_r) {
+   int r, nfds, to;
+   struct pollfd fds[MAX_POLLFDS];
+-
++  
+   adns__consistency(ads,0,cc_entex);
+ 
+   for (;;) {
+@@ -124,7 +123,7 @@
+   }
+ 
+  xit:
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+ 
+Index: query.c
+===================================================================
+--- query.c	(revision 71177)
++++ query.c	(working copy)
+@@ -5,28 +5,28 @@
+  * - query submission and cancellation (user-visible and internal)
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
++
+ #ifdef ADNS_JGAA_WIN32
+ # include "adns_win32.h"
+ #else
+@@ -38,14 +38,16 @@
+ 
+ #include "internal.h"
+ 
+-static adns_query query_alloc(adns_state ads, const typeinfo *typei,
++static adns_query query_alloc(adns_state ads,
++			      const typeinfo *typei, adns_rrtype type,
+ 			      adns_queryflags flags, struct timeval now) {
+   /* Allocate a virgin query and return it. */
+   adns_query qu;
+-
++  
+   qu= malloc(sizeof(*qu));  if (!qu) return 0;
+-  qu->answer= malloc(sizeof(*qu->answer));  if (!qu->answer) { free(qu); return 0; }
+-
++  qu->answer= malloc(sizeof(*qu->answer));
++  if (!qu->answer) { free(qu); return 0; }
++  
+   qu->ads= ads;
+   qu->state= query_tosend;
+   qu->back= qu->next= qu->parent= 0;
+@@ -79,11 +81,11 @@
+ 
+   qu->answer->status= adns_s_ok;
+   qu->answer->cname= qu->answer->owner= 0;
+-  qu->answer->type= typei->type;
++  qu->answer->type= type;
+   qu->answer->expires= -1;
+   qu->answer->nrrs= 0;
+   qu->answer->rrs.untyped= 0;
+-  qu->answer->rrsz= typei->rrsz;
++  qu->answer->rrsz= typei->getrrsz(typei,type);
+ 
+   return qu;
+ }
+@@ -98,30 +100,81 @@
+   qu->vb= *qumsg_vb;
+   adns__vbuf_init(qumsg_vb);
+ 
+-  qu->query_dgram= malloc( (size_t) qu->vb.used);
++  qu->query_dgram= malloc(qu->vb.used);
+   if (!qu->query_dgram) { adns__query_fail(qu,adns_s_nomemory); return; }
+-
++  
+   qu->id= id;
+   qu->query_dglen= qu->vb.used;
+-  memcpy(qu->query_dgram,qu->vb.buf,(size_t) qu->vb.used);
++  memcpy(qu->query_dgram,qu->vb.buf,qu->vb.used);
+ 
+-  adns__query_send(qu,now);
++  typei->query_send(qu,now);
+ }
+ 
++adns_status adns__ckl_hostname(adns_state ads, adns_queryflags flags,
++			       union checklabel_state *cls,
++			       qcontext *ctx, int labnum,
++			       const char *dgram, int labstart, int lablen)
++{
++  int i, c;
++  const char *label = dgram+labstart;
++
++  if (flags & adns_qf_quoteok_query) return adns_s_ok;
++  for (i=0; i<lablen; i++) {
++    c= label[i];
++    if (c == '-') {
++      if (!i) return adns_s_querydomaininvalid;
++    } else if (!ctype_alpha(c) && !ctype_digit(c)) {
++      return adns_s_querydomaininvalid;
++    }
++  }
++  return adns_s_ok;
++}
++
++static adns_status check_domain_name(adns_state ads, adns_queryflags flags,
++				     qcontext *ctx, const typeinfo *typei,
++				     const byte *dgram, int dglen)
++{
++  findlabel_state fls;
++  adns_status st;
++  int labnum= 0, labstart, lablen;
++  union checklabel_state cls;
++
++  adns__findlabel_start(&fls,ads, -1,0, dgram,dglen,dglen, DNS_HDRSIZE,0);
++  do {
++    st= adns__findlabel_next(&fls, &lablen,&labstart);
++    assert(!st); assert(lablen >= 0);
++    st= typei->checklabel(ads,flags, &cls,ctx,
++			   labnum++, dgram,labstart,lablen);
++    if (st) return st;
++  } while (lablen);
++  return adns_s_ok;
++}
++
+ adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+-				  const typeinfo *typei, vbuf *qumsg_vb, int id,
++				  adns_query parent,
++				  const typeinfo *typei, adns_rrtype type,
++				  vbuf *qumsg_vb, int id,
+ 				  adns_queryflags flags, struct timeval now,
+-				  const qcontext *ctx) {
++				  qcontext *ctx) {
+   adns_query qu;
++  adns_status st;
+ 
+-  qu= query_alloc(ads,typei,flags,now);
+-  if (!qu) { adns__vbuf_free(qumsg_vb); return adns_s_nomemory; }
++  st= check_domain_name(ads, flags,ctx,typei, qumsg_vb->buf,qumsg_vb->used);
++  if (st) goto x_err;
++  qu= query_alloc(ads,typei,type,flags,now);
++  if (!qu) { st = adns_s_nomemory; goto x_err; }
+   *query_r= qu;
+ 
+-  memcpy(&qu->ctx,ctx,(size_t) sizeof(qu->ctx));
++  qu->parent= parent;
++  LIST_LINK_TAIL_PART(parent->children,qu,siblings.);
++  memcpy(&qu->ctx,ctx,sizeof(qu->ctx));
+   query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
++  
++  return adns_s_ok;
+ 
+-  return adns_s_ok;
++x_err:
++  adns__vbuf_free(qumsg_vb);
++  return st;
+ }
+ 
+ static void query_simple(adns_state ads, adns_query qu,
+@@ -130,19 +183,23 @@
+ 			 struct timeval now) {
+   vbuf vb_new;
+   int id;
+-  adns_status stat;
++  adns_status st;
+ 
+-  stat= adns__mkquery(ads,&qu->vb,&id, owner,ol, typei,flags);
+-  if (stat) {
+-    if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search)) {
++  st= adns__mkquery(ads,&qu->vb,&id, owner,ol,
++		      typei,qu->answer->type, flags);
++  if (st) {
++    if (st == adns_s_querydomaintoolong && (flags & adns_qf_search)) {
+       adns__search_next(ads,qu,now);
+       return;
+     } else {
+-      adns__query_fail(qu,stat);
++      adns__query_fail(qu,st);
+       return;
+     }
+   }
+ 
++  st= check_domain_name(ads, flags,&qu->ctx,typei, qu->vb.buf,qu->vb.used);
++  if (st) { adns__query_fail(qu,st); return; }
++
+   vb_new= qu->vb;
+   adns__vbuf_init(&qu->vb);
+   query_submit(ads,qu, typei,&vb_new,id, flags,now);
+@@ -150,8 +207,8 @@
+ 
+ void adns__search_next(adns_state ads, adns_query qu, struct timeval now) {
+   const char *nextentry;
+-  adns_status stat;
+-
++  adns_status st;
++  
+   if (qu->search_doneabs<0) {
+     nextentry= 0;
+     qu->search_doneabs= 1;
+@@ -158,8 +215,8 @@
+   } else {
+     if (qu->search_pos >= ads->nsearchlist) {
+       if (qu->search_doneabs) {
+-	stat= adns_s_nxdomain; goto x_fail;
+-	return;
++	qu->search_vb.used= qu->search_origlen;
++	st= adns_s_nxdomain; goto x_fail;
+       } else {
+ 	nextentry= 0;
+ 	qu->search_doneabs= 1;
+@@ -171,20 +228,22 @@
+ 
+   qu->search_vb.used= qu->search_origlen;
+   if (nextentry) {
+-    if (!adns__vbuf_append(&qu->search_vb,(byte*)".",1) ||
+-	!adns__vbuf_appendstr(&qu->search_vb,nextentry)) {
+-      stat= adns_s_nomemory; goto x_fail;
+-    }
++    if (!adns__vbuf_append(&qu->search_vb,".",1) ||
++	!adns__vbuf_appendstr(&qu->search_vb,nextentry))
++      goto x_nomemory;
+   }
+ 
+   free(qu->query_dgram);
+   qu->query_dgram= 0; qu->query_dglen= 0;
+ 
+-  query_simple(ads,qu, (char*)qu->search_vb.buf, qu->search_vb.used, qu->typei, qu->flags, now);
++  query_simple(ads,qu, qu->search_vb.buf, qu->search_vb.used,
++	       qu->typei, qu->flags, now);
+   return;
+ 
++x_nomemory:
++  st= adns_s_nomemory;
+ x_fail:
+-  adns__query_fail(qu,stat);
++  adns__query_fail(qu,st);
+ }
+ 
+ static int save_owner(adns_query qu, const char *owner, int ol) {
+@@ -191,12 +250,14 @@
+   /* Returns 1 if OK, otherwise there was no memory. */
+   adns_answer *ans;
+ 
++  if (!(qu->flags & adns_qf_owner)) return 1;
++
+   ans= qu->answer;
+   assert(!ans->owner);
+ 
+-  ans->owner= adns__alloc_preserved(qu, (size_t) ol+1);  if (!ans->owner) return 0;
++  ans->owner= adns__alloc_preserved(qu,ol+1);  if (!ans->owner) return 0;
+ 
+-  memcpy(ans->owner,owner, (size_t) ol);
++  memcpy(ans->owner,owner,ol);
+   ans->owner[ol]= 0;
+   return 1;
+ }
+@@ -208,7 +269,7 @@
+ 		void *context,
+ 		adns_query *query_r) {
+   int r, ol, ndots;
+-  adns_status stat;
++  adns_status st;
+   const typeinfo *typei;
+   struct timeval now;
+   adns_query qu;
+@@ -216,22 +277,29 @@
+ 
+   adns__consistency(ads,0,cc_entex);
+ 
++  if (flags & ~(adns_queryflags)0x4009ffff)
++    /* 0x40080000 are reserved for `harmless' future expansion
++     * 0x00000020 used to be adns_qf_quoteok_cname, now the default;
++     * see also addrfam.c:textaddr_check_qf */
++    return ENOSYS;
++
+   typei= adns__findtype(type);
+   if (!typei) return ENOSYS;
+ 
+   r= gettimeofday(&now,0); if (r) goto x_errno;
+-  qu= query_alloc(ads,typei,flags,now); if (!qu) goto x_errno;
+-
++  qu= query_alloc(ads,typei,type,flags,now); if (!qu) goto x_errno;
++  
+   qu->ctx.ext= context;
+   qu->ctx.callback= 0;
+-  memset(&qu->ctx.info,0,sizeof(qu->ctx.info));
++  memset(&qu->ctx.pinfo,0,sizeof(qu->ctx.pinfo));
++  memset(&qu->ctx.tinfo,0,sizeof(qu->ctx.tinfo));
+ 
+   *query_r= qu;
+ 
+   ol= strlen(owner);
+-  if (!ol) { stat= adns_s_querydomaininvalid; goto x_adnsfail; }
+-  if (ol>DNS_MAXDOMAIN+1) { stat= adns_s_querydomaintoolong; goto x_adnsfail; }
+-
++  if (!ol) { st= adns_s_querydomaininvalid; goto x_adnsfail; }
++  if (ol>DNS_MAXDOMAIN+1) { st= adns_s_querydomaintoolong; goto x_adnsfail; }
++				 
+   if (ol>=1 && owner[ol-1]=='.' && (ol<2 || owner[ol-2]!='\\')) {
+     flags &= ~adns_qf_search;
+     qu->flags= flags;
+@@ -239,8 +307,8 @@
+   }
+ 
+   if (flags & adns_qf_search) {
+-    r= adns__vbuf_append(&qu->search_vb,(byte*)owner,ol);
+-    if (!r) { stat= adns_s_nomemory; goto x_adnsfail; }
++    r= adns__vbuf_append(&qu->search_vb,owner,ol);
++    if (!r) { st= adns_s_nomemory; goto x_adnsfail; }
+ 
+     for (ndots=0, p=owner; (p= strchr(p,'.')); p++, ndots++);
+     qu->search_doneabs= (ndots >= ads->searchndots) ? -1 : 0;
+@@ -248,23 +316,23 @@
+     adns__search_next(ads,qu,now);
+   } else {
+     if (flags & adns_qf_owner) {
+-      if (!save_owner(qu,owner,ol)) { stat= adns_s_nomemory; goto x_adnsfail; }
++      if (!save_owner(qu,owner,ol)) { st= adns_s_nomemory; goto x_adnsfail; }
+     }
+     query_simple(ads,qu, owner,ol, typei,flags, now);
+   }
+   adns__autosys(ads,now);
+-  adns__consistency(ads,qu,cc_entex);
++  adns__returning(ads,qu);
+   return 0;
+ 
+  x_adnsfail:
+-  adns__query_fail(qu,stat);
+-  adns__consistency(ads,qu,cc_entex);
++  adns__query_fail(qu,st);
++  adns__returning(ads,qu);
+   return 0;
+ 
+  x_errno:
+   r= errno;
+   assert(r);
+-  adns__consistency(ads,0,cc_entex);
++  adns__returning(ads,0);
+   return r;
+ }
+ 
+@@ -275,27 +343,15 @@
+ 			    adns_queryflags flags,
+ 			    void *context,
+ 			    adns_query *query_r) {
+-  const unsigned char *iaddr;
+-  char *buf, *buf_free;
++  char *buf, *buf_free = 0;
+   char shortbuf[100];
+-  int r, lreq;
++  int r;
+ 
+   flags &= ~adns_qf_search;
+ 
+-  if (addr->sa_family != AF_INET) return ENOSYS;
+-  iaddr= (const unsigned char*) &(((const struct sockaddr_in*)addr) -> sin_addr);
+-
+-  lreq= strlen(zone) + 4*4 + 1;
+-  if (lreq > (int)sizeof(shortbuf)) {
+-    buf= malloc( strlen(zone) + 4*4 + 1 );
+-    if (!buf) return errno;
+-    buf_free= buf;
+-  } else {
+-    buf= shortbuf;
+-    buf_free= 0;
+-  }
+-  sprintf(buf, "%d.%d.%d.%d.%s", iaddr[3], iaddr[2], iaddr[1], iaddr[0], zone);
+-
++  buf = shortbuf;
++  r= adns__make_reverse_domain(addr,zone, &buf,sizeof(shortbuf),&buf_free);
++  if (r) return r;
+   r= adns_submit(ads,buf,type,flags,context,query_r);
+   free(buf_free);
+   return r;
+@@ -307,8 +363,10 @@
+ 			adns_queryflags flags,
+ 			void *context,
+ 			adns_query *query_r) {
+-  if (type != adns_r_ptr && type != adns_r_ptr_raw) return EINVAL;
+-  return adns_submit_reverse_any(ads,addr,"in-addr.arpa",type,flags,context,query_r);
++  if (((type^adns_r_ptr) & adns_rrt_reprmask) &&
++      ((type^adns_r_ptr_raw) & adns_rrt_reprmask))
++    return EINVAL;
++  return adns_submit_reverse_any(ads,addr,0,type,flags,context,query_r);
+ }
+ 
+ int adns_synchronous(adns_state ads,
+@@ -318,7 +376,7 @@
+ 		     adns_answer **answer_r) {
+   adns_query qu;
+   int r;
+-
++  
+   r= adns_submit(ads,owner,type,flags,0,&qu);
+   if (r) return r;
+ 
+@@ -336,12 +394,13 @@
+   an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
+   if (!an) return 0;
+   LIST_LINK_TAIL(qu->allocations,an);
++  an->sz= sz;
+   return (byte*)an + MEM_ROUND(sizeof(*an));
+ }
+ 
+ void *adns__alloc_interim(adns_query qu, size_t sz) {
+   void *rv;
+-
++  
+   sz= MEM_ROUND(sz);
+   rv= alloc_common(qu,sz);
+   if (!rv) return 0;
+@@ -351,7 +410,7 @@
+ 
+ void *adns__alloc_preserved(adns_query qu, size_t sz) {
+   void *rv;
+-
++  
+   sz= MEM_ROUND(sz);
+   rv= adns__alloc_interim(qu,sz);
+   if (!rv) return 0;
+@@ -359,23 +418,43 @@
+   return rv;
+ }
+ 
++static allocnode *alloc__info(adns_query qu, void *p, size_t *sz_r) {
++  allocnode *an;
++
++  if (!p || p == qu) { *sz_r= 0; return 0; }
++  an= (allocnode *)((byte *)p - MEM_ROUND(sizeof(allocnode)));
++  *sz_r= MEM_ROUND(an->sz);
++  return an;
++}
++
++void adns__free_interim(adns_query qu, void *p) {
++  size_t sz;
++  allocnode *an= alloc__info(qu, p, &sz);
++
++  if (!an) return;
++  assert(!qu->final_allocspace);
++  LIST_UNLINK(qu->allocations, an);
++  free(an);
++  qu->interim_allocd -= sz;
++  assert(!qu->interim_allocd >= 0);
++}
++
+ void *adns__alloc_mine(adns_query qu, size_t sz) {
+   return alloc_common(qu,MEM_ROUND(sz));
+ }
+ 
+-void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz) {
+-  allocnode *an;
++void adns__transfer_interim(adns_query from, adns_query to, void *block) {
++  size_t sz;
++  allocnode *an= alloc__info(from, block, &sz);
+ 
+-  if (!block) return;
+-  an= (void*)((byte*)block - MEM_ROUND(sizeof(*an)));
++  if (!an) return;
+ 
+   assert(!to->final_allocspace);
+   assert(!from->final_allocspace);
+-
++  
+   LIST_UNLINK(from->allocations,an);
+   LIST_LINK_TAIL(to->allocations,an);
+ 
+-  sz= MEM_ROUND(sz);
+   from->interim_allocd -= sz;
+   to->interim_allocd += sz;
+ 
+@@ -398,18 +477,18 @@
+   return rp;
+ }
+ 
+-static void cancel_children(adns_query qu) {
++void adns__cancel_children(adns_query qu) {
+   adns_query cqu, ncqu;
+ 
+   for (cqu= qu->children.head; cqu; cqu= ncqu) {
+     ncqu= cqu->siblings.next;
+-    adns_cancel(cqu);
++    adns__cancel(cqu);
+   }
+ }
+ 
+ void adns__reset_preserved(adns_query qu) {
+   assert(!qu->final_allocspace);
+-  cancel_children(qu);
++  adns__cancel_children(qu);
+   qu->answer->nrrs= 0;
+   qu->answer->rrs.untyped= 0;
+   qu->interim_allocd= qu->preserved_allocd;
+@@ -418,7 +497,7 @@
+ static void free_query_allocs(adns_query qu) {
+   allocnode *an, *ann;
+ 
+-  cancel_children(qu);
++  adns__cancel_children(qu);
+   for (an= qu->allocations.head; an; an= ann) { ann= an->next; free(an); }
+   LIST_INIT(qu->allocations);
+   adns__vbuf_free(&qu->vb);
+@@ -427,11 +506,26 @@
+   qu->query_dgram= 0;
+ }
+ 
+-void adns_cancel(adns_query qu) {
++void adns__returning(adns_state ads, adns_query qu_for_caller) {
++  while (ads->intdone.head) {
++    adns_query iq= ads->intdone.head;
++    adns_query parent= iq->parent;
++    LIST_UNLINK_PART(parent->children,iq,siblings.);
++    LIST_UNLINK(iq->ads->childw,parent);
++    LIST_UNLINK(ads->intdone,iq);
++    iq->ctx.callback(parent,iq);
++    free_query_allocs(iq);
++    free(iq->answer);
++    free(iq);
++  }
++  adns__consistency(ads,qu_for_caller,cc_entex);
++}
++
++void adns__cancel(adns_query qu) {
+   adns_state ads;
+ 
+   ads= qu->ads;
+-  adns__consistency(ads,qu,cc_entex);
++  adns__consistency(ads,qu,cc_freq);
+   if (qu->parent) LIST_UNLINK_PART(qu->parent->children,qu,siblings.);
+   switch (qu->state) {
+   case query_tosend:
+@@ -444,7 +538,10 @@
+     LIST_UNLINK(ads->childw,qu);
+     break;
+   case query_done:
+-    LIST_UNLINK(ads->output,qu);
++    if (qu->parent)
++      LIST_UNLINK(ads->intdone,qu);
++    else
++      LIST_UNLINK(ads->output,qu);
+     break;
+   default:
+     abort();
+@@ -452,10 +549,20 @@
+   free_query_allocs(qu);
+   free(qu->answer);
+   free(qu);
+-  adns__consistency(ads,0,cc_entex);
+ }
+ 
+-void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now) {
++void adns_cancel(adns_query qu) {
++  adns_state ads;
++
++  assert(!qu->parent);
++  ads= qu->ads;
++  adns__consistency(ads,qu,cc_entex);
++  adns__cancel(qu);
++  adns__returning(ads,0);
++}
++
++void adns__update_expires(adns_query qu, unsigned long ttl,
++			  struct timeval now) {
+   time_t max;
+ 
+   assert(ttl <= MAXTTLBELIEVE);
+@@ -471,7 +578,8 @@
+   ans= qu->answer;
+ 
+   if (qu->interim_allocd) {
+-    ans= realloc(qu->answer, MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd));
++    ans= realloc(qu->answer,
++		 MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd));
+     if (!ans) goto x_nomem;
+     qu->answer= ans;
+   }
+@@ -479,17 +587,17 @@
+   qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans));
+   adns__makefinal_str(qu,&ans->cname);
+   adns__makefinal_str(qu,&ans->owner);
+-
++  
+   if (ans->nrrs) {
+-    adns__makefinal_block(qu, &ans->rrs.untyped, (size_t) ans->nrrs*ans->rrsz);
++    adns__makefinal_block(qu, &ans->rrs.untyped, ans->nrrs*ans->rrsz);
+ 
+     for (rrn=0; rrn<ans->nrrs; rrn++)
+       qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz);
+   }
+-
++  
+   free_query_allocs(qu);
+   return;
+-
++  
+  x_nomem:
+   qu->preserved_allocd= 0;
+   qu->answer->cname= 0;
+@@ -501,17 +609,16 @@
+ }
+ 
+ void adns__query_done(adns_query qu) {
++  adns_state ads=qu->ads;
+   adns_answer *ans;
+-  adns_query parent;
+ 
+-  cancel_children(qu);
++  adns__cancel_children(qu);
+ 
+   qu->id= -1;
+   ans= qu->answer;
+ 
+-  if (qu->flags & adns_qf_owner && qu->flags & adns_qf_search &&
+-      ans->status != adns_s_nomemory) {
+-    if (!save_owner(qu, (char*)qu->search_vb.buf, qu->search_vb.used)) {
++  if (qu->flags & adns_qf_search && ans->status != adns_s_nomemory) {
++    if (!save_owner(qu, qu->search_vb.buf, qu->search_vb.used)) {
+       adns__query_fail(qu,adns_s_nomemory);
+       return;
+     }
+@@ -518,35 +625,34 @@
+   }
+ 
+   if (ans->nrrs && qu->typei->diff_needswap) {
+-    if (!adns__vbuf_ensure(&qu->vb,qu->typei->rrsz)) {
++    if (!adns__vbuf_ensure(&qu->vb,qu->answer->rrsz)) {
+       adns__query_fail(qu,adns_s_nomemory);
+       return;
+     }
+     adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz,
+ 		qu->vb.buf,
+-		(int(*)(void*, const void*, const void*))qu->typei->diff_needswap,
++		(int(*)(void*, const void*, const void*))
++		  qu->typei->diff_needswap,
+ 		qu->ads);
+   }
++  if (ans->nrrs && qu->typei->postsort) {
++    qu->typei->postsort(qu->ads, ans->rrs.bytes,
++			ans->nrrs,ans->rrsz, qu->typei);
++  }
+ 
+   ans->expires= qu->expires;
+-  parent= qu->parent;
+-  if (parent) {
+-    LIST_UNLINK_PART(parent->children,qu,siblings.);
+-    LIST_UNLINK(qu->ads->childw,parent);
+-    qu->ctx.callback(parent,qu);
+-    free_query_allocs(qu);
+-    free(qu->answer);
+-    free(qu);
++  qu->state= query_done;
++  if (qu->parent) {
++    LIST_LINK_TAIL(ads->intdone,qu);
+   } else {
+     makefinal_query(qu);
+     LIST_LINK_TAIL(qu->ads->output,qu);
+-    qu->state= query_done;
+   }
+ }
+ 
+-void adns__query_fail(adns_query qu, adns_status stat) {
++void adns__query_fail(adns_query qu, adns_status st) {
+   adns__reset_preserved(qu);
+-  qu->answer->status= stat;
++  qu->answer->status= st;
+   adns__query_done(qu);
+ }
+ 
+@@ -557,9 +663,9 @@
+   before= *strp;
+   if (!before) return;
+   l= strlen(before)+1;
+-  after= adns__alloc_final(qu, (size_t) l);
+-  memcpy(after,before,(size_t) l);
+-  *strp= after;
++  after= adns__alloc_final(qu,l);
++  memcpy(after,before,l);
++  *strp= after;  
+ }
+ 
+ void adns__makefinal_block(adns_query qu, void **blpp, size_t sz) {
+@@ -568,6 +674,6 @@
+   before= *blpp;
+   if (!before) return;
+   after= adns__alloc_final(qu,sz);
+-  memcpy(after,before, (size_t) sz);
++  memcpy(after,before,sz);
+   *blpp= after;
+ }
+Index: reply.c
+===================================================================
+--- reply.c	(revision 71177)
++++ reply.c	(working copy)
+@@ -3,36 +3,31 @@
+  * - main handling and parsing routine for received datagrams
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+-#ifdef ADNS_JGAA_WIN32
+-# include "adns_win32.h"
+-#endif
+-
+ #include <stdlib.h>
+ 
+ #include "internal.h"
+-
++    
+ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
+ 		     int serv, int viatcp, struct timeval now) {
+   int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here;
+@@ -39,7 +34,7 @@
+   int id, f1, f2, qdcount, ancount, nscount, arcount;
+   int flg_ra, flg_rd, flg_tc, flg_qr, opcode;
+   int rrtype, rrclass, rdlength, rdstart;
+-  int anstart, nsstart, arstart;
++  int anstart, nsstart;
+   int ownermatched, l, nrrs;
+   unsigned long ttl, soattl;
+   const typeinfo *typei;
+@@ -49,9 +44,10 @@
+   vbuf tempvb;
+   byte *newquery, *rrsdata;
+   parseinfo pai;
+-
++  
+   if (dglen<DNS_HDRSIZE) {
+-    adns__diag(ads,serv,0,"received datagram too short for message header (%d)",dglen);
++    adns__diag(ads,serv,0,"received datagram"
++	       " too short for message header (%d)",dglen);
+     return;
+   }
+   cbyte= 0;
+@@ -72,18 +68,19 @@
+   rcode= (f2&0x0f);
+ 
+   cname_here= 0;
+-
++  
+   if (!flg_qr) {
+     adns__diag(ads,serv,0,"server sent us a query, not a response");
+     return;
+   }
+   if (opcode) {
+-    adns__diag(ads,serv,0,"server sent us unknown opcode %d (wanted 0=QUERY)",opcode);
++    adns__diag(ads,serv,0,"server sent us unknown opcode"
++	       " %d (wanted 0=QUERY)",opcode);
+     return;
+   }
+ 
+   qu= 0;
+-  /* See if we can find the relevant query, or leave qu=0 otherwise ... */
++  /* See if we can find the relevant query, or leave qu=0 otherwise ... */   
+ 
+   if (qdcount == 1) {
+     for (qu= viatcp ? ads->tcpw.head : ads->udpw.head; qu; qu= nqu) {
+@@ -92,7 +89,7 @@
+       if (dglen < qu->query_dglen) continue;
+       if (memcmp(qu->query_dgram+DNS_HDRSIZE,
+ 		 dgram+DNS_HDRSIZE,
+-		 (size_t) qu->query_dglen-DNS_HDRSIZE))
++		 qu->query_dglen-DNS_HDRSIZE))
+ 	continue;
+       if (viatcp) {
+ 	assert(qu->state == query_tcpw);
+@@ -108,7 +105,7 @@
+       else LIST_UNLINK(ads->udpw,qu);
+     }
+   }
+-
++  
+   /* If we're going to ignore the packet, we return as soon as we have
+    * failed the query (if any) and printed the warning message (if
+    * any).
+@@ -118,7 +115,8 @@
+   case rcode_nxdomain:
+     break;
+   case rcode_formaterror:
+-    adns__warn(ads,serv,qu,"server cannot understand our query (Format Error)");
++    adns__warn(ads,serv,qu,"server cannot understand our query"
++	       " (Format Error)");
+     if (qu) adns__query_fail(qu,adns_s_rcodeformaterror);
+     return;
+   case rcode_servfail:
+@@ -143,21 +141,22 @@
+     if (!qdcount) {
+       adns__diag(ads,serv,0,"server sent reply without quoting our question");
+     } else if (qdcount>1) {
+-      adns__diag(ads,serv,0,"server claimed to answer %d questions with one message",
+-		 qdcount);
++      adns__diag(ads,serv,0,"server claimed to answer %d"
++		 " questions with one message", qdcount);
+     } else if (ads->iflags & adns_if_debug) {
+       adns__vbuf_init(&tempvb);
+       adns__debug(ads,serv,0,"reply not found, id %02x, query owner %s",
+-		  id, adns__diag_domain(ads,serv,0,&tempvb,dgram,dglen,DNS_HDRSIZE));
++		  id, adns__diag_domain(ads,serv,0,&tempvb,
++					dgram,dglen,DNS_HDRSIZE));
+       adns__vbuf_free(&tempvb);
+     }
+     return;
+   }
+ 
+-  /* We're definitely going to do something with this packet and this query now. */
+-
++  /* We're definitely going to do something with this packet and this
++   * query now. */
++  
+   anstart= qu->query_dglen;
+-  arstart= -1;
+ 
+   /* Now, take a look at the answer section, and see if it is complete.
+    * If it has any CNAMEs we stuff them in the answer.
+@@ -173,47 +172,54 @@
+     if (rrtype == -1) goto x_truncated;
+ 
+     if (rrclass != DNS_CLASS_IN) {
+-      adns__diag(ads,serv,qu,"ignoring answer RR with wrong class %d (expected IN=%d)",
+-		 rrclass,DNS_CLASS_IN);
++      adns__diag(ads,serv,qu,"ignoring answer RR with wrong class %d"
++		 " (expected IN=%d)", rrclass,DNS_CLASS_IN);
+       continue;
+     }
+     if (!ownermatched) {
+       if (ads->iflags & adns_if_debug) {
+ 	adns__debug(ads,serv,qu,"ignoring RR with an unexpected owner %s",
+-		    adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rrstart));
++		    adns__diag_domain(ads,serv,qu, &qu->vb,
++				      dgram,dglen,rrstart));
+       }
+       continue;
+     }
+     if (rrtype == adns_r_cname &&
+-	(qu->typei->type & adns__rrt_typemask) != adns_r_cname) {
++	(qu->answer->type & adns_rrt_typemask) != adns_r_cname) {
+       if (qu->flags & adns_qf_cname_forbid) {
+ 	adns__query_fail(qu,adns_s_prohibitedcname);
+ 	return;
+       } else if (qu->cname_dgram) { /* Ignore second and subsequent CNAME(s) */
+-	adns__debug(ads,serv,qu,"allegedly canonical name %s is actually alias for %s",
+-		    qu->answer->cname,
+-		    adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rdstart));
++	adns__debug(ads,serv,qu,"allegedly canonical name %s"
++		    " is actually alias for %s", qu->answer->cname,
++		    adns__diag_domain(ads,serv,qu, &qu->vb,
++				      dgram,dglen,rdstart));
+ 	adns__query_fail(qu,adns_s_prohibitedcname);
+ 	return;
+       } else if (wantedrrs) { /* Ignore CNAME(s) after RR(s). */
+ 	adns__debug(ads,serv,qu,"ignoring CNAME (to %s) coexisting with RR",
+-		    adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rdstart));
++		    adns__diag_domain(ads,serv,qu, &qu->vb,
++				      dgram,dglen,rdstart));
+       } else {
+ 	qu->cname_begin= rdstart;
+ 	qu->cname_dglen= dglen;
+ 	st= adns__parse_domain(ads,serv,qu, &qu->vb,
+-			       qu->flags & adns_qf_quotefail_cname ? 0 : pdf_quoteok,
++			       qu->flags & adns_qf_quotefail_cname
++			       ? 0 : pdf_quoteok,
+ 			       dgram,dglen, &rdstart,rdstart+rdlength);
+ 	if (!qu->vb.used) goto x_truncated;
+ 	if (st) { adns__query_fail(qu,st); return; }
+-	l= strlen((char*)qu->vb.buf)+1;
+-	qu->answer->cname= adns__alloc_preserved(qu,(size_t) l);
+-	if (!qu->answer->cname) { adns__query_fail(qu,adns_s_nomemory); return; }
++	l= strlen(qu->vb.buf)+1;
++	qu->answer->cname= adns__alloc_preserved(qu,l);
++	if (!qu->answer->cname) {
++	  adns__query_fail(qu,adns_s_nomemory);
++	  return;
++	}
+ 
+-	qu->cname_dgram= adns__alloc_mine(qu, (size_t) dglen);
+-	memcpy(qu->cname_dgram,dgram,(size_t) dglen);
++	qu->cname_dgram= adns__alloc_mine(qu,dglen);
++	memcpy(qu->cname_dgram,dgram,dglen);
+ 
+-	memcpy(qu->answer->cname,qu->vb.buf, (size_t) l);
++	memcpy(qu->answer->cname,qu->vb.buf,l);
+ 	cname_here= 1;
+ 	adns__update_expires(qu,ttl,now);
+ 	/* If we find the answer section truncated after this point we restart
+@@ -222,10 +228,11 @@
+ 	 * it contains the relevant info.
+ 	 */
+       }
+-    } else if (rrtype == ((INT)qu->typei->type & (INT)adns__rrt_typemask)) {
++    } else if (rrtype == (qu->answer->type & adns_rrt_typemask)) {
+       wantedrrs++;
+     } else {
+-      adns__debug(ads,serv,qu,"ignoring answer RR with irrelevant type %d",rrtype);
++      adns__debug(ads,serv,qu,"ignoring answer RR"
++		  " with irrelevant type %d",rrtype);
+     }
+   }
+ 
+@@ -233,13 +240,15 @@
+    * which we could use.
+    */
+   if (flg_tc) goto x_truncated;
+-
++  
+   nsstart= cbyte;
+ 
+   if (!wantedrrs) {
+-    /* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */
++    /* Oops, NODATA or NXDOMAIN or perhaps a referral
++     * (which would be a problem) */
+ 
+-    /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */
++    /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records
++     * in authority section */
+     foundsoa= 0; soattl= 0; foundns= 0;
+     for (rri= 0; rri<nscount; rri++) {
+       rrstart= cbyte;
+@@ -249,19 +258,19 @@
+       if (rrtype==-1) goto x_truncated;
+       if (rrclass != DNS_CLASS_IN) {
+ 	adns__diag(ads,serv,qu,
+-		   "ignoring authority RR with wrong class %d (expected IN=%d)",
+-		   rrclass,DNS_CLASS_IN);
++		   "ignoring authority RR with wrong class %d"
++		   " (expected IN=%d)", rrclass,DNS_CLASS_IN);
+ 	continue;
+       }
+       if (rrtype == adns_r_soa_raw) { foundsoa= 1; soattl= ttl; break; }
+       else if (rrtype == adns_r_ns_raw) { foundns= 1; }
+     }
+-
++    
+     if (rcode == rcode_nxdomain) {
+       /* We still wanted to look for the SOA so we could find the TTL. */
+       adns__update_expires(qu,soattl,now);
+ 
+-      if (qu->flags & adns_qf_search) {
++      if (qu->flags & adns_qf_search && !qu->cname_dgram) {
+ 	adns__search_next(ads,qu,now);
+       } else {
+ 	adns__query_fail(qu,adns_s_nxdomain);
+@@ -285,13 +294,16 @@
+ 
+     /* Bloody hell, I thought we asked for recursion ? */
+     if (!flg_ra) {
+-      adns__diag(ads,serv,qu,"server is not willing to do recursive lookups for us");
++      adns__diag(ads,serv,qu,"server is not willing"
++		 " to do recursive lookups for us");
+       adns__query_fail(qu,adns_s_norecurse);
+     } else {
+       if (!flg_rd)
+-	adns__diag(ads,serv,qu,"server thinks we didn't ask for recursive lookup");
++	adns__diag(ads,serv,qu,"server thinks"
++		   " we didn't ask for recursive lookup");
+       else
+-	adns__debug(ads,serv,qu,"server claims to do recursion, but gave us a referral");
++	adns__debug(ads,serv,qu,"server claims to do recursion,"
++		    " but gave us a referral");
+       adns__query_fail(qu,adns_s_invalidresponse);
+     }
+     return;
+@@ -299,8 +311,11 @@
+ 
+   /* Now, we have some RRs which we wanted. */
+ 
+-  qu->answer->rrs.untyped= adns__alloc_interim(qu,(size_t) qu->typei->rrsz*wantedrrs);
+-  if (!qu->answer->rrs.untyped) { adns__query_fail(qu,adns_s_nomemory); return; }
++  qu->answer->rrs.untyped= adns__alloc_interim(qu,qu->answer->rrsz*wantedrrs);
++  if (!qu->answer->rrs.untyped) {
++    adns__query_fail(qu,adns_s_nomemory);
++    return;
++  }
+ 
+   typei= qu->typei;
+   cbyte= anstart;
+@@ -322,11 +337,12 @@
+ 		     &ownermatched);
+     assert(!st); assert(rrtype != -1);
+     if (rrclass != DNS_CLASS_IN ||
+-	rrtype != ((INT)qu->typei->type & (INT)adns__rrt_typemask) ||
++	rrtype != (qu->answer->type & adns_rrt_typemask) ||
+ 	!ownermatched)
+       continue;
+     adns__update_expires(qu,ttl,now);
+-    st= typei->parse(&pai, rdstart,rdstart+rdlength, rrsdata+nrrs*typei->rrsz);
++    st= typei->parse(&pai, rdstart,rdstart+rdlength,
++		     rrsdata+nrrs*qu->answer->rrsz);
+     if (st) { adns__query_fail(qu,st); return; }
+     if (rdstart==-1) goto x_truncated;
+     nrrs++;
+@@ -344,7 +360,7 @@
+   return;
+ 
+  x_truncated:
+-
++  
+   if (!flg_tc) {
+     adns__diag(ads,serv,qu,"server sent datagram which points outside itself");
+     adns__query_fail(qu,adns_s_invalidresponse);
+@@ -351,22 +367,22 @@
+     return;
+   }
+   qu->flags |= adns_qf_usevc;
+-
++  
+  x_restartquery:
+   if (qu->cname_dgram) {
+     st= adns__mkquery_frdgram(qu->ads,&qu->vb,&qu->id,
+-			      qu->cname_dgram, qu->cname_dglen, qu->cname_begin,
+-			      qu->typei->type, qu->flags);
++			      qu->cname_dgram,qu->cname_dglen,qu->cname_begin,
++			      qu->answer->type, qu->flags);
+     if (st) { adns__query_fail(qu,st); return; }
+-
+-    newquery= realloc(qu->query_dgram, (size_t) qu->vb.used);
++    
++    newquery= realloc(qu->query_dgram,qu->vb.used);
+     if (!newquery) { adns__query_fail(qu,adns_s_nomemory); return; }
+-
++    
+     qu->query_dgram= newquery;
+     qu->query_dglen= qu->vb.used;
+-    memcpy(newquery,qu->vb.buf, (size_t) qu->vb.used);
++    memcpy(newquery,qu->vb.buf,qu->vb.used);
+   }
+-
++  
+   if (qu->state == query_tcpw) qu->state= query_tosend;
+   qu->retries= 0;
+   adns__reset_preserved(qu);
+Index: setup.c
+===================================================================
+--- setup.c	(revision 71177)
++++ setup.c	(working copy)
+@@ -4,26 +4,25 @@
+  * - management of global state
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #ifdef ADNS_JGAA_WIN32
+@@ -35,6 +34,7 @@
+ # include <limits.h>
+ # include <unistd.h>
+ # include <fcntl.h>
++# include <sys/types.h>
+ # include <netdb.h>
+ # include <sys/socket.h>
+ # include <netinet/in.h>
+@@ -45,24 +45,29 @@
+ 
+ static void readconfig(adns_state ads, const char *filename, int warnmissing);
+ 
+-static void addserver(adns_state ads, struct in_addr addr) {
++static void addserver(adns_state ads, const struct sockaddr *sa, int salen) {
+   int i;
+-  struct server *ss;
+-
++  adns_rr_addr *ss;
++  char buf[ADNS_ADDR2TEXT_BUFLEN];
++  
+   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 (adns__sockaddrs_equal(sa, &ads->servers[i].addr.sa)) {
++      adns__debug(ads,-1,0,"duplicate nameserver %s ignored",
++		  adns__sockaddr_ntoa(sa, buf));
+       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, buf));
+     return;
+   }
+ 
+   ss= ads->servers+ads->nservers;
+-  ss->addr= addr;
++  assert(salen <= sizeof(ss->addr));
++  ss->len = salen;
++  memcpy(&ss->addr, sa, salen);
+   ads->nservers++;
+ }
+ 
+@@ -80,14 +85,14 @@
+   va_list al;
+ 
+   saveerr(ads,EINVAL);
+-  if (!ads->diagfile || (ads->iflags & adns_if_noerrprint)) return;
++  if (!ads->logfn || (ads->iflags & adns_if_noerrprint)) return;
+ 
+-  if (lno==-1) fprintf(ads->diagfile,"adns: %s: ",fn);
+-  else fprintf(ads->diagfile,"adns: %s:%d: ",fn,lno);
++  if (lno==-1) adns__lprintf(ads,"adns: %s: ",fn);
++  else adns__lprintf(ads,"adns: %s:%d: ",fn,lno);
+   va_start(al,fmt);
+-  vfprintf(ads->diagfile,fmt,al);
++  adns__vlprintf(ads,fmt,al);
+   va_end(al);
+-  fputc('\n',ads->diagfile);
++  adns__lprintf(ads,"\n");
+ }
+ 
+ static int nextword(const char **bufp_io, const char **word_r, int *l_r) {
+@@ -107,18 +112,32 @@
+   return 1;
+ }
+ 
+-static void ccf_nameserver(adns_state ads, const char *fn, int lno, const char *buf) {
+-  struct in_addr ia;
++static void ccf_nameserver(adns_state ads, const char *fn,
++			   int lno, const char *buf) {
++  adns_rr_addr a;
++  char addrbuf[ADNS_ADDR2TEXT_BUFLEN];
++  int err;
+ 
+-  if (!inet_aton(buf,&ia)) {
++  a.len= sizeof(a.addr);
++  err= adns_text2addr(buf,DNS_PORT, 0, &a.addr.sa,&a.len);
++  switch (err) {
++  case 0:
++    break;
++  case EINVAL:
+     configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
+     return;
++  default:
++    configparseerr(ads,fn,lno,"failed to parse nameserver address `%s': %s",
++		   buf,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(&a.addr.sa, addrbuf));
++  addserver(ads,&a.addr.sa,a.len);
+ }
+ 
+-static void ccf_search(adns_state ads, const char *fn, int lno, const char *buf) {
++static void ccf_search(adns_state ads, const char *fn,
++		       int lno, const char *buf) {
+   const char *bufp, *word;
+   char *newchars, **newptrs, **pp;
+   int count, tl, l;
+@@ -130,9 +149,12 @@
+   tl= 0;
+   while (nextword(&bufp,&word,&l)) { count++; tl += l+1; }
+ 
+-  newptrs= malloc(sizeof(char*)*count);  if (!newptrs) { saveerr(ads,errno); return; }
+-  newchars= malloc(tl);  if (!newchars) { saveerr(ads,errno); free(newptrs); return; }
++  newptrs= malloc(sizeof(char*)*count);
++  if (!newptrs) { saveerr(ads,errno); return; }
+ 
++  newchars= malloc(tl);
++  if (!newchars) { saveerr(ads,errno); free(newptrs); return; }
++
+   bufp= buf;
+   pp= newptrs;
+   while (nextword(&bufp,&word,&l)) {
+@@ -147,19 +169,34 @@
+   ads->searchlist= newptrs;
+ }
+ 
+-static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *buf) {
++static int gen_pton(const char *text, int want_af, adns_sockaddr *a) {
++  int err;
++  int len;
++
++  len= sizeof(*a);
++  err= adns_text2addr(text,0, adns_qf_addrlit_scope_forbid,
++		      &a->sa, &len);
++  if (err) { assert(err == EINVAL); return 0; }
++  if (want_af != AF_UNSPEC && a->sa.sa_family != want_af) return 0;
++  return 1;
++}
++
++static void ccf_sortlist(adns_state ads, const char *fn,
++			 int lno, const char *buf) {
+   const char *word;
+   char tbuf[200], *slash, *ep;
+-  struct in_addr base, mask;
++  const char *maskwhat;
++  struct sortlist *sl;
+   int l;
+-  unsigned long initial, baselocal;
++  int initial= -1;
+ 
+   if (!buf) return;
+-
++  
+   ads->nsortlist= 0;
+   while (nextword(&buf,&word,&l)) {
+     if (ads->nsortlist >= MAXSORTLIST) {
+-      adns__diag(ads,-1,0,"too many sortlist entries, ignoring %.*s onwards",l,word);
++      adns__diag(ads,-1,0,"too many sortlist entries,"
++		 " ignoring %.*s onwards",l,word);
+       return;
+     }
+ 
+@@ -167,59 +204,67 @@
+       configparseerr(ads,fn,lno,"sortlist entry `%.*s' too long",l,word);
+       continue;
+     }
+-
++    
+     memcpy(tbuf,word,l); tbuf[l]= 0;
+     slash= strchr(tbuf,'/');
+     if (slash) *slash++= 0;
+ 
+-    if (!inet_aton(tbuf,&base)) {
++    sl= &ads->sortlist[ads->nsortlist];
++    if (!gen_pton(tbuf, AF_UNSPEC, &sl->base)) {
+       configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf);
+       continue;
+     }
+ 
+     if (slash) {
+-      if (strchr(slash,'.')) {
+-	if (!inet_aton(slash,&mask)) {
++      if (slash[strspn(slash, "0123456789")]) {
++	maskwhat = "mask";
++	if (!gen_pton(slash, sl->base.sa.sa_family, &sl->mask)) {
+ 	  configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash);
+ 	  continue;
+ 	}
+-	if (base.s_addr & ~mask.s_addr) {
+-	  configparseerr(ads,fn,lno,
+-			 "mask `%s' in sortlist overlaps address `%s'",slash,tbuf);
+-	  continue;
+-	}
+       } else {
++	maskwhat = "prefix length";
+ 	initial= strtoul(slash,&ep,10);
+-	if (*ep || initial>32) {
++	if (*ep || initial>adns__addr_width(sl->base.sa.sa_family)) {
+ 	  configparseerr(ads,fn,lno,"mask length `%s' invalid",slash);
+ 	  continue;
+ 	}
+-	mask.s_addr= htonl((0x0ffffffffUL) << (32-initial));
++	sl->mask.sa.sa_family= sl->base.sa.sa_family;
++	adns__prefix_mask(&sl->mask, initial);
+       }
+     } else {
+-      baselocal= ntohl(base.s_addr);
+-      if (!(baselocal & 0x080000000UL)) /* class A */
+-	mask.s_addr= htonl(0x0ff000000UL);
+-      else if ((baselocal & 0x0c0000000UL) == 0x080000000UL)
+-	mask.s_addr= htonl(0x0ffff0000UL); /* class B */
+-      else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL)
+-	mask.s_addr= htonl(0x0ff000000UL); /* class C */
+-      else {
+-	configparseerr(ads,fn,lno,
+-		       "network address `%s' in sortlist is not in classed ranges,"
++      maskwhat = "implied prefix length";
++      initial= adns__guess_prefix_length(&sl->base);
++      if (initial < 0) {
++	configparseerr(ads,fn,lno, "network address `%s'"
++		       " in sortlist is not in classed ranges,"
+ 		       " must specify mask explicitly", tbuf);
+ 	continue;
+       }
++      sl->mask.sa.sa_family= sl->base.sa.sa_family;
++      adns__prefix_mask(&sl->mask, initial);
+     }
+ 
+-    ads->sortlist[ads->nsortlist].base= base;
+-    ads->sortlist[ads->nsortlist].mask= mask;
++    if (!adns__addr_matches(sl->base.sa.sa_family,
++			    adns__sockaddr_addr(&sl->base.sa),
++			    &sl->base,&sl->mask)) {
++      if (initial >= 0) {
++	configparseerr(ads,fn,lno, "%s %d in sortlist"
++		       " overlaps address `%s'",maskwhat,initial,tbuf);
++      } else {
++	configparseerr(ads,fn,lno, "%s `%s' in sortlist"
++		       " overlaps address `%s'",maskwhat,slash,tbuf);
++      }
++      continue;
++    }
++
+     ads->nsortlist++;
+   }
+ }
+ 
+-static void ccf_options(adns_state ads, const char *fn, int lno, const char *buf) {
+-  const char *word;
++static void ccf_options(adns_state ads, const char *fn,
++			int lno, const char *buf) {
++  const char *opt, *word, *endword, *endopt;
+   char *ep;
+   unsigned long v;
+   int l;
+@@ -226,44 +271,97 @@
+ 
+   if (!buf) return;
+ 
++#define WORD__IS(s,op) ((endword-word) op (sizeof(s)-1) && \
++			  !memcmp(word,s,(sizeof(s)-1)))
++#define WORD_IS(s)     (WORD__IS(s,==))
++#define WORD_STARTS(s) (WORD__IS(s,>=) ? ((word+=sizeof(s)-1)) : 0)
++
+   while (nextword(&buf,&word,&l)) {
+-    if (l==5 && !memcmp(word,"debug",5)) {
++    opt=word;
++    endopt=endword=word+l;
++    if (WORD_IS("debug")) {
+       ads->iflags |= adns_if_debug;
+       continue;
+     }
+-    if (l>=6 && !memcmp(word,"ndots:",6)) {
+-      v= strtoul(word+6,&ep,10);
+-      if (l==6 || ep != word+l || v > INT_MAX) {
+-	configparseerr(ads,fn,lno,"option `%.*s' malformed or has bad value",l,word);
++    if (WORD_STARTS("ndots:")) {
++      v= strtoul(word,&ep,10);
++      if (ep==word || ep != endword || v > INT_MAX) {
++	configparseerr(ads,fn,lno,"option `%.*s' malformed"
++		       " or has bad value",l,opt);
+ 	continue;
+       }
+       ads->searchndots= v;
+       continue;
+     }
+-    if (l>=12 && !memcmp(word,"adns_checkc:",12)) {
+-      if (!strcmp(word+12,"none")) {
++    if (WORD_STARTS("adns_checkc:")) {
++      if (WORD_IS("none")) {
+ 	ads->iflags &= ~adns_if_checkc_freq;
+ 	ads->iflags |= adns_if_checkc_entex;
+-      } else if (!strcmp(word+12,"entex")) {
++      } else if (WORD_IS("entex")) {
+ 	ads->iflags &= ~adns_if_checkc_freq;
+ 	ads->iflags |= adns_if_checkc_entex;
+-      } else if (!strcmp(word+12,"freq")) {
++      } else if (WORD_IS("freq")) {
+ 	ads->iflags |= adns_if_checkc_freq;
+       } else {
+ 	configparseerr(ads,fn,lno, "option adns_checkc has bad value `%s' "
+-		       "(must be none, entex or freq", word+12);
++		       "(must be none, entex or freq", word);
+       }
+       continue;
+     }
+-    adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word);
++    if (WORD_STARTS("adns_af:")) {
++      ads->iflags &= ~adns_if_afmask;
++      if (!WORD_IS("any")) for (;;) {
++	const char *comma= memchr(word,',',endopt-word);
++	endword=comma?comma:endopt;
++	if (WORD_IS("ipv4"))
++	  ads->iflags |= adns_if_permit_ipv4;
++	else if (WORD_IS("ipv6"))
++	  ads->iflags |= adns_if_permit_ipv6;
++	else {
++	  if (ads->config_report_unknown)
++	    adns__diag(ads,-1,0,"%s:%d: "
++		       "option adns_af has bad value or entry `%.*s' "
++		       "(option must be `any', or list of `ipv4',`ipv6')",
++		       fn,lno, (int)(endword-word),word);
++	  break;
++	}
++	if (!comma) break;
++	word= comma+1;
++      }
++      continue;
++    }
++    if (WORD_IS("adns_ignoreunkcfg")) {
++      ads->config_report_unknown=0;
++      continue;
++    }
++    if (/* adns's query strategy is not configurable */
++	WORD_STARTS("timeout:") ||
++	WORD_STARTS("attempts:") ||
++	WORD_IS("rotate") ||
++	/* adns provides the application with knob for this */
++	WORD_IS("no-check-names") ||
++	/* adns normally does IPv6 if the application wants it; control
++	 * this with the adns_af: option if you like */
++	WORD_IS("inet6") ||
++	/* adns does not do edns0 and this is not a problem */
++	WORD_IS("edns0"))
++      continue;
++    if (ads->config_report_unknown)
++      adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,opt);
+   }
++
++#undef WORD__IS
++#undef WORD_IS
++#undef WORD_STARTS
+ }
+ 
+-static void ccf_clearnss(adns_state ads, const char *fn, int lno, const char *buf) {
++static void ccf_clearnss(adns_state ads, const char *fn,
++			 int lno, const char *buf) {
+   ads->nservers= 0;
+ }
+ 
+-static void ccf_include(adns_state ads, const char *fn, int lno, const char *buf) {
++static void ccf_include(adns_state ads, const char *fn,
++			int lno, const char *buf) {
+   if (!*buf) {
+     configparseerr(ads,fn,lno,"`include' directive with no filename");
+     return;
+@@ -271,6 +369,40 @@
+   readconfig(ads,buf,1);
+ }
+ 
++static void ccf_lookup(adns_state ads, const char *fn, int lno,
++		       const char *buf) {
++  int found_bind=0;
++  const char *word;
++  int l;
++
++  if (!*buf) {
++    configparseerr(ads,fn,lno,"`lookup' directive with no databases");
++    return;
++  }
++
++  while (nextword(&buf,&word,&l)) {
++    if (l==4 && !memcmp(word,"bind",4)) {
++      found_bind=1;
++    } else if (l==4 && !memcmp(word,"file",4)) {
++      /* ignore this and hope /etc/hosts is not essential */
++    } else if (l==2 && !memcmp(word,"yp",2)) {
++      adns__diag(ads,-1,0,"%s:%d: yp lookups not supported by adns", fn,lno);
++      found_bind=-1;
++    } else {
++      if (ads->config_report_unknown)
++	adns__diag(ads,-1,0,"%s:%d: unknown `lookup' database `%.*s'",
++		   fn,lno, l,word);
++      found_bind=-1;
++    }
++  }
++  if (!found_bind)
++    adns__diag(ads,-1,0,"%s:%d: `lookup' specified, but not `bind'", fn,lno);
++}
++
++static void ccf_ignore(adns_state ads, const char *fn, int lno,
++		       const char *buf) {
++}
++
+ static const struct configcommandinfo {
+   const char *name;
+   void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
+@@ -282,6 +414,8 @@
+   { "options",           ccf_options     },
+   { "clearnameservers",  ccf_clearnss    },
+   { "include",           ccf_include     },
++  { "lookup",            ccf_lookup      }, /* OpenBSD */
++  { "lwserver",          ccf_ignore      }, /* BIND9 lwresd */
+   {  0                                   }
+ };
+ 
+@@ -299,7 +433,7 @@
+   p= buf;
+   buflen--;
+   i= 0;
+-
++    
+   for (;;) { /* loop over chars */
+     if (i == buflen) {
+       adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
+@@ -314,7 +448,8 @@
+     } else if (c == EOF) {
+       if (ferror(file)) {
+ 	saveerr(ads,errno);
+-	adns__diag(ads,-1,0,"%s:%d: read error: %s",filename,lno,strerror(errno));
++	adns__diag(ads,-1,0,"%s:%d: read error: %s",
++		   filename,lno,strerror(errno));
+ 	return -1;
+       }
+       if (!i) return -1;
+@@ -350,7 +485,7 @@
+     saveerr(ads,EINVAL);
+     return -2;
+   }
+-
++    
+   memcpy(buf,cp,l);
+   buf[l]= 0;
+   return l;
+@@ -377,16 +512,18 @@
+     linebuf[l]= 0;
+     p= linebuf;
+     while (ctype_whitespace(*p)) p++;
+-    if (*p == '#' || !*p) continue;
++    if (*p == '#' || *p == ';' || !*p) continue;
+     q= p;
+     while (*q && !ctype_whitespace(*q)) q++;
+     dirl= q-p;
+     for (ccip=configcommandinfos;
+-	 ccip->name && !((int)strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
++	 ccip->name &&
++	   !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
+ 	 ccip++);
+     if (!ccip->name) {
+-      adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
+-		 filename,lno,q-p,p);
++      if (ads->config_report_unknown)
++	adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
++		   filename,lno,(int)(q-p),p);
+       continue;
+     }
+     while (ctype_whitespace(*q)) q++;
+@@ -399,18 +536,20 @@
+ 
+   value= getenv(envvar);
+   if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar);
+-  else adns__debug(ads,-1,0,"environment variable %s set to `%s'",envvar,value);
++  else adns__debug(ads,-1,0,"environment variable %s"
++		   " set to `%s'",envvar,value);
+   return value;
+ }
+ 
+ static void readconfig(adns_state ads, const char *filename, int warnmissing) {
+   getline_ctx gl_ctx;
+-
++  
+   gl_ctx.file= fopen(filename,"r");
+   if (!gl_ctx.file) {
+     if (errno == ENOENT) {
+       if (warnmissing)
+-	adns__debug(ads,-1,0,"configuration file `%s' does not exist",filename);
++	adns__debug(ads,-1,0, "configuration file"
++		    " `%s' does not exist",filename);
+       return;
+     }
+     saveerr(ads,errno);
+@@ -420,17 +559,18 @@
+   }
+ 
+   readconfiggeneric(ads,filename,gl_file,gl_ctx);
+-
++  
+   fclose(gl_ctx.file);
+ }
+ 
+-static void readconfigtext(adns_state ads, const char *text, const char *showname) {
++static void readconfigtext(adns_state ads, const char *text,
++			   const char *showname) {
+   getline_ctx gl_ctx;
+-
++  
+   gl_ctx.text= text;
+   readconfiggeneric(ads,showname,gl_text,gl_ctx);
+ }
+-
++  
+ static void readconfigenv(adns_state ads, const char *envvar) {
+   const char *filename;
+ 
+@@ -454,13 +594,13 @@
+ }
+ 
+ 
+-int adns__setnonblock(adns_state ads, ADNS_SOCKET fd) {
++int adns__setnonblock(adns_state ads, int fd) {
+ #ifdef ADNS_JGAA_WIN32
+    unsigned long Val = 1;
+    return (ioctlsocket (fd, FIONBIO, &Val) == 0) ? 0 : -1;
+ #else
+   int r;
+-
++  
+   r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
+   r |= O_NONBLOCK;
+   r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
+@@ -468,27 +608,30 @@
+ #endif
+ }
+ 
+-static int init_begin(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
++static int init_begin(adns_state *ads_r, adns_initflags flags,
++		      adns_logcallbackfn *logfn, void *logfndata) {
+   adns_state ads;
++  pid_t pid;
++  
++  if (flags & ~(adns_initflags)(0x4fff))
++    /* 0x4000 is reserved for `harmless' future expansion */
++    return ENOSYS;
+ 
+-#ifdef ADNS_JGAA_WIN32
+-  WORD wVersionRequested = MAKEWORD( 2, 0 );
+-  WSADATA wsaData;
+-  int err;
+-#endif
+-
+   ads= malloc(sizeof(*ads)); if (!ads) return errno;
+ 
+   ads->iflags= flags;
+-  ads->diagfile= diagfile;
++  ads->logfn= logfn;
++  ads->logfndata= logfndata;
+   ads->configerrno= 0;
+   LIST_INIT(ads->udpw);
+   LIST_INIT(ads->tcpw);
+   LIST_INIT(ads->childw);
+   LIST_INIT(ads->output);
++  LIST_INIT(ads->intdone);
+   ads->forallnext= 0;
+   ads->nextid= 0x311f;
+-  ads->udpsocket= ads->tcpsocket= -1;
++  ads->nudpsockets= 0;
++  ads->tcpsocket= -1;
+   adns__vbuf_init(&ads->tcpsend);
+   adns__vbuf_init(&ads->tcprecv);
+   ads->tcprecv_skip= 0;
+@@ -497,58 +640,55 @@
+   ads->tcpstate= server_disconnected;
+   timerclear(&ads->tcptimeout);
+   ads->searchlist= 0;
++  ads->config_report_unknown=1;
+ 
+- #ifdef ADNS_JGAA_WIN32
+-  err= WSAStartup( wVersionRequested, &wsaData );
+-  if ( err != 0 ) {
+-    if (ads->diagfile && ads->iflags & adns_if_debug)
+-      fprintf(ads->diagfile,"adns: WSAStartup() failed. \n");
+-    return -1;}
+-  if (LOBYTE( wsaData.wVersion ) != 2 ||
+-    HIBYTE( wsaData.wVersion ) != 0 ) {
+-    if (ads->diagfile && ads->iflags & adns_if_debug)
+-      fprintf(ads->diagfile,"adns: Need Winsock 2.0 or better!\n");
++  pid= getpid();
++  ads->rand48xsubi[0]= pid;
++  ads->rand48xsubi[1]= (unsigned long)pid >> 16;
++  ads->rand48xsubi[2]= pid ^ ((unsigned long)pid >> 16);
+ 
+-    WSACleanup();
+-    return -1;}
+-
+-  /* The WinSock DLL is acceptable. Proceed. */
+-#endif
+-
+   *ads_r= ads;
+-
+   return 0;
+ }
+ 
+ static int init_finish(adns_state ads) {
+-  struct in_addr ia;
++  struct sockaddr_in sin;
+   struct protoent *proto;
++  struct udpsocket *udp;
++  int i;
+   int r;
+-
++  
+   if (!ads->nservers) {
+-    if (ads->diagfile && ads->iflags & adns_if_debug)
+-      fprintf(ads->diagfile,"adns: no nameservers, using localhost\n");
+-    ia.s_addr= htonl(INADDR_LOOPBACK);
+-    addserver(ads,ia);
++    if (ads->logfn && ads->iflags & adns_if_debug)
++      adns__lprintf(ads,"adns: no nameservers, using IPv4 localhost\n");
++    memset(&sin, 0, sizeof(sin));
++    sin.sin_family = AF_INET;
++    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; }
+-  ADNS_CLEAR_ERRNO;
+-  ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
+-  ADNS_CAPTURE_ERRNO;
+-  if (ads->udpsocket<0) { r= errno; goto x_free; }
+-
+-  r= adns__setnonblock(ads,ads->udpsocket);
+-  if (r) { r= errno; goto x_closeudp; }
++  ads->nudpsockets= 0;
++  for (i=0; i<ads->nservers; i++) {
++    if (adns__udpsocket_by_af(ads, ads->servers[i].addr.sa.sa_family))
++      continue;
++    assert(ads->nudpsockets < MAXUDP);
++    udp= &ads->udpsockets[ads->nudpsockets];
++    udp->af= ads->servers[i].addr.sa.sa_family;
++    udp->fd= socket(udp->af,SOCK_DGRAM,proto->p_proto);
++    if (udp->fd < 0) { r= errno; goto x_free; }
++    ads->nudpsockets++;
++    r= adns__setnonblock(ads,udp->fd);
++    if (r) { r= errno; goto x_closeudp; }
++  }
++  
+   return 0;
+ 
+  x_closeudp:
+-  adns_socket_close(ads->udpsocket);
++  for (i=0; i<ads->nudpsockets; i++) adns_socket_close(ads->udpsockets[i].fd);
+  x_free:
+   free(ads);
+-#ifdef ADNS_JGAA_WIN32
+-  WSACleanup();
+-#endif /* WIN32 */
+   return r;
+ }
+ 
+@@ -558,71 +698,29 @@
+     free(ads->searchlist);
+   }
+   free(ads);
+-#ifdef ADNS_JGAA_WIN32
+-  WSACleanup();
+-#endif /* WIN32 */
++}
+ 
++static void logfn_file(adns_state ads, void *logfndata,
++		       const char *fmt, va_list al) {
++  vfprintf(logfndata,fmt,al);
+ }
+ 
+-int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
++static int init_files(adns_state *ads_r, adns_initflags flags,
++		      adns_logcallbackfn *logfn, void *logfndata) {
+   adns_state ads;
+   const char *res_options, *adns_res_options;
+   int r;
+-#ifdef ADNS_JGAA_WIN32
+-  #define SECURE_PATH_LEN (MAX_PATH - 64)
+-  char PathBuf[MAX_PATH];
+-  struct in_addr addr;
+-  #define ADNS_PFIXED_INFO_BLEN (2048)
+-  PFIXED_INFO network_info = (PFIXED_INFO)_alloca(ADNS_PFIXED_INFO_BLEN);
+-  ULONG network_info_blen = ADNS_PFIXED_INFO_BLEN;
+-  DWORD network_info_result;
+-  PIP_ADDR_STRING pip;
+-  const char *network_err_str = "";
+-#endif
+-
+-  r= init_begin(&ads, flags, diagfile ? diagfile : stderr);
++  
++  r= init_begin(&ads, flags, logfn, logfndata);
+   if (r) return r;
+-
++  
+   res_options= instrum_getenv(ads,"RES_OPTIONS");
+   adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS");
+   ccf_options(ads,"RES_OPTIONS",-1,res_options);
+   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
+ 
+-#ifdef ADNS_JGAA_WIN32
+-  GetWindowsDirectory(PathBuf, SECURE_PATH_LEN);
+-  strcat(PathBuf,"\\resolv.conf");
+-  readconfig(ads,PathBuf,1);
+-  GetWindowsDirectory(PathBuf, SECURE_PATH_LEN);
+-  strcat(PathBuf,"\\resolv-adns.conf");
+-  readconfig(ads,PathBuf,0);
+-  GetWindowsDirectory(PathBuf, SECURE_PATH_LEN);
+-  strcat(PathBuf,"\\System32\\Drivers\\etc\\resolv.conf");
+-  readconfig(ads,PathBuf,1);
+-  GetWindowsDirectory(PathBuf, SECURE_PATH_LEN);
+-  strcat(PathBuf,"\\System32\\Drivers\\etc\\resolv-adns.conf");
+-  readconfig(ads,PathBuf,0);
+-  network_info_result = GetNetworkParams(network_info, &network_info_blen);
+-  if (network_info_result != ERROR_SUCCESS){
+-    switch(network_info_result) {
+-    case ERROR_BUFFER_OVERFLOW: network_err_str = "ERROR_BUFFER_OVERFLOW"; break;
+-    case ERROR_INVALID_PARAMETER: network_err_str = "ERROR_INVALID_PARAMETER"; break;
+-    case ERROR_NO_DATA: network_err_str = "ERROR_NO_DATA"; break;
+-    case ERROR_NOT_SUPPORTED: network_err_str = "ERROR_NOT_SUPPORTED"; break;}
+-    adns__diag(ads,-1,0,"GetNetworkParams() failed with error [%d] %s",
+-      network_info_result,network_err_str);
+-    }
+-  else {
+-    for(pip = &(network_info->DnsServerList); pip; pip = pip->Next) {
+-      addr.s_addr = inet_addr(pip->IpAddress.String);
+-      if ((addr.s_addr != INADDR_ANY) && (addr.s_addr != INADDR_NONE))
+-        addserver(ads, addr);
+-    }
+-  }
+-#else
+   readconfig(ads,"/etc/resolv.conf",1);
+   readconfig(ads,"/etc/resolv-adns.conf",0);
+-#endif
+-
+   readconfigenv(ads,"RES_CONF");
+   readconfigenv(ads,"ADNS_RES_CONF");
+ 
+@@ -649,12 +747,18 @@
+   return 0;
+ }
+ 
+-int adns_init_strcfg(adns_state *ads_r, adns_initflags flags,
+-		     FILE *diagfile, const char *configtext) {
++int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
++  return init_files(ads_r, flags, logfn_file, diagfile ? diagfile : stderr);
++}
++
++static int init_strcfg(adns_state *ads_r, adns_initflags flags,
++		       adns_logcallbackfn *logfn, void *logfndata,
++		       const char *configtext) {
+   adns_state ads;
+   int r;
+ 
+-  r= init_begin(&ads, flags, diagfile);  if (r) return r;
++  r= init_begin(&ads, flags, logfn, logfndata);
++  if (r) return r;
+ 
+   readconfigtext(ads,configtext,"<supplied configuration text>");
+   if (ads->configerrno) {
+@@ -669,26 +773,42 @@
+   return 0;
+ }
+ 
++int adns_init_strcfg(adns_state *ads_r, adns_initflags flags,
++		     FILE *diagfile, const char *configtext) {
++  return init_strcfg(ads_r, flags,
++		     diagfile ? logfn_file : 0, diagfile,
++		     configtext);
++}
+ 
++int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
++		    const char *configtext /*0=>use default config files*/,
++		    adns_logcallbackfn *logfn /*0=>logfndata is a FILE* */,
++		    void *logfndata /*0 with logfn==0 => discard*/) {
++  if (!logfn && logfndata)
++    logfn= logfn_file;
++  if (configtext)
++    return init_strcfg(newstate_r, flags, logfn, logfndata, configtext);
++  else
++    return init_files(newstate_r, flags, logfn, logfndata);
++}
++
+ void adns_finish(adns_state ads) {
++  int i;
+   adns__consistency(ads,0,cc_entex);
+   for (;;) {
+-    if (ads->udpw.head) adns_cancel(ads->udpw.head);
+-    else if (ads->tcpw.head) adns_cancel(ads->tcpw.head);
+-    else if (ads->childw.head) adns_cancel(ads->childw.head);
+-    else if (ads->output.head) adns_cancel(ads->output.head);
++    if (ads->udpw.head) adns__cancel(ads->udpw.head);
++    else if (ads->tcpw.head) adns__cancel(ads->tcpw.head);
++    else if (ads->childw.head) adns__cancel(ads->childw.head);
++    else if (ads->output.head) adns__cancel(ads->output.head);
++    else if (ads->intdone.head) adns__cancel(ads->output.head);
+     else break;
+   }
+-  adns_socket_close(ads->udpsocket);
+-  if (ads->tcpsocket != -1) adns_socket_close(ads->tcpsocket);
++  for (i=0; i<ads->nudpsockets; i++) adns_socket_close(ads->udpsockets[i].fd);
++  if (ads->tcpsocket >= 0) adns_socket_close(ads->tcpsocket);
+   adns__vbuf_free(&ads->tcpsend);
+   adns__vbuf_free(&ads->tcprecv);
+   freesearchlist(ads);
+   free(ads);
+-#ifdef ADNS_JGAA_WIN32
+-  WSACleanup();
+-#endif /* WIN32 */
+-
+ }
+ 
+ void adns_forallqueries_begin(adns_state ads) {
+@@ -699,7 +819,7 @@
+     ads->childw.head ? ads->childw.head :
+     ads->output.head;
+ }
+-
++  
+ adns_query adns_forallqueries_next(adns_state ads, void **context_r) {
+   adns_query qu, nqu;
+ 
+@@ -730,8 +850,3 @@
+   if (context_r) *context_r= qu->ctx.ext;
+   return qu;
+ }
+-
+-/* ReactOS addition */
+-void adns_addserver(adns_state ads, struct in_addr addr) {
+-    addserver(ads, addr);
+-}
+Index: transmit.c
+===================================================================
+--- transmit.c	(revision 71177)
++++ transmit.c	(working copy)
+@@ -4,26 +4,25 @@
+  * - send queries
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #include <errno.h>
+@@ -43,15 +42,16 @@
+ #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
+ #define MKQUERY_STOP(vb) ((vb)->used= rqp-(vb)->buf)
+ 
+-static adns_status mkquery_header(adns_state ads, vbuf *vb, int *id_r, int qdlen) {
++static adns_status mkquery_header(adns_state ads, vbuf *vb,
++				  int *id_r, int qdlen) {
+   int id;
+   byte *rqp;
+-
++  
+   if (!adns__vbuf_ensure(vb,DNS_HDRSIZE+qdlen+4)) return adns_s_nomemory;
+ 
+   vb->used= 0;
+   MKQUERY_START(vb);
+-
++  
+   *id_r= id= (ads->nextid++) & 0x0ffff;
+   MKQUERY_ADDW(id);
+   MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
+@@ -62,7 +62,7 @@
+   MKQUERY_ADDW(0); /* ARCOUNT=0 */
+ 
+   MKQUERY_STOP(vb);
+-
++  
+   return adns_s_ok;
+ }
+ 
+@@ -70,73 +70,89 @@
+   byte *rqp;
+ 
+   MKQUERY_START(vb);
+-  MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */
++  MKQUERY_ADDW(type & adns_rrt_typemask); /* QTYPE */
+   MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */
+   MKQUERY_STOP(vb);
+   assert(vb->used <= vb->avail);
++  
++  return adns_s_ok;
++}
+ 
++static adns_status qdparselabel(adns_state ads,
++				const char **p_io, const char *pe,
++				char label_r[], int *ll_io,
++				adns_queryflags flags) {
++  int ll, c;
++  const char *p;
++
++  ll= 0;
++  p= *p_io;
++  
++  while (p!=pe && (c= *p++)!='.') {
++    if (c=='\\') {
++      if (!(flags & adns_qf_quoteok_query)) return adns_s_querydomaininvalid;
++      if (ctype_digit(p[0])) {
++	if (p+1==pe || p+2==pe) return adns_s_querydomaininvalid;
++	if (ctype_digit(p[1]) && ctype_digit(p[2])) {
++	  c= (*p++ - '0')*100;
++	  c += (*p++ - '0')*10;
++	  c += (*p++ - '0');
++	  if (c >= 256) return adns_s_querydomaininvalid;
++	} else {
++	  return adns_s_querydomaininvalid;
++	}
++      } else if (!(c= *p++)) {
++	return adns_s_querydomaininvalid;
++      }
++    }
++    if (ll == *ll_io) return adns_s_querydomaininvalid;
++    label_r[ll++]= c;
++  }
++  
++  *p_io= p;
++  *ll_io= ll;
+   return adns_s_ok;
+ }
+ 
+ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+ 			  const char *owner, int ol,
+-			  const typeinfo *typei, adns_queryflags flags) {
+-  int ll, c, nbytes;
+-  byte label[255], *rqp;
++			  const typeinfo *typei, adns_rrtype type,
++			  adns_queryflags flags) {
++  int ll, nbytes;
++  byte label[255];
++  byte *rqp;
+   const char *p, *pe;
+   adns_status st;
+ 
+   st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st;
+-
++  
+   MKQUERY_START(vb);
+ 
+   p= owner; pe= owner+ol;
+   nbytes= 0;
+   while (p!=pe) {
+-    ll= 0;
+-    while (p!=pe && (c= *p++)!='.') {
+-      if (c=='\\') {
+-	if (!(flags & adns_qf_quoteok_query)) return adns_s_querydomaininvalid;
+-	if (ctype_digit(p[0])) {
+-	  if (ctype_digit(p[1]) && ctype_digit(p[2])) {
+-	    c= (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
+-	    p += 3;
+-	    if (c >= 256) return adns_s_querydomaininvalid;
+-	  } else {
+-	    return adns_s_querydomaininvalid;
+-	  }
+-	} else if (!(c= *p++)) {
+-	  return adns_s_querydomaininvalid;
+-	}
+-      }
+-      if (!(flags & adns_qf_quoteok_query)) {
+-	if (c == '-') {
+-	  if (!ll) return adns_s_querydomaininvalid;
+-	} else if (!ctype_alpha(c) && !ctype_digit(c)) {
+-	  return adns_s_querydomaininvalid;
+-	}
+-      }
+-      if (ll == sizeof(label)) return adns_s_querydomaininvalid;
+-      label[ll++]= c;
+-    }
++    ll= sizeof(label);
++    st= qdparselabel(ads, &p,pe, label, &ll, flags);
++    if (st) return st;
+     if (!ll) return adns_s_querydomaininvalid;
+     if (ll > DNS_MAXLABEL) return adns_s_querydomaintoolong;
+     nbytes+= ll+1;
+     if (nbytes >= DNS_MAXDOMAIN) return adns_s_querydomaintoolong;
+     MKQUERY_ADDB(ll);
+-    memcpy(rqp,label,(size_t) ll); rqp+= ll;
++    memcpy(rqp,label,ll); rqp+= ll;
+   }
+   MKQUERY_ADDB(0);
+ 
+   MKQUERY_STOP(vb);
+-
+-  st= mkquery_footer(vb,typei->type);
+-
++  
++  st= mkquery_footer(vb,type);
++  
+   return adns_s_ok;
+ }
+ 
+ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+-				  const byte *qd_dgram, int qd_dglen, int qd_begin,
++				  const byte *qd_dgram, int qd_dglen,
++				  int qd_begin,
+ 				  adns_rrtype type, adns_queryflags flags) {
+   byte *rqp;
+   findlabel_state fls;
+@@ -153,15 +169,15 @@
+     if (!lablen) break;
+     assert(lablen<255);
+     MKQUERY_ADDB(lablen);
+-    memcpy(rqp,qd_dgram+labstart, (size_t) lablen);
++    memcpy(rqp,qd_dgram+labstart,lablen);
+     rqp+= lablen;
+   }
+   MKQUERY_ADDB(0);
+ 
+   MKQUERY_STOP(vb);
+-
++  
+   st= mkquery_footer(vb,type);
+-
++  
+   return adns_s_ok;
+ }
+ 
+@@ -179,7 +195,8 @@
+   length[1]= (qu->query_dglen&0x0ff);
+ 
+   ads= qu->ads;
+-  if (!adns__vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->query_dglen+2)) return;
++  if (!adns__vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->query_dglen+2))
++    return;
+ 
+   qu->retries++;
+ 
+@@ -189,18 +206,15 @@
+   if (ads->tcpsend.used) {
+     wr= 0;
+   } else {
+-    iov[0].iov_base= (char*)length;
++    iov[0].iov_base= length;
+     iov[0].iov_len= 2;
+-    iov[1].iov_base= (char*)qu->query_dgram;
++    iov[1].iov_base= qu->query_dgram;
+     iov[1].iov_len= qu->query_dglen;
+     adns__sigpipe_protect(qu->ads);
+-
+-    ADNS_CLEAR_ERRNO;
+     wr= writev(qu->ads->tcpsocket,iov,2);
+-    ADNS_CAPTURE_ERRNO;
+     adns__sigpipe_unprotect(qu->ads);
+     if (wr < 0) {
+-      if (!(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ENOSPC ||
++      if (!(errno == EAGAIN || errno == EINTR || errno == ENOSPC ||
+ 	    errno == ENOBUFS || errno == ENOMEM)) {
+ 	adns__tcp_broken(ads,"write",strerror(errno));
+ 	return;
+@@ -216,7 +230,8 @@
+     wr-= 2;
+   }
+   if (wr<qu->query_dglen) {
+-    r= adns__vbuf_append(&ads->tcpsend,qu->query_dgram+wr,qu->query_dglen-wr); assert(r);
++    r= adns__vbuf_append(&ads->tcpsend,qu->query_dgram+wr,qu->query_dglen-wr);
++    assert(r);
+   }
+ }
+ 
+@@ -229,10 +244,18 @@
+   adns__tcp_tryconnect(qu->ads,now);
+ }
+ 
++struct udpsocket *adns__udpsocket_by_af(adns_state ads, int af) {
++  int i;
++  for (i=0; i<ads->nudpsockets; i++)
++    if (ads->udpsockets[i].af == af) return &ads->udpsockets[i];
++  return 0;
++}
++
+ void adns__query_send(adns_query qu, struct timeval now) {
+-  struct sockaddr_in servaddr;
+   int serv, r;
+   adns_state ads;
++  struct udpsocket *udp;
++  adns_rr_addr *addr;
+ 
+   assert(qu->state == query_tosend);
+   if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) {
+@@ -245,21 +268,22 @@
+     return;
+   }
+ 
++  ads= qu->ads;
+   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);
+-
+-  ADNS_CLEAR_ERRNO;
+-  r= sendto(ads->udpsocket,(char*)qu->query_dgram,qu->query_dglen,0,
+-	    (const struct sockaddr*)&servaddr,sizeof(servaddr));
+-  ADNS_CAPTURE_ERRNO;
+-  if (r<0 && errno == EMSGSIZE) { qu->retries= 0; query_usetcp(qu,now); return; }
+-  if (r<0 && ((errno != EAGAIN) && (errno != EWOULDBLOCK))) adns__warn(ads,serv,0,"sendto failed: %s (%d)",strerror(errno), errno);
+-
++  addr= &ads->servers[serv];
++  udp= adns__udpsocket_by_af(ads, addr->addr.sa.sa_family);
++  assert(udp);
++  
++  r= sendto(udp->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);
++    return;
++  }
++  if (r<0 && errno != EAGAIN)
++    adns__warn(ads,serv,0,"sendto failed: %s",strerror(errno));
++  
+   qu->timeout= now;
+   timevaladd(&qu->timeout,UDPRETRYMS);
+   qu->udpsent |= (1<<serv);
+Index: tvarith.h
+===================================================================
+--- tvarith.h	(revision 71177)
++++ tvarith.h	(working copy)
+@@ -3,16 +3,16 @@
+  * - static inline functions for doing arithmetic on timevals
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
+  *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+  *
+  *  This program is distributed in the hope that it will be useful,
+@@ -21,8 +21,7 @@
+  *  GNU General Public License for more details.
+  *
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #ifndef ADNS_TVARITH_H_INCLUDED
+Index: types.c
+===================================================================
+--- types.c	(revision 71177)
++++ types.c	(working copy)
+@@ -3,31 +3,31 @@
+  * - RR-type-specific code, and the machinery to call it
+  */
+ /*
+- *  This file is
+- *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+- *
+- *  It is part of adns, which is
+- *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+- *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+- *
++ *  This file is part of adns, which is
++ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
++ *    Copyright (C) 2014  Mark Wooding
++ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
++ *    Copyright (C) 1991 Massachusetts Institute of Technology
++ *  (See the file INSTALL for full details.)
++ *  
+  *  This program is free software; you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+- *  the Free Software Foundation; either version 2, or (at your option)
++ *  the Free Software Foundation; either version 3, or (at your option)
+  *  any later version.
+- *
++ *  
+  *  This program is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+- *
++ *  
+  *  You should have received a copy of the GNU General Public License
+- *  along with this program; if not, write to the Free Software Foundation,
+- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *  along with this program; if not, write to the Free Software Foundation.
+  */
+ 
+ #ifdef ADNS_JGAA_WIN32
+ # include "adns_win32.h"
+ #else
++# include <stddef.h>
+ # include <stdlib.h>
+ # include <sys/types.h>
+ # include <sys/socket.h>
+@@ -37,8 +37,10 @@
+ 
+ #include "internal.h"
+ 
+-#define R_NOMEM           return adns_s_nomemory
+-#define CSP_ADDSTR(s)     do { if (!adns__vbuf_appendstr(vb,(s))) R_NOMEM; } while (0)
++#define R_NOMEM       return adns_s_nomemory
++#define CSP_ADDSTR(s) do {			\
++    if (!adns__vbuf_appendstr(vb,(s))) R_NOMEM;	\
++  } while (0)
+ 
+ /*
+  * order of sections:
+@@ -49,24 +51,35 @@
+  * _intstr                    (mf,csp,cs)
+  * _manyistr                  (mf,cs)
+  * _txt                       (pa)
+- * _inaddr                    (pa,dip,di)
+- * _addr                      (pa,di,csp,cs)
+- * _domain                    (pap)
++ * _inaddr                    (pa,di,cs
++ *				+search_sortlist, dip_genaddr, csp_genaddr)
++ * _in6addr		      (pa,di,cs)
++ * _addr                      (pap,pa,di,div,csp,cs,gsz,qs
++ *				+search_sortlist_sa, dip_sockaddr,
++ *				 addr_rrtypes, addr_submit, icb_addr)
++ * _domain                    (pap,csp,cs)
++ * _dom_raw		      (pa)
+  * _host_raw                  (pa)
+- * _hostaddr                  (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs)
++ * _hostaddr                  (pap,pa,dip,di,mfp,mf,csp,cs
++ *				+pap_findaddrs, icb_hostaddr)
+  * _mx_raw                    (pa,di)
+  * _mx                        (pa,di)
+  * _inthostaddr               (mf,cs)
+- * _ptr                       (pa)
+- * _strpair                   (mf,cs)
+- * _intstrpair                (mf,cs)
++ * _inthost		      (cs)
++ * _ptr                       (ckl,pa +icb_ptr)
++ * _strpair                   (mf)
++ * _intstrpair                (mf)
+  * _hinfo                     (pa)
+- * _mailbox                   (pap +pap_mailbox822)
+- * _rp                        (pa)
++ * _mailbox                   (pap,csp +pap_mailbox822)
++ * _rp                        (pa,cs)
+  * _soa                       (pa,mf,cs)
++ * _srv*                      (ckl,(pap),pa*2,mf*2,di,(csp),cs*2,postsort)
++ * _byteblock                 (mf)
++ * _opaque                    (pa,cs)
+  * _flat                      (mf)
+  *
+  * within each section:
++ *    ckl_*
+  *    pap_*
+  *    pa_*
+  *    dip_*
+@@ -75,6 +88,9 @@
+  *    mf_*
+  *    csp_*
+  *    cs_*
++ *    gsz_*
++ *    postsort_*
++ *    qs_*
+  */
+ 
+ /*
+@@ -95,17 +111,17 @@
+   if (cbyte >= max) return adns_s_invaliddata;
+   GET_B(cbyte,l);
+   if (cbyte+l > max) return adns_s_invaliddata;
+-
+-  str= adns__alloc_interim(pai->qu, (size_t)l+1);
++  
++  str= adns__alloc_interim(pai->qu, l+1);
+   if (!str) R_NOMEM;
+-
++  
+   str[l]= 0;
+-  memcpy(str,dgram+cbyte,(size_t)l);
++  memcpy(str,dgram+cbyte,l);
+ 
+   *len_r= l;
+   *str_r= str;
+   *cbyte_io= cbyte+l;
+-
++  
+   return adns_s_ok;
+ }
+ 
+@@ -129,7 +145,7 @@
+     }
+   }
+   CSP_ADDSTR("\"");
+-
++  
+   return adns_s_ok;
+ }
+ 
+@@ -175,7 +191,8 @@
+  * _txt   (pa,cs)
+  */
+ 
+-static adns_status pa_txt(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_txt(const parseinfo *pai, int cbyte,
++			  int max, void *datap) {
+   adns_rr_intstr **rrp= datap, *table, *te;
+   const byte *dgram= pai->dgram;
+   int ti, tc, l, startbyte;
+@@ -202,7 +219,7 @@
+ 
+   te->i= -1;
+   te->str= 0;
+-
++  
+   *rrp= table;
+   return adns_s_ok;
+ }
+@@ -235,73 +252,232 @@
+ }
+ 
+ /*
+- * _inaddr   (pa,dip,di)
++ * _inaddr   (pa,di,cs +search_sortlist, dip_genaddr, csp_genaddr)
+  */
+ 
+-static adns_status pa_inaddr(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_inaddr(const parseinfo *pai, int cbyte,
++			     int max, void *datap) {
+   struct in_addr *storeto= datap;
+-
++  
+   if (max-cbyte != 4) return adns_s_invaliddata;
+   memcpy(storeto, pai->dgram + cbyte, 4);
+   return adns_s_ok;
+ }
+ 
+-static int search_sortlist(adns_state ads, struct in_addr ad) {
++static int search_sortlist(adns_state ads, int af, const void *ad) {
+   const struct sortlist *slp;
++  struct in_addr a4;
+   int i;
+-
++  int v6mappedp= 0;
++#ifdef WITH_IPV6
++  if (af == AF_INET6) {
++    const struct in6_addr *a6= ad;
++    if (IN6_IS_ADDR_V4MAPPED(a6)) {
++      a4.s_addr= htonl(((unsigned long)a6->s6_addr[12] << 24) |
++		       ((unsigned long)a6->s6_addr[13] << 16) |
++		       ((unsigned long)a6->s6_addr[14] <<  8) |
++		       ((unsigned long)a6->s6_addr[15] <<  0));
++      v6mappedp= 1;
++    }
++  }
++#endif
+   for (i=0, slp=ads->sortlist;
+-       i<ads->nsortlist && !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr);
++       i<ads->nsortlist &&
++	 !adns__addr_matches(af,ad, &slp->base,&slp->mask) &&
++	 !(v6mappedp &&
++	   adns__addr_matches(AF_INET,&a4, &slp->base,&slp->mask));
+        i++, slp++);
+   return i;
+ }
+ 
+-static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) {
++static int dip_genaddr(adns_state ads, int af, const void *a, const void *b) {
+   int ai, bi;
+-
++  
+   if (!ads->nsortlist) return 0;
+ 
+-  ai= search_sortlist(ads,a);
+-  bi= search_sortlist(ads,b);
++  ai= search_sortlist(ads,af,a);
++  bi= search_sortlist(ads,af,b);
+   return bi<ai;
+ }
+ 
+-static int di_inaddr(adns_state ads, const void *datap_a, const void *datap_b) {
+-  const struct in_addr *ap= datap_a, *bp= datap_b;
++static int di_inaddr(adns_state ads,
++		     const void *datap_a, const void *datap_b) {
++  return dip_genaddr(ads,AF_INET,datap_a,datap_b);
++}
+ 
+-  return dip_inaddr(ads,*ap,*bp);
++static adns_status csp_genaddr(vbuf *vb, int af, const void *p) {
++  char buf[ADNS_ADDR2TEXT_BUFLEN];
++  int len= sizeof(buf);
++  adns_rr_addr a;
++  int err;
++
++  memset(&a, 0, sizeof(a));
++  a.addr.sa.sa_family= af;
++  adns__addr_inject(p, &a.addr);
++  err= adns_addr2text(&a.addr.sa,0, buf,&len, 0); assert(!err);
++  CSP_ADDSTR(buf);
++  return adns_s_ok;
+ }
+ 
+ static adns_status cs_inaddr(vbuf *vb, const void *datap) {
+-  const struct in_addr *rrp= datap, rr= *rrp;
+-  const char *ia;
++  return csp_genaddr(vb, AF_INET,datap);
++}
+ 
+-  ia= inet_ntoa(rr); assert(ia);
+-  CSP_ADDSTR(ia);
++/*
++ * _in6addr   (pa,di,cs)
++ */
++
++#ifdef WITH_IPV6
++static adns_status pa_in6addr(const parseinfo *pai, int cbyte,
++			     int max, void *datap) {
++  struct in6_addr *storeto= datap;
++
++  if (max-cbyte != 16) return adns_s_invaliddata;
++  memcpy(storeto->s6_addr, pai->dgram + cbyte, 16);
+   return adns_s_ok;
+ }
+ 
++static int di_in6addr(adns_state ads,
++		     const void *datap_a, const void *datap_b) {
++  return dip_genaddr(ads,AF_INET6,datap_a,datap_b);
++}
++
++static adns_status cs_in6addr(vbuf *vb, const void *datap) {
++  return csp_genaddr(vb,AF_INET6,datap);
++}
++#endif
+ /*
+- * _addr   (pa,di,csp,cs)
++ * _addr   (pap,pa,di,div,csp,cs,gsz,qs
++ *		+search_sortlist_sa, dip_sockaddr, addr_rrtypes,
++ *		 addr_submit, icb_addr)
+  */
+ 
+-static adns_status pa_addr(const parseinfo *pai, int cbyte, int max, void *datap) {
+-  adns_rr_addr *storeto= datap;
+-  const byte *dgram= pai->dgram;
++static const typeinfo tinfo_addrsub;
+ 
+-  if (max-cbyte != 4) return adns_s_invaliddata;
+-  storeto->len= sizeof(storeto->addr.inet);
+-  memset(&storeto->addr,0,sizeof(storeto->addr.inet));
+-  storeto->addr.inet.sin_family= AF_INET;
+-  memcpy(&storeto->addr.inet.sin_addr,dgram+cbyte,4);
++#define ADDR_RRTYPES(_) _(a) _(aaaa)
++
++static const adns_rrtype addr_all_rrtypes[] = {
++#define RRTY_CODE(ty) adns_r_##ty,
++  ADDR_RRTYPES(RRTY_CODE)
++#undef RRTY_CODE
++};
++
++enum {
++#define RRTY_INDEX(ty) addr__ri_##ty,
++  ADDR_RRTYPES(RRTY_INDEX)
++#undef RRTY_INDEX
++  addr_nrrtypes,
++#define RRTY_FLAG(ty) addr_rf_##ty = 1 << addr__ri_##ty,
++  ADDR_RRTYPES(RRTY_FLAG)
++  addr__rrty_eat_final_comma
++#undef RRTY_FLAG
++};
++
++static unsigned addr_rrtypeflag(adns_rrtype type) {
++  int i;
++
++  type &= adns_rrt_typemask;
++  for (i=0; i<addr_nrrtypes; i++)
++     if (type==addr_all_rrtypes[i])
++       return 1 << i;
++  return 0;
++}
++
++/* About CNAME handling in addr queries.
++ *
++ * A user-level addr query is translated into a number of protocol-level
++ * queries, and its job is to reassemble the results.  This gets tricky if
++ * the answers aren't consistent.  In particular, if the answers report
++ * inconsistent indirection via CNAME records (e.g., different CNAMEs, or
++ * some indirect via a CNAME, and some don't) then we have trouble.
++ *
++ * Once we've received an answer, even if it was NODATA, we set
++ * adns__qf_addr_answer on the parent query.  This will let us detect a
++ * conflict between a no-CNAME-with-NODATA reply and a subsequent CNAME.
++ *
++ * If we detect a conflict of any kind, then at least one answer came back
++ * with a CNAME record, so we pick the first such answer (somewhat
++ * arbitrarily) as being the `right' canonical name, and set this in the
++ * parent query's answer->cname slot.  We discard address records from the
++ * wrong name.  And finally we cancel the outstanding child queries, and
++ * resubmit address queries for the address families we don't yet have, with
++ * adns__qf_addr_cname set so that we know that we're in the fixup state.
++ */
++
++static adns_status pap_addr(const parseinfo *pai, int in_rrty, size_t out_rrsz,
++			    int *cbyte_io, int cbyte_max, adns_rr_addr *out) {
++  int in_addrlen;
++  int out_af, out_salen;
++#ifdef WITH_IPV6
++  struct in6_addr v6map;
++#endif
++  const void *use_addr= pai->dgram + *cbyte_io;
++
++  switch (in_rrty) {
++  case adns_r_a:    in_addrlen= 4;  out_af= AF_INET;  break;
++#ifdef WITH_IPV6
++  case adns_r_aaaa: in_addrlen= 16; out_af= AF_INET6; break;
++#endif
++  default: abort();
++  }
++
++  if ((*cbyte_io + in_addrlen) != cbyte_max) return adns_s_invaliddata;
++
++#ifdef WITH_IPV6
++  if (out_af==AF_INET &&
++      (pai->qu->flags & adns_qf_ipv6_mapv4) &&
++      (pai->qu->answer->type & adns__qtf_bigaddr)) {
++    memset(v6map.s6_addr +  0, 0x00,    10);
++    memset(v6map.s6_addr + 10, 0xff,     2);
++    memcpy(v6map.s6_addr + 12, use_addr, 4);
++    use_addr= v6map.s6_addr;
++    out_af= AF_INET6;
++  }
++#endif
++
++  switch (out_af) {
++  case AF_INET:  out_salen= sizeof(out->addr.inet);  break;
++#ifdef WITH_IPV6
++  case AF_INET6: out_salen= sizeof(out->addr.inet6); break;
++#endif
++  default: abort();
++  }
++
++  assert(offsetof(adns_rr_addr, addr) + out_salen <= out_rrsz);
++
++  memset(&out->addr, 0, out_salen);
++  out->len= out_salen;
++  out->addr.sa.sa_family= out_af;
++  adns__addr_inject(use_addr, &out->addr);
++
++  *cbyte_io += in_addrlen;
+   return adns_s_ok;
+ }
+ 
++static adns_status pa_addr(const parseinfo *pai, int cbyte,
++			   int max, void *datap) {
++  int err= pap_addr(pai, pai->qu->answer->type & adns_rrt_typemask,
++		    pai->qu->answer->rrsz, &cbyte, max, datap);
++  if (err) return err;
++  if (cbyte != max) return adns_s_invaliddata;
++  return adns_s_ok;
++}
++
++static int search_sortlist_sa(adns_state ads, const struct sockaddr *sa) {
++  const void *pa = adns__sockaddr_addr(sa);
++  return search_sortlist(ads, sa->sa_family, pa);
++}
++
++static int dip_sockaddr(adns_state ads,
++			const struct sockaddr *sa,
++			const struct sockaddr *sb) {
++  if (!ads->sortlist) return 0;
++  return search_sortlist_sa(ads, sa) > search_sortlist_sa(ads, sb);
++}
++
+ static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) {
+   const adns_rr_addr *ap= datap_a, *bp= datap_b;
+-
+-  assert(ap->addr.sa.sa_family == AF_INET);
+-  return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr);
++  return dip_sockaddr(ads, &ap->addr.sa, &bp->addr.sa);
+ }
+ 
+ static int div_addr(void *context, const void *datap_a, const void *datap_b) {
+@@ -308,17 +484,25 @@
+   const adns_state ads= context;
+ 
+   return di_addr(ads, datap_a, datap_b);
+-}
++}		     
+ 
+ static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) {
+-  const char *ia;
+-  static char buf[30];
++  char buf[ADNS_ADDR2TEXT_BUFLEN];
++  int len= sizeof(buf);
++  int err;
+ 
+   switch (rrp->addr.inet.sin_family) {
+   case AF_INET:
+     CSP_ADDSTR("INET ");
+-    ia= inet_ntoa(rrp->addr.inet.sin_addr); assert(ia);
+-    CSP_ADDSTR(ia);
++#ifdef WITH_IPV6
++    goto a2t;
++  case AF_INET6:
++    CSP_ADDSTR("INET6 ");
++    goto a2t;
++  a2t:
++#endif
++    err= adns_addr2text(&rrp->addr.sa,0, buf,&len, 0); assert(!err);
++    CSP_ADDSTR(buf);
+     break;
+   default:
+     sprintf(buf,"AF=%u",rrp->addr.sa.sa_family);
+@@ -334,6 +518,255 @@
+   return csp_addr(vb,rrp);
+ }
+ 
++static int gsz_addr(const typeinfo *typei, adns_rrtype type) {
++  return type & adns__qtf_bigaddr ?
++    sizeof(adns_rr_addr) : sizeof(adns_rr_addr_v4only);
++}
++
++static unsigned addr_rrtypes(adns_state ads, adns_rrtype type,
++			     adns_queryflags qf) {
++  /* Return a mask of addr_rf_... flags indicating which address families are
++   * wanted, given a query type and flags.
++   */
++
++  adns_queryflags permitaf= 0;
++  unsigned want= 0;
++
++  if (!(type & adns__qtf_bigaddr))
++    qf= (qf & ~adns_qf_want_allaf) | adns_qf_want_ipv4;
++  else {
++    if (!(qf & adns_qf_want_allaf)) {
++      qf |= (type & adns__qtf_manyaf) ?
++	adns_qf_want_allaf : adns_qf_want_ipv4;
++    }
++    if (ads->iflags & adns_if_permit_ipv4) permitaf |= adns_qf_want_ipv4;
++    if (ads->iflags & adns_if_permit_ipv6) permitaf |= adns_qf_want_ipv6;
++    if (qf & permitaf) qf &= permitaf | ~adns_qf_want_allaf;
++  }
++
++  if (qf & adns_qf_want_ipv4) want |= addr_rf_a;
++  if (qf & adns_qf_want_ipv6) want |= addr_rf_aaaa;
++
++  return want;
++}
++
++static void icb_addr(adns_query parent, adns_query child);
++
++static void addr_subqueries(adns_query qu, struct timeval now,
++			    adns_queryflags qf_extra,
++			    const byte *qd_dgram, int qd_dglen) {
++  int i, err, id;
++  adns_query cqu;
++  adns_queryflags qf= (qu->flags & ~adns_qf_search) | qf_extra;
++  adns_rrtype qtf= qu->answer->type & adns__qtf_deref;
++  unsigned which= qu->ctx.tinfo.addr.want & ~qu->ctx.tinfo.addr.have;
++  qcontext ctx;
++
++  memset(&ctx, 0, sizeof(ctx));
++  ctx.callback= icb_addr;
++  for (i=0; i<addr_nrrtypes; i++) {
++    if (!(which & (1 << i))) continue;
++    err= adns__mkquery_frdgram(qu->ads, &qu->vb, &id, qd_dgram,qd_dglen,
++			       DNS_HDRSIZE, addr_all_rrtypes[i], qf);
++    if (err) goto x_error;
++    err= adns__internal_submit(qu->ads, &cqu, qu, &tinfo_addrsub,
++			       addr_all_rrtypes[i] | qtf,
++			       &qu->vb, id, qf, now, &ctx);
++    if (err) goto x_error;
++    cqu->answer->rrsz= qu->answer->rrsz;
++  }
++  qu->state= query_childw;
++  LIST_LINK_TAIL(qu->ads->childw, qu);
++  return;
++
++x_error:
++  adns__query_fail(qu, err);
++}
++
++static adns_status addr_submit(adns_query parent, adns_query *query_r,
++			       vbuf *qumsg_vb, int id, unsigned want,
++			       adns_queryflags flags, struct timeval now,
++			       qcontext *ctx) {
++  /* This is effectively a substitute for adns__internal_submit, intended for
++   * the case where the caller (possibly) only wants a subset of the
++   * available record types.  The memory management and callback rules are
++   * the same as for adns__internal_submit.
++   *
++   * Some differences: the query is linked onto the parent's children
++   * list before exit (though the parent's state is not changed, and
++   * it is not linked into the childw list queue); and we set the
++   * `tinfo' portion of the context structure (yes, modifying *ctx),
++   * since this is, in fact, the main purpose of this function.
++   */
++
++  adns_state ads= parent->ads;
++  adns_query qu;
++  adns_status err;
++  adns_rrtype type= ((adns_r_addr & adns_rrt_reprmask) |
++		     (parent->answer->type & ~adns_rrt_reprmask));
++
++  ctx->tinfo.addr.want= want;
++  ctx->tinfo.addr.have= 0;
++  err= adns__internal_submit(ads, &qu, parent, adns__findtype(adns_r_addr),
++			     type, qumsg_vb, id, flags, now, ctx);
++  if (err) return err;
++
++  *query_r= qu;
++  return adns_s_ok;
++}
++
++static adns_status append_addrs(adns_query qu, size_t rrsz,
++				adns_rr_addr **dp, int *dlen,
++				const adns_rr_addr *sp, int slen) {
++  /* Append a vector of slen addr records, each of size rrsz, starting at ap,
++   * to a vector starting at *dp, of length *dlen.  On successful completion,
++   * *dp and *dlen are updated.
++   */
++
++  size_t drrsz= *dlen*rrsz, srrsz= slen*rrsz;
++  byte *p;
++
++  if (!slen) return adns_s_ok;
++  p= adns__alloc_interim(qu, drrsz + srrsz);
++  if (!p) R_NOMEM;
++  if (*dlen) {
++    memcpy(p, *dp, drrsz);
++    adns__free_interim(qu, *dp);
++  }
++  memcpy(p + drrsz, sp, srrsz);
++  *dlen += slen;
++  *dp= (adns_rr_addr *)p;
++  return adns_s_ok;
++}
++
++static void propagate_ttl(adns_query to, adns_query from)
++  { if (to->expires > from->expires) to->expires= from->expires; }
++
++static adns_status copy_cname_from_child(adns_query parent, adns_query child) {
++  adns_answer *pans= parent->answer, *cans= child->answer;
++  size_t n= strlen(cans->cname) + 1;
++
++  pans->cname= adns__alloc_preserved(parent, n);
++  if (!pans->cname) R_NOMEM;
++  memcpy(pans->cname, cans->cname, n);
++  return adns_s_ok;
++}
++
++static void done_addr_type(adns_query qu, adns_rrtype type) {
++  unsigned f= addr_rrtypeflag(type);
++  assert(f); qu->ctx.tinfo.addr.have |= f;
++}
++
++static void icb_addr(adns_query parent, adns_query child) {
++  adns_state ads= parent->ads;
++  adns_answer *pans= parent->answer, *cans= child->answer;
++  struct timeval now;
++  adns_status err;
++  adns_queryflags qf;
++  int id, r;
++
++  propagate_ttl(parent, child);
++
++  if (!(child->flags & adns__qf_addr_cname) &&
++      (parent->flags & adns__qf_addr_answer) &&
++      (!!pans->cname != !!cans->cname ||
++       (pans->cname && strcmp(pans->cname, cans->cname)))) {
++    /* We've detected an inconsistency in CNAME records, and must deploy
++     * countermeasures.
++     */
++
++    if (!pans->cname) {
++      /* The child has a CNAME record, but the parent doesn't.  We must
++       * discard all of the parent's addresses, and substitute the child's.
++       */
++
++      assert(pans->rrsz == cans->rrsz);
++      adns__free_interim(parent, pans->rrs.bytes);
++      adns__transfer_interim(child, parent, cans->rrs.bytes);
++      pans->rrs.bytes= cans->rrs.bytes;
++      pans->nrrs= cans->nrrs;
++      parent->ctx.tinfo.addr.have= 0;
++      done_addr_type(parent, cans->type);
++      err= copy_cname_from_child(parent, child); if (err) goto x_err;
++    }
++
++    /* We've settled on the CNAME (now) associated with the parent, which
++     * already has appropriate address records.  Build a query datagram for
++     * this name so that we can issue child queries for the missing address
++     * families.  The child's vbuf looks handy for this.
++     */
++    err= adns__mkquery(ads, &child->vb, &id, pans->cname,
++		       strlen(pans->cname), &tinfo_addrsub,
++		       adns_r_addr, parent->flags);
++    if (err) goto x_err;
++
++    /* Now cancel the remaining children, and try again with the CNAME we've
++     * settled on.
++     */
++    adns__cancel_children(parent);
++    r= gettimeofday(&now, 0);  if (r) goto x_gtod;
++    qf= adns__qf_addr_cname;
++    if (!(parent->flags & adns_qf_cname_loose)) qf |= adns_qf_cname_forbid;
++    addr_subqueries(parent, now, qf, child->vb.buf, child->vb.used);
++    return;
++  }
++
++  if (cans->cname && !pans->cname) {
++    err= copy_cname_from_child(parent, child);
++    if (err) goto x_err;
++  }
++
++  if ((parent->flags & adns_qf_search) &&
++      !pans->cname && cans->status == adns_s_nxdomain) {
++    /* We're searching a list of suffixes, and the name doesn't exist.  Try
++     * the next one.
++     */
++
++    adns__cancel_children(parent);
++    adns__free_interim(parent, pans->rrs.bytes);
++    pans->rrs.bytes= 0; pans->nrrs= 0;
++    r= gettimeofday(&now, 0);  if (r) goto x_gtod;
++    adns__search_next(ads, parent, now);
++    return;
++  }
++
++  if (cans->status && cans->status != adns_s_nodata)
++    { err= cans->status; goto x_err; }
++
++  assert(pans->rrsz == cans->rrsz);
++  err= append_addrs(parent, pans->rrsz,
++		    &pans->rrs.addr, &pans->nrrs,
++		    cans->rrs.addr, cans->nrrs);
++  if (err) goto x_err;
++  done_addr_type(parent, cans->type);
++
++  if (parent->children.head) LIST_LINK_TAIL(ads->childw, parent);
++  else if (!pans->nrrs) adns__query_fail(parent, adns_s_nodata);
++  else adns__query_done(parent);
++  parent->flags |= adns__qf_addr_answer;
++  return;
++
++x_gtod:
++  /* We have our own error handling, because adns__must_gettimeofday
++   * handles errors by calling adns_globalsystemfailure, which would
++   * reenter the query processing logic. */
++  adns__diag(ads, -1, parent, "gettimeofday failed: %s", strerror(errno));
++  err= adns_s_systemfail;
++  goto x_err;
++
++x_err:
++  adns__query_fail(parent, err);
++}
++
++static void qs_addr(adns_query qu, struct timeval now) {
++  if (!qu->ctx.tinfo.addr.want) {
++    qu->ctx.tinfo.addr.want= addr_rrtypes(qu->ads, qu->answer->type,
++					  qu->flags);
++    qu->ctx.tinfo.addr.have= 0;
++  }
++  addr_subqueries(qu, now, 0, qu->query_dgram, qu->query_dglen);
++}
++
+ /*
+  * _domain      (pap,csp,cs)
+  * _dom_raw     (pa)
+@@ -343,18 +776,18 @@
+ 			      char **domain_r, parsedomain_flags flags) {
+   adns_status st;
+   char *dm;
+-
++  
+   st= adns__parse_domain(pai->qu->ads, pai->serv, pai->qu, &pai->qu->vb, flags,
+ 			 pai->dgram,pai->dglen, cbyte_io, max);
+   if (st) return st;
+   if (!pai->qu->vb.used) return adns_s_invaliddata;
+ 
+-  dm= adns__alloc_interim(pai->qu, (size_t) pai->qu->vb.used+1);
++  dm= adns__alloc_interim(pai->qu, pai->qu->vb.used+1);
+   if (!dm) R_NOMEM;
+ 
+   dm[pai->qu->vb.used]= 0;
+-  memcpy(dm,pai->qu->vb.buf, (size_t) pai->qu->vb.used);
+-
++  memcpy(dm,pai->qu->vb.buf,pai->qu->vb.used);
++  
+   *domain_r= dm;
+   return adns_s_ok;
+ }
+@@ -370,13 +803,14 @@
+   return csp_domain(vb,*domainp);
+ }
+ 
+-static adns_status pa_dom_raw(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_dom_raw(const parseinfo *pai, int cbyte,
++			      int max, void *datap) {
+   char **rrp= datap;
+   adns_status st;
+ 
+   st= pap_domain(pai, &cbyte, max, rrp, pdf_quoteok);
+   if (st) return st;
+-
++  
+   if (cbyte != max) return adns_s_invaliddata;
+   return adns_s_ok;
+ }
+@@ -385,7 +819,8 @@
+  * _host_raw   (pa)
+  */
+ 
+-static adns_status pa_host_raw(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_host_raw(const parseinfo *pai, int cbyte,
++			       int max, void *datap) {
+   char **rrp= datap;
+   adns_status st;
+ 
+@@ -392,65 +827,88 @@
+   st= pap_domain(pai, &cbyte, max, rrp,
+ 		 pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+   if (st) return st;
+-
++  
+   if (cbyte != max) return adns_s_invaliddata;
+   return adns_s_ok;
+ }
+ 
+ /*
+- * _hostaddr   (pap,pa,dip,di,mfp,mf,csp,cs +icb_hostaddr, pap_findaddrs)
++ * _hostaddr   (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs, icb_hostaddr)
+  */
+ 
+ static adns_status pap_findaddrs(const parseinfo *pai, adns_rr_hostaddr *ha,
++				 unsigned *want_io, size_t addrsz,
+ 				 int *cbyte_io, int count, int dmstart) {
+   int rri, naddrs;
+-  int type, class, rdlen, rdstart, ownermatched;
++  unsigned typef, want= *want_io, need= want;
++  int type, class, rdlen, rdend, rdstart, ownermatched;
+   unsigned long ttl;
+   adns_status st;
+-
+-  for (rri=0, naddrs=-1; rri<count; rri++) {
+-    st= adns__findrr_anychk(pai->qu, pai->serv, pai->dgram, pai->dglen, cbyte_io,
++  
++  for (rri=0, naddrs=0; rri<count; rri++) {
++    st= adns__findrr_anychk(pai->qu, pai->serv, pai->dgram,
++			    pai->dglen, cbyte_io,
+ 			    &type, &class, &ttl, &rdlen, &rdstart,
+ 			    pai->dgram, pai->dglen, dmstart, &ownermatched);
+     if (st) return st;
+-    if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) {
+-      if (naddrs>0) break; else continue;
+-    }
+-    if (naddrs == -1) {
+-      naddrs= 0;
+-    }
+-    if (!adns__vbuf_ensure(&pai->qu->vb, (int) ((naddrs+1)*sizeof(adns_rr_addr)))) R_NOMEM;
++    if (!ownermatched || class != DNS_CLASS_IN) continue;
++    typef= addr_rrtypeflag(type);
++    if (!(want & typef)) continue;
++    need &= ~typef;
++    if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*addrsz)) R_NOMEM;
+     adns__update_expires(pai->qu,ttl,pai->now);
+-    st= pa_addr(pai, rdstart,rdstart+rdlen,
+-		pai->qu->vb.buf + naddrs*sizeof(adns_rr_addr));
++    rdend= rdstart + rdlen;
++    st= pap_addr(pai, type, addrsz, &rdstart, rdend,
++		 (adns_rr_addr *)(pai->qu->vb.buf + naddrs*addrsz));
+     if (st) return st;
++    if (rdstart != rdend) return adns_s_invaliddata;
+     naddrs++;
+   }
+-  if (naddrs >= 0) {
+-    ha->addrs= adns__alloc_interim(pai->qu, naddrs*sizeof(adns_rr_addr));
+-    if (!ha->addrs) R_NOMEM;
+-    memcpy(ha->addrs, pai->qu->vb.buf, naddrs*sizeof(adns_rr_addr));
+-    ha->naddrs= naddrs;
++  if (naddrs > 0) {
++    st= append_addrs(pai->qu, addrsz, &ha->addrs, &ha->naddrs,
++		     (const adns_rr_addr *)pai->qu->vb.buf, naddrs);
++    if (st) return st;
+     ha->astatus= adns_s_ok;
+ 
+-    adns__isort(ha->addrs, naddrs, sizeof(adns_rr_addr), pai->qu->vb.buf,
+-		div_addr, pai->ads);
++    if (!need) {
++      adns__isort(ha->addrs, naddrs, addrsz, pai->qu->vb.buf,
++		  div_addr, pai->ads);
++    }
+   }
++  *want_io= need;
+   return adns_s_ok;
+ }
+ 
+ static void icb_hostaddr(adns_query parent, adns_query child) {
+   adns_answer *cans= child->answer;
+-  adns_rr_hostaddr *rrp= child->ctx.info.hostaddr;
++  adns_rr_hostaddr *rrp= child->ctx.pinfo.hostaddr;
+   adns_state ads= parent->ads;
+   adns_status st;
++  size_t addrsz= gsz_addr(0, parent->answer->type);
+ 
+-  st= cans->status;
++  st= cans->status == adns_s_nodata ? adns_s_ok : cans->status;
++  if (st) goto done;
++  propagate_ttl(parent, child);
++
++  assert(addrsz == cans->rrsz);
++  st= append_addrs(parent, addrsz,
++		   &rrp->addrs, &rrp->naddrs,
++		   cans->rrs.addr, cans->nrrs);
++  if (st) goto done;
++  if (!rrp->naddrs) { st= adns_s_nodata; goto done; }
++
++  if (!adns__vbuf_ensure(&parent->vb, addrsz))
++    { st= adns_s_nomemory; goto done; }
++  adns__isort(rrp->addrs, rrp->naddrs, addrsz, parent->vb.buf,
++	      div_addr, ads);
++
++done:
++  if (st) {
++    adns__free_interim(parent, rrp->addrs);
++    rrp->naddrs= (st>0 && st<=adns_s_max_tempfail) ? -1 : 0;
++  }
++
+   rrp->astatus= st;
+-  rrp->naddrs= (st>0 && st<=adns_s_max_tempfail) ? -1 : cans->nrrs;
+-  rrp->addrs= cans->rrs.addr;
+-  adns__transfer_interim(child, parent, rrp->addrs, rrp->naddrs*sizeof(adns_rr_addr));
+-
+   if (parent->children.head) {
+     LIST_LINK_TAIL(ads->childw,parent);
+   } else {
+@@ -466,6 +924,8 @@
+   int id;
+   adns_query nqu;
+   adns_queryflags nflags;
++  unsigned want;
++  size_t addrsz= gsz_addr(0, pai->qu->answer->type);
+ 
+   dmstart= cbyte= *cbyte_io;
+   st= pap_domain(pai, &cbyte, max, &rrp->host,
+@@ -474,18 +934,20 @@
+   *cbyte_io= cbyte;
+ 
+   rrp->astatus= adns_s_ok;
+-  rrp->naddrs= -1;
++  rrp->naddrs= 0;
+   rrp->addrs= 0;
+ 
+   cbyte= pai->nsstart;
+ 
+-  st= pap_findaddrs(pai, rrp, &cbyte, pai->nscount, dmstart);
++  want= addr_rrtypes(pai->ads, pai->qu->answer->type, pai->qu->flags);
++
++  st= pap_findaddrs(pai, rrp, &want, addrsz, &cbyte, pai->nscount, dmstart);
+   if (st) return st;
+-  if (rrp->naddrs != -1) return adns_s_ok;
++  if (!want) return adns_s_ok;
+ 
+-  st= pap_findaddrs(pai, rrp, &cbyte, pai->arcount, dmstart);
++  st= pap_findaddrs(pai, rrp, &want, addrsz, &cbyte, pai->arcount, dmstart);
+   if (st) return st;
+-  if (rrp->naddrs != -1) return adns_s_ok;
++  if (!want) return adns_s_ok;
+ 
+   st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
+ 			    pai->dgram, pai->dglen, dmstart,
+@@ -494,22 +956,21 @@
+ 
+   ctx.ext= 0;
+   ctx.callback= icb_hostaddr;
+-  ctx.info.hostaddr= rrp;
+-
+-  nflags= adns_qf_quoteok_query;
++  ctx.pinfo.hostaddr= rrp;
++  
++  nflags= adns_qf_quoteok_query | (pai->qu->flags & (adns_qf_want_allaf |
++						     adns_qf_ipv6_mapv4));
+   if (!(pai->qu->flags & adns_qf_cname_loose)) nflags |= adns_qf_cname_forbid;
+-
+-  st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+-			    &pai->qu->vb, id, nflags, pai->now, &ctx);
++  
++  st= addr_submit(pai->qu, &nqu, &pai->qu->vb, id, want,
++		  nflags, pai->now, &ctx);
+   if (st) return st;
+ 
+-  nqu->parent= pai->qu;
+-  LIST_LINK_TAIL_PART(pai->qu->children,nqu,siblings.);
+-
+   return adns_s_ok;
+ }
+ 
+-static adns_status pa_hostaddr(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_hostaddr(const parseinfo *pai, int cbyte,
++			       int max, void *datap) {
+   adns_rr_hostaddr *rrp= datap;
+   adns_status st;
+ 
+@@ -520,18 +981,16 @@
+   return adns_s_ok;
+ }
+ 
+-static int dip_hostaddr(adns_state ads, const adns_rr_hostaddr *ap, const adns_rr_hostaddr *bp) {
++static int dip_hostaddr(adns_state ads,
++			const adns_rr_hostaddr *ap, const adns_rr_hostaddr *bp) {
+   if (ap->astatus != bp->astatus) return ap->astatus;
+   if (ap->astatus) return 0;
+ 
+-  assert(ap->addrs[0].addr.sa.sa_family == AF_INET);
+-  assert(bp->addrs[0].addr.sa.sa_family == AF_INET);
+-  return dip_inaddr(ads,
+-		    ap->addrs[0].addr.inet.sin_addr,
+-		    bp->addrs[0].addr.inet.sin_addr);
++  return dip_sockaddr(ads, &ap->addrs[0].addr.sa, &bp->addrs[0].addr.sa);
+ }
+ 
+-static int di_hostaddr(adns_state ads, const void *datap_a, const void *datap_b) {
++static int di_hostaddr(adns_state ads,
++		       const void *datap_a, const void *datap_b) {
+   const adns_rr_hostaddr *ap= datap_a, *bp= datap_b;
+ 
+   return dip_hostaddr(ads, ap,bp);
+@@ -539,10 +998,11 @@
+ 
+ static void mfp_hostaddr(adns_query qu, adns_rr_hostaddr *rrp) {
+   void *tablev;
++  size_t addrsz= gsz_addr(0, qu->answer->type);
+ 
+   adns__makefinal_str(qu,&rrp->host);
+   tablev= rrp->addrs;
+-  adns__makefinal_block(qu, &tablev, rrp->naddrs*sizeof(*rrp->addrs));
++  adns__makefinal_block(qu, &tablev, rrp->naddrs*addrsz);
+   rrp->addrs= tablev;
+ }
+ 
+@@ -570,8 +1030,8 @@
+   CSP_ADDSTR(" ");
+ 
+   errstr= adns_strerror(rrp->astatus);
+-  st= csp_qstring(vb,errstr,(int)strlen(errstr));  if (st) return st;
+-
++  st= csp_qstring(vb,errstr,strlen(errstr));  if (st) return st;
++  
+   if (rrp->naddrs >= 0) {
+     CSP_ADDSTR(" (");
+     for (i=0; i<rrp->naddrs; i++) {
+@@ -595,7 +1055,8 @@
+  * _mx_raw   (pa,di)
+  */
+ 
+-static adns_status pa_mx_raw(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_mx_raw(const parseinfo *pai, int cbyte,
++			     int max, void *datap) {
+   const byte *dgram= pai->dgram;
+   adns_rr_intstr *rrp= datap;
+   adns_status st;
+@@ -607,7 +1068,7 @@
+   st= pap_domain(pai, &cbyte, max, &rrp->str,
+ 		 pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+   if (st) return st;
+-
++  
+   if (cbyte != max) return adns_s_invaliddata;
+   return adns_s_ok;
+ }
+@@ -624,7 +1085,8 @@
+  * _mx   (pa,di)
+  */
+ 
+-static adns_status pa_mx(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_mx(const parseinfo *pai, int cbyte,
++			 int max, void *datap) {
+   const byte *dgram= pai->dgram;
+   adns_rr_inthostaddr *rrp= datap;
+   adns_status st;
+@@ -635,7 +1097,7 @@
+   rrp->i= pref;
+   st= pap_hostaddr(pai, &cbyte, max, &rrp->ha);
+   if (st) return st;
+-
++  
+   if (cbyte != max) return adns_s_invaliddata;
+   return adns_s_ok;
+ }
+@@ -682,12 +1144,29 @@
+ }
+ 
+ /*
+- * _ptr   (pa, +icb_ptr)
++ * _ptr   (ckl,pa +icb_ptr)
+  */
+ 
++static adns_status ckl_ptr(adns_state ads, adns_queryflags flags,
++			   union checklabel_state *cls, qcontext *ctx,
++			   int labnum, const char *dgram,
++			   int labstart, int lablen) {
++  if (lablen) {
++    if (!adns__revparse_label(&cls->ptr, labnum, dgram,labstart,lablen))
++      return adns_s_querydomainwrong;
++  } else {
++    if (!adns__revparse_done(&cls->ptr, dgram, labnum,
++			     &ctx->tinfo.ptr.rev_rrtype,
++			     &ctx->tinfo.ptr.addr))
++      return adns_s_querydomainwrong;
++  }
++  return adns_s_ok;
++}
++
+ static void icb_ptr(adns_query parent, adns_query child) {
+   adns_answer *cans= child->answer;
+-  const adns_rr_addr *queried, *found;
++  const adns_sockaddr *queried;
++  const unsigned char *found;
+   adns_state ads= parent->ads;
+   int i;
+ 
+@@ -699,10 +1178,10 @@
+     return;
+   }
+ 
+-  queried= &parent->ctx.info.ptr_parent_addr;
+-  for (i=0, found=cans->rrs.addr; i<cans->nrrs; i++, found++) {
+-    if (queried->len == found->len &&
+-	!memcmp(&queried->addr,&found->addr,(size_t) queried->len)) {
++  queried= &parent->ctx.tinfo.ptr.addr;
++  for (i=0, found=cans->rrs.bytes; i<cans->nrrs; i++, found+=cans->rrsz) {
++    if (adns__addrs_equal_raw(&queried->sa,
++			  parent->ctx.tinfo.ptr.addr.sa.sa_family,found)) {
+       if (!parent->children.head) {
+ 	adns__query_done(parent);
+ 	return;
+@@ -716,17 +1195,12 @@
+   adns__query_fail(parent,adns_s_inconsistent);
+ }
+ 
+-static adns_status pa_ptr(const parseinfo *pai, int dmstart, int max, void *datap) {
+-  static const char *(expectdomain[])= { DNS_INADDR_ARPA };
+-
++static adns_status pa_ptr(const parseinfo *pai, int dmstart,
++			  int max, void *datap) {
+   char **rrp= datap;
+   adns_status st;
+-  adns_rr_addr *ap;
+-  findlabel_state fls;
+-  char *ep;
+-  byte ipv[4];
+-  char labbuf[4];
+-  int cbyte, i, lablen, labstart, l, id;
++  adns_rrtype rrtype= pai->qu->ctx.tinfo.ptr.rev_rrtype;
++  int cbyte, id;
+   adns_query nqu;
+   qcontext ctx;
+ 
+@@ -736,50 +1210,21 @@
+   if (st) return st;
+   if (cbyte != max) return adns_s_invaliddata;
+ 
+-  ap= &pai->qu->ctx.info.ptr_parent_addr;
+-  if (!ap->len) {
+-    adns__findlabel_start(&fls, pai->ads, -1, pai->qu,
+-			  pai->qu->query_dgram, pai->qu->query_dglen,
+-			  pai->qu->query_dglen, DNS_HDRSIZE, 0);
+-    for (i=0; i<4; i++) {
+-      st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+-      if (lablen<=0 || lablen>3) return adns_s_querydomainwrong;
+-      memcpy(labbuf, pai->qu->query_dgram + labstart, (size_t) lablen);  labbuf[lablen]= 0;
+-      ipv[3-i]= (unsigned char)strtoul(labbuf,&ep,10);  if (*ep) return adns_s_querydomainwrong;
+-      if (lablen>1 && pai->qu->query_dgram[labstart]=='0')
+-	return adns_s_querydomainwrong;
+-    }
+-    for (i=0; i<(int)sizeof(expectdomain)/(int)sizeof(*expectdomain); i++) {
+-      st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+-      l= strlen(expectdomain[i]);
+-      if (lablen != l || memcmp(pai->qu->query_dgram + labstart, expectdomain[i], (size_t)l))
+-	return adns_s_querydomainwrong;
+-    }
+-    st= adns__findlabel_next(&fls,&lablen,0); assert(!st);
+-    if (lablen) return adns_s_querydomainwrong;
+-
+-    ap->len= sizeof(struct sockaddr_in);
+-    memset(&ap->addr,0,sizeof(ap->addr.inet));
+-    ap->addr.inet.sin_family= AF_INET;
+-    ap->addr.inet.sin_addr.s_addr=
+-      htonl((u_long)(ipv[0]<<24) | (ipv[1]<<16) | (ipv[2]<<8) | (ipv[3]));
+-  }
+-
+   st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
+ 			    pai->dgram, pai->dglen, dmstart,
+-			    adns_r_addr, adns_qf_quoteok_query);
++			    rrtype, adns_qf_quoteok_query);
+   if (st) return st;
+ 
+   ctx.ext= 0;
+   ctx.callback= icb_ptr;
+-  memset(&ctx.info,0,(size_t) sizeof(ctx.info));
+-  st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+-			    &pai->qu->vb, id,
++  memset(&ctx.pinfo,0,sizeof(ctx.pinfo));
++  memset(&ctx.tinfo,0,sizeof(ctx.tinfo));
++  st= adns__internal_submit(pai->ads, &nqu, pai->qu,
++			    adns__findtype(rrtype),
++			    rrtype, &pai->qu->vb, id,
+ 			    adns_qf_quoteok_query, pai->now, &ctx);
+   if (st) return st;
+ 
+-  nqu->parent= pai->qu;
+-  LIST_LINK_TAIL_PART(pai->qu->children,nqu,siblings.);
+   return adns_s_ok;
+ }
+ 
+@@ -809,7 +1254,8 @@
+  * _hinfo   (pa)
+  */
+ 
+-static adns_status pa_hinfo(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_hinfo(const parseinfo *pai, int cbyte,
++			    int max, void *datap) {
+   adns_rr_intstrpair *rrp= datap;
+   adns_status st;
+   int i;
+@@ -820,16 +1266,16 @@
+   }
+ 
+   if (cbyte != max) return adns_s_invaliddata;
+-
++  
+   return adns_s_ok;
+ }
+ 
+ /*
+- * _mailbox   (pap,cs)
++ * _mailbox   (pap,cs +pap_mailbox822)
+  */
+ 
+-static adns_status pap_mailbox822(const parseinfo *pai, int *cbyte_io, int max,
+-				  char **mb_r) {
++static adns_status pap_mailbox822(const parseinfo *pai,
++				  int *cbyte_io, int max, char **mb_r) {
+   int lablen, labstart, i, needquote, c, r, neednorm;
+   const unsigned char *p;
+   char *str;
+@@ -860,13 +1306,13 @@
+ 
+   if (needquote || neednorm) {
+     r= adns__vbuf_ensure(vb, lablen+needquote+4); if (!r) R_NOMEM;
+-    adns__vbuf_appendq(vb,(byte*)"\"",1);
++    adns__vbuf_appendq(vb,"\"",1);
+     for (i=0, needquote=0, p= pai->dgram+labstart; i<lablen; i++, p++) {
+       c= *p;
+-      if (c == '"' || c=='\\') adns__vbuf_appendq(vb,(byte*)"\\",1);
++      if (c == '"' || c=='\\') adns__vbuf_appendq(vb,"\\",1);
+       adns__vbuf_appendq(vb,p,1);
+     }
+-    adns__vbuf_appendq(vb,(byte*)"\"",1);
++    adns__vbuf_appendq(vb,"\"",1);
+   } else {
+     r= adns__vbuf_append(vb, pai->dgram+labstart, lablen); if (!r) R_NOMEM;
+   }
+@@ -877,8 +1323,8 @@
+   if (st) return st;
+ 
+  x_ok:
+-  str= adns__alloc_interim(pai->qu, (size_t) vb->used+1); if (!str) R_NOMEM;
+-  memcpy(str,vb->buf,(size_t) vb->used);
++  str= adns__alloc_interim(pai->qu, vb->used+1); if (!str) R_NOMEM;
++  memcpy(str,vb->buf,vb->used);
+   str[vb->used]= 0;
+   *mb_r= str;
+   return adns_s_ok;
+@@ -886,7 +1332,7 @@
+ 
+ static adns_status pap_mailbox(const parseinfo *pai, int *cbyte_io, int max,
+ 			       char **mb_r) {
+-  if (pai->qu->typei->type & adns__qtf_mail822) {
++  if (pai->qu->typei->typekey & adns__qtf_mail822) {
+     return pap_mailbox822(pai, cbyte_io, max, mb_r);
+   } else {
+     return pap_domain(pai, cbyte_io, max, mb_r, pdf_quoteok);
+@@ -901,7 +1347,8 @@
+  * _rp   (pa,cs)
+  */
+ 
+-static adns_status pa_rp(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_rp(const parseinfo *pai, int cbyte,
++			 int max, void *datap) {
+   adns_rr_strpair *rrp= datap;
+   adns_status st;
+ 
+@@ -924,13 +1371,14 @@
+   st= csp_domain(vb,rrp->array[1]);  if (st) return st;
+ 
+   return adns_s_ok;
+-}
++}  
+ 
+ /*
+  * _soa   (pa,mf,cs)
+  */
+ 
+-static adns_status pa_soa(const parseinfo *pai, int cbyte, int max, void *datap) {
++static adns_status pa_soa(const parseinfo *pai, int cbyte,
++			  int max, void *datap) {
+   adns_rr_soa *rrp= datap;
+   const byte *dgram= pai->dgram;
+   adns_status st;
+@@ -944,7 +1392,7 @@
+   if (st) return st;
+ 
+   if (cbyte+20 != max) return adns_s_invaliddata;
+-
++  
+   for (i=0; i<5; i++) {
+     GET_W(cbyte,msw);
+     GET_W(cbyte,lsw);
+@@ -966,7 +1414,7 @@
+   char buf[20];
+   int i;
+   adns_status st;
+-
++  
+   st= csp_domain(vb,rrp->mname);  if (st) return st;
+   CSP_ADDSTR(" ");
+   st= csp_mailbox(vb,rrp->rname);  if (st) return st;
+@@ -980,6 +1428,220 @@
+ }
+ 
+ /*
++ * _srv*  (ckl,(pap),pa*2,mf*2,di,(csp),cs*2,postsort)
++ */
++
++static adns_status ckl_srv(adns_state ads, adns_queryflags flags,
++			   union checklabel_state *cls, qcontext *ctx,
++			   int labnum, const char *dgram,
++			   int labstart, int lablen) {
++  const char *label = dgram+labstart;
++  if (labnum < 2) {
++    if (flags & adns_qf_quoteok_query) return adns_s_ok;
++    if (!lablen || label[0] != '_') return adns_s_querydomaininvalid;
++    return adns_s_ok;
++  }
++  return adns__ckl_hostname(ads,flags, cls,ctx, labnum, dgram,labstart,lablen);
++}
++
++static adns_status pap_srv_begin(const parseinfo *pai, int *cbyte_io, int max,
++				 adns_rr_srvha *rrp
++				   /* might be adns_rr_srvraw* */) {
++  const byte *dgram= pai->dgram;
++  int ti, cbyte;
++
++  cbyte= *cbyte_io;
++  if ((*cbyte_io += 6) > max) return adns_s_invaliddata;
++  
++  rrp->priority= GET_W(cbyte, ti);
++  rrp->weight=   GET_W(cbyte, ti);
++  rrp->port=     GET_W(cbyte, ti);
++  return adns_s_ok;
++}
++
++static adns_status pa_srvraw(const parseinfo *pai, int cbyte,
++			     int max, void *datap) {
++  adns_rr_srvraw *rrp= datap;
++  adns_status st;
++
++  st= pap_srv_begin(pai,&cbyte,max,datap);
++  if (st) return st;
++  
++  st= pap_domain(pai, &cbyte, max, &rrp->host,
++		 pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
++  if (st) return st;
++  
++  if (cbyte != max) return adns_s_invaliddata;
++  return adns_s_ok;
++}
++
++static adns_status pa_srvha(const parseinfo *pai, int cbyte,
++			    int max, void *datap) {
++  adns_rr_srvha *rrp= datap;
++  adns_status st;
++
++  st= pap_srv_begin(pai,&cbyte,max,datap);       if (st) return st;
++  st= pap_hostaddr(pai, &cbyte, max, &rrp->ha);  if (st) return st;
++  if (cbyte != max) return adns_s_invaliddata;
++  return adns_s_ok;
++}
++
++static void mf_srvraw(adns_query qu, void *datap) {
++  adns_rr_srvraw *rrp= datap;
++  adns__makefinal_str(qu, &rrp->host);
++}
++
++static void mf_srvha(adns_query qu, void *datap) {
++  adns_rr_srvha *rrp= datap;
++  mfp_hostaddr(qu,&rrp->ha);
++}
++
++static int di_srv(adns_state ads, const void *datap_a, const void *datap_b) {
++  const adns_rr_srvraw *ap= datap_a, *bp= datap_b;
++    /* might be const adns_rr_svhostaddr* */
++
++  if (ap->priority < bp->priority) return 0;
++  if (ap->priority > bp->priority) return 1;
++  return 0;
++}
++
++static adns_status csp_srv_begin(vbuf *vb, const adns_rr_srvha *rrp
++				   /* might be adns_rr_srvraw* */) {
++  char buf[30];
++  sprintf(buf,"%u %u %u ", rrp->priority, rrp->weight, rrp->port);
++  CSP_ADDSTR(buf);
++  return adns_s_ok;
++}
++
++static adns_status cs_srvraw(vbuf *vb, const void *datap) {
++  const adns_rr_srvraw *rrp= datap;
++  adns_status st;
++  
++  st= csp_srv_begin(vb,(const void*)rrp);  if (st) return st;
++  return csp_domain(vb,rrp->host);
++}
++
++static adns_status cs_srvha(vbuf *vb, const void *datap) {
++  const adns_rr_srvha *rrp= datap;
++  adns_status st;
++
++  st= csp_srv_begin(vb,(const void*)datap);  if (st) return st;
++  return csp_hostaddr(vb,&rrp->ha);
++}
++
++static void postsort_srv(adns_state ads, void *array, int nrrs,int rrsz,
++			 const struct typeinfo *typei) {
++  /* we treat everything in the array as if it were an adns_rr_srvha
++   * even though the array might be of adns_rr_srvraw.  That's OK
++   * because they have the same prefix, which is all we access.
++   * We use rrsz, too, rather than naive array indexing, of course.
++   */
++  char *workbegin, *workend, *search, *arrayend;
++  const adns_rr_srvha *rr;
++  union { adns_rr_srvha ha; adns_rr_srvraw raw; } rrtmp;
++  int cpriority, totalweight, runtotal;
++  long randval;
++
++  assert(rrsz <= sizeof(rrtmp));
++  for (workbegin= array, arrayend= workbegin + rrsz * nrrs;
++       workbegin < arrayend;
++       workbegin= workend) {
++    cpriority= (rr=(void*)workbegin)->priority;
++    
++    for (workend= workbegin, totalweight= 0;
++	 workend < arrayend && (rr=(void*)workend)->priority == cpriority;
++	 workend += rrsz) {
++      totalweight += rr->weight;
++    }
++
++    /* Now workbegin..(workend-1) incl. are exactly all of the RRs of
++     * cpriority.  From now on, workbegin points to the `remaining'
++     * records: we select one record at a time (RFC2782 `Usage rules'
++     * and `Format of the SRV RR' subsection `Weight') to place at
++     * workbegin (swapping with the one that was there, and then
++     * advance workbegin. */
++    for (;
++	 workbegin + rrsz < workend; /* don't bother if just one */
++	 workbegin += rrsz) {
++      
++      randval= nrand48(ads->rand48xsubi);
++      randval %= (totalweight + 1);
++        /* makes it into 0..totalweight inclusive; with 2^10 RRs,
++	 * totalweight must be <= 2^26 so probability nonuniformity is
++	 * no worse than 1 in 2^(31-26) ie 1 in 2^5, ie
++	 *  abs(log(P_intended(RR_i) / P_actual(RR_i)) <= log(2^-5).
++	 */
++
++      for (search=workbegin, runtotal=0;
++	   (runtotal += (rr=(void*)search)->weight) < randval;
++	   search += rrsz);
++      assert(search < arrayend);
++      totalweight -= rr->weight;
++      if (search != workbegin) {
++	memcpy(&rrtmp, workbegin, rrsz);
++	memcpy(workbegin, search, rrsz);
++	memcpy(search, &rrtmp, rrsz);
++      }
++    }
++  }
++  /* tests:
++   *  dig -t srv _srv._tcp.test.iwj.relativity.greenend.org.uk.
++   *   ./adnshost_s -t srv- _sip._udp.voip.net.cam.ac.uk.
++   *   ./adnshost_s -t srv- _jabber._tcp.jabber.org
++   */
++}
++
++/*
++ * _byteblock   (mf)
++ */
++
++static void mf_byteblock(adns_query qu, void *datap) {
++  adns_rr_byteblock *rrp= datap;
++  void *bytes= rrp->data;
++  adns__makefinal_block(qu,&bytes,rrp->len);
++  rrp->data= bytes;
++}
++
++/*
++ * _opaque   (pa,cs)
++ */
++
++static adns_status pa_opaque(const parseinfo *pai, int cbyte,
++			     int max, void *datap) {
++  adns_rr_byteblock *rrp= datap;
++
++  rrp->len= max - cbyte;
++  rrp->data= adns__alloc_interim(pai->qu, rrp->len);
++  if (!rrp->data) R_NOMEM;
++  memcpy(rrp->data, pai->dgram + cbyte, rrp->len);
++  return adns_s_ok;
++}
++
++static adns_status cs_opaque(vbuf *vb, const void *datap) {
++  const adns_rr_byteblock *rrp= datap;
++  char buf[10];
++  int l;
++  unsigned char *p;
++
++  sprintf(buf,"\\# %d",rrp->len);
++  CSP_ADDSTR(buf);
++  
++  for (l= rrp->len, p= rrp->data;
++       l>=4;
++       l -= 4, p += 4) {
++    sprintf(buf," %02x%02x%02x%02x",p[0],p[1],p[2],p[3]);
++    CSP_ADDSTR(buf);
++  }
++  for (;
++       l>0;
++       l--, p++) {
++    sprintf(buf," %02x",*p);
++    CSP_ADDSTR(buf);
++  }
++  return adns_s_ok;
++}
++  
++/*
+  * _flat   (mf)
+  */
+ 
+@@ -991,46 +1653,90 @@
+ 
+ #define TYPESZ_M(member)           (sizeof(*((adns_answer*)0)->rrs.member))
+ 
+-#define DEEP_MEMB(memb) TYPESZ_M(memb), mf_##memb, cs_##memb
+-#define FLAT_MEMB(memb) TYPESZ_M(memb), mf_flat, cs_##memb
++#define DEEP_TYPE(code,rrt,fmt,memb,parser,comparer,/*printer*/...)	\
++ { adns_r_##code&adns_rrt_reprmask, rrt,fmt,TYPESZ_M(memb), mf_##memb,	\
++     GLUE(cs_, CAR(__VA_ARGS__)),pa_##parser,di_##comparer,		\
++     adns__ckl_hostname, 0, adns__getrrsz_default, adns__query_send,	\
++     CDR(__VA_ARGS__) }
++#define FLAT_TYPE(code,rrt,fmt,memb,parser,comparer,/*printer*/...)	\
++ { adns_r_##code&adns_rrt_reprmask, rrt,fmt,TYPESZ_M(memb), mf_flat,	\
++     GLUE(cs_, CAR(__VA_ARGS__)),pa_##parser,di_##comparer,		\
++     adns__ckl_hostname, 0, adns__getrrsz_default, adns__query_send,	\
++     CDR(__VA_ARGS__) }
+ 
+-#define DEEP_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \
+- { adns_r_##code, rrt, fmt, TYPESZ_M(memb), mf_##memb, printer, parser, comparer }
+-#define FLAT_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \
+- { adns_r_##code, rrt, fmt, TYPESZ_M(memb), mf_flat, printer, parser, comparer }
++#define di_0 0
+ 
+ static const typeinfo typeinfos[] = {
+ /* Must be in ascending order of rrtype ! */
+-/* mem-mgmt code  rrt     fmt     member      parser      comparer    printer       */
++/* mem-mgmt code  rrt     fmt   member   parser      comparer  printer */
+ 
+-FLAT_TYPE(a,      "A",     0,     inaddr,     pa_inaddr,  di_inaddr,  cs_inaddr     ),
+-DEEP_TYPE(ns_raw, "NS",   "raw",  str,        pa_host_raw,0,          cs_domain     ),
+-DEEP_TYPE(cname,  "CNAME", 0,     str,        pa_dom_raw, 0,          cs_domain     ),
+-DEEP_TYPE(soa_raw,"SOA",  "raw",  soa,        pa_soa,     0,          cs_soa        ),
+-DEEP_TYPE(ptr_raw,"PTR",  "raw",  str,        pa_host_raw,0,          cs_domain     ),
+-DEEP_TYPE(hinfo,  "HINFO", 0,     intstrpair, pa_hinfo,   0,          cs_hinfo      ),
+-DEEP_TYPE(mx_raw, "MX",   "raw",  intstr,     pa_mx_raw,  di_mx_raw,  cs_inthost    ),
+-DEEP_TYPE(txt,    "TXT",   0,     manyistr,   pa_txt,     0,          cs_txt        ),
+-DEEP_TYPE(rp_raw, "RP",   "raw",  strpair,    pa_rp,      0,          cs_rp         ),
++FLAT_TYPE(a,      "A",     0,   inaddr,    inaddr,  inaddr,inaddr          ),
++DEEP_TYPE(ns_raw, "NS",   "raw",str,       host_raw,0,     domain          ),
++DEEP_TYPE(cname,  "CNAME", 0,   str,       dom_raw, 0,     domain          ),
++DEEP_TYPE(soa_raw,"SOA",  "raw",soa,       soa,     0,     soa             ),
++DEEP_TYPE(ptr_raw,"PTR",  "raw",str,       host_raw,0,     domain          ),
++DEEP_TYPE(hinfo,  "HINFO", 0,   intstrpair,hinfo,   0,     hinfo           ),
++DEEP_TYPE(mx_raw, "MX",   "raw",intstr,    mx_raw,  mx_raw,inthost         ),
++DEEP_TYPE(txt,    "TXT",   0,   manyistr,  txt,     0,     txt             ),
++DEEP_TYPE(rp_raw, "RP",   "raw",strpair,   rp,      0,     rp              ),
++#ifdef WITH_IPV6
++FLAT_TYPE(aaaa,	  "AAAA",  0,   in6addr,   in6addr, in6addr,in6addr        ),
++DEEP_TYPE(srv_raw,"SRV",  "raw",srvraw ,   srvraw,  srv,   srvraw,
++			      .checklabel= ckl_srv, .postsort= postsort_srv),
+ 
+-FLAT_TYPE(addr,   "A",  "addr",   addr,       pa_addr,    di_addr,    cs_addr       ),
+-DEEP_TYPE(ns,     "NS", "+addr",  hostaddr,   pa_hostaddr,di_hostaddr,cs_hostaddr   ),
+-DEEP_TYPE(ptr,    "PTR","checked",str,        pa_ptr,     0,          cs_domain     ),
+-DEEP_TYPE(mx,     "MX", "+addr",  inthostaddr,pa_mx,      di_mx,      cs_inthostaddr),
++FLAT_TYPE(addr,   "A",  "addr", addr,      addr,    addr,  addr,
++				   .getrrsz= gsz_addr, .query_send= qs_addr),
++#else
++DEEP_TYPE(srv_raw,"SRV",  "raw",srvraw ,   srvraw,  srv,   srvraw          ),
+ 
+-DEEP_TYPE(soa,    "SOA","822",    soa,        pa_soa,     0,          cs_soa        ),
+-DEEP_TYPE(rp,     "RP", "822",    strpair,    pa_rp,      0,          cs_rp         ),
++FLAT_TYPE(addr,   "A",  "addr", addr,      addr,    addr,  addr            ),
++#endif
++DEEP_TYPE(ns,     "NS", "+addr",hostaddr,  hostaddr,hostaddr,hostaddr      ),
++#ifdef WITH_IPV6
++DEEP_TYPE(ptr,    "PTR","checked",str,     ptr,     0,     domain,
++						       .checklabel= ckl_ptr),
++#else
++DEEP_TYPE(ptr,    "PTR","checked",str,     ptr,     0,     domain          ),
++#endif
++DEEP_TYPE(mx,     "MX", "+addr",inthostaddr,mx,     mx,    inthostaddr     ),
++#ifdef WITH_IPV6
++DEEP_TYPE(srv,    "SRV","+addr",srvha,     srvha,   srv,   srvha,
++			      .checklabel= ckl_srv, .postsort= postsort_srv),
++#else
++DEEP_TYPE(srv,    "SRV","+addr",srvha,     srvha,   srv,   srvha           ),
++#endif
++
++DEEP_TYPE(soa,    "SOA","822",  soa,       soa,     0,     soa             ),
++DEEP_TYPE(rp,     "RP", "822",  strpair,   rp,      0,     rp              ),
+ };
+ 
++static const typeinfo tinfo_addrsub =
++#ifdef WITH_IPV6
++FLAT_TYPE(none,	  "<addr>","sub",addr,	   addr,    0,	   addr,
++							 .getrrsz= gsz_addr);
++#else
++FLAT_TYPE(none,	  "<addr>","sub",addr,	   addr,    0,	   addr            );
++#endif
++
++static const typeinfo typeinfo_unknown=
++DEEP_TYPE(unknown,0, "unknown",byteblock,opaque,  0,     opaque            );
++
+ const typeinfo *adns__findtype(adns_rrtype type) {
+   const typeinfo *begin, *end, *mid;
+ 
++  if (type & ~(adns_rrtype)0x63ffffff)
++    /* 0x60000000 is reserved for `harmless' future expansion */
++    return 0;
++
++  if (type & adns_r_unknown) return &typeinfo_unknown;
++  type &= adns_rrt_reprmask;
++
+   begin= typeinfos;  end= typeinfos+(sizeof(typeinfos)/sizeof(typeinfo));
+ 
+   while (begin < end) {
+     mid= begin + ((end-begin)>>1);
+-    if (mid->type == type) return mid;
+-    if (type > mid->type) begin= mid+1;
++    if (mid->typekey == type) return mid;
++    if (type > mid->typekey) begin= mid+1;
+     else end= mid;
+   }
+   return 0;
diff -Naur src_150/transmit.c src/transmit.c
--- src_150/transmit.c	2014-10-20 02:07:03 +0300
+++ src/transmit.c	2016-05-25 20:31:06 +0300
@@ -27,8 +27,12 @@
 
 #include <errno.h>
 
-#include <sys/types.h>
-#include <sys/uio.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
+# include <sys/types.h>
+# include <sys/uio.h>
+#endif
 
 #include "internal.h"
 #include "tvarith.h"
diff -Naur src_150/types.c src/types.c
--- src_150/types.c	2014-10-26 13:42:43 +0200
+++ src/types.c	2016-05-27 17:07:54 +0300
@@ -24,13 +24,16 @@
  *  along with this program; if not, write to the Free Software Foundation.
  */
 
-#include <stddef.h>
-#include <stdlib.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#ifdef ADNS_JGAA_WIN32
+# include "adns_win32.h"
+#else
+# include <stddef.h>
+# include <stdlib.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
 
 #include "internal.h"
 
@@ -266,7 +269,7 @@
   struct in_addr a4;
   int i;
   int v6mappedp= 0;
-
+#ifdef WITH_IPV6
   if (af == AF_INET6) {
     const struct in6_addr *a6= ad;
     if (IN6_IS_ADDR_V4MAPPED(a6)) {
@@ -277,7 +280,7 @@
       v6mappedp= 1;
     }
   }
-
+#endif
   for (i=0, slp=ads->sortlist;
        i<ads->nsortlist &&
 	 !adns__addr_matches(af,ad, &slp->base,&slp->mask) &&
@@ -324,6 +327,7 @@
  * _in6addr   (pa,di,cs)
  */
 
+#ifdef WITH_IPV6
 static adns_status pa_in6addr(const parseinfo *pai, int cbyte,
 			     int max, void *datap) {
   struct in6_addr *storeto= datap;
@@ -341,7 +345,7 @@
 static adns_status cs_in6addr(vbuf *vb, const void *datap) {
   return csp_genaddr(vb,AF_INET6,datap);
 }
-
+#endif
 /*
  * _addr   (pap,pa,di,div,csp,cs,gsz,qs
  *		+search_sortlist_sa, dip_sockaddr, addr_rrtypes,
@@ -404,18 +408,22 @@
 			    int *cbyte_io, int cbyte_max, adns_rr_addr *out) {
   int in_addrlen;
   int out_af, out_salen;
+#ifdef WITH_IPV6
   struct in6_addr v6map;
-
+#endif
   const void *use_addr= pai->dgram + *cbyte_io;
 
   switch (in_rrty) {
   case adns_r_a:    in_addrlen= 4;  out_af= AF_INET;  break;
+#ifdef WITH_IPV6
   case adns_r_aaaa: in_addrlen= 16; out_af= AF_INET6; break;
+#endif
   default: abort();
   }
 
   if ((*cbyte_io + in_addrlen) != cbyte_max) return adns_s_invaliddata;
 
+#ifdef WITH_IPV6
   if (out_af==AF_INET &&
       (pai->qu->flags & adns_qf_ipv6_mapv4) &&
       (pai->qu->answer->type & adns__qtf_bigaddr)) {
@@ -425,10 +433,13 @@
     use_addr= v6map.s6_addr;
     out_af= AF_INET6;
   }
+#endif
 
   switch (out_af) {
   case AF_INET:  out_salen= sizeof(out->addr.inet);  break;
+#ifdef WITH_IPV6
   case AF_INET6: out_salen= sizeof(out->addr.inet6); break;
+#endif
   default: abort();
   }
 
@@ -483,11 +494,13 @@
   switch (rrp->addr.inet.sin_family) {
   case AF_INET:
     CSP_ADDSTR("INET ");
+#ifdef WITH_IPV6
     goto a2t;
   case AF_INET6:
     CSP_ADDSTR("INET6 ");
     goto a2t;
   a2t:
+#endif
     err= adns_addr2text(&rrp->addr.sa,0, buf,&len, 0); assert(!err);
     CSP_ADDSTR(buf);
     break;
@@ -1666,26 +1679,44 @@
 DEEP_TYPE(mx_raw, "MX",   "raw",intstr,    mx_raw,  mx_raw,inthost         ),
 DEEP_TYPE(txt,    "TXT",   0,   manyistr,  txt,     0,     txt             ),
 DEEP_TYPE(rp_raw, "RP",   "raw",strpair,   rp,      0,     rp              ),
+#ifdef WITH_IPV6
 FLAT_TYPE(aaaa,	  "AAAA",  0,   in6addr,   in6addr, in6addr,in6addr        ),
 DEEP_TYPE(srv_raw,"SRV",  "raw",srvraw ,   srvraw,  srv,   srvraw,
 			      .checklabel= ckl_srv, .postsort= postsort_srv),
 
 FLAT_TYPE(addr,   "A",  "addr", addr,      addr,    addr,  addr,
 				   .getrrsz= gsz_addr, .query_send= qs_addr),
+#else
+DEEP_TYPE(srv_raw,"SRV",  "raw",srvraw ,   srvraw,  srv,   srvraw          ),
+
+FLAT_TYPE(addr,   "A",  "addr", addr,      addr,    addr,  addr            ),
+#endif
 DEEP_TYPE(ns,     "NS", "+addr",hostaddr,  hostaddr,hostaddr,hostaddr      ),
+#ifdef WITH_IPV6
 DEEP_TYPE(ptr,    "PTR","checked",str,     ptr,     0,     domain,
 						       .checklabel= ckl_ptr),
-DEEP_TYPE(mx,     "MX", "+addr",inthostaddr,mx,     mx,    inthostaddr,    ),
+#else
+DEEP_TYPE(ptr,    "PTR","checked",str,     ptr,     0,     domain          ),
+#endif
+DEEP_TYPE(mx,     "MX", "+addr",inthostaddr,mx,     mx,    inthostaddr     ),
+#ifdef WITH_IPV6
 DEEP_TYPE(srv,    "SRV","+addr",srvha,     srvha,   srv,   srvha,
 			      .checklabel= ckl_srv, .postsort= postsort_srv),
+#else
+DEEP_TYPE(srv,    "SRV","+addr",srvha,     srvha,   srv,   srvha           ),
+#endif
 
 DEEP_TYPE(soa,    "SOA","822",  soa,       soa,     0,     soa             ),
 DEEP_TYPE(rp,     "RP", "822",  strpair,   rp,      0,     rp              ),
 };
 
 static const typeinfo tinfo_addrsub =
+#ifdef WITH_IPV6
 FLAT_TYPE(none,	  "<addr>","sub",addr,	   addr,    0,	   addr,
 							 .getrrsz= gsz_addr);
+#else
+FLAT_TYPE(none,	  "<addr>","sub",addr,	   addr,    0,	   addr            );
+#endif
 
 static const typeinfo typeinfo_unknown=
 DEEP_TYPE(unknown,0, "unknown",byteblock,opaque,  0,     opaque            );
