about summary refs log tree commit diff
path: root/resolv
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2008-05-10 23:27:39 +0000
committerUlrich Drepper <drepper@redhat.com>2008-05-10 23:27:39 +0000
commit1eb946b93509b94db2bddce741f2f3b483418a6d (patch)
tree054e49d0b1be9539a0d778c4b1a6252ac4d68523 /resolv
parenta82e8bb8d0b51d30cb0d8d24f0ff0f72066cf2ce (diff)
downloadglibc-1eb946b93509b94db2bddce741f2f3b483418a6d.tar.gz
glibc-1eb946b93509b94db2bddce741f2f3b483418a6d.tar.xz
glibc-1eb946b93509b94db2bddce741f2f3b483418a6d.zip
* include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend
	prototypes.
	* include/arpa/nameser_compat.h: Define T_UNSPEC.
	* nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r.
	(libnss_nisplus): Export _nss_nisplus_gethostbyname4_r.
	* nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle
	af==AF_UNSPEC.
	(_nss_nis_gethostbyname4_r): New function.
	* nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent):
	Change to also handle af==AF_UNSPEC.
	(get_tablename): New function.  Use it to avoid duplication.
	(_nss_nisplus_gethostbyname4_r): New function.
	* nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is
	available.
	* nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r.
	* nss/nss.h: Define struct gaih_addrtuple.
	* nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle
	af==AF_UNSPEC.
	(_nss_files_gethostbyname4_r): New function.
	* resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r.
	* resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery
	calls.
	* resolv/res_query.c (__libc_res_nquery): Take two additional
	parameters for second answer buffer.  Handle type=T_UNSPEC to mean
	look up IPv4 and IPv6.
	Change all callers.
	* resolv/res_send.c (__libc_res_nsend): Take five aditional parameters
	for an additional query and answer buffer.  Pass to send_vc and
	send_dg.
	(send_vc): Send possibly two requests and receive two answers.
	(send_dg): Likewise.
	* resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and
	__libc_res_nquery.
	(_nss_dns_gethostbyname4_r): New function.
	(gaih_getanswer_slice): Likewise.
	(gaih_getanswer): Likewise.
	* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust
	__libc_res_nquery call.
	* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise.
	(_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call.
	* sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is
	available.
Diffstat (limited to 'resolv')
-rw-r--r--resolv/Versions1
-rw-r--r--resolv/gethnamaddr.c6
-rw-r--r--resolv/nss_dns/dns-canon.c4
-rw-r--r--resolv/nss_dns/dns-host.c337
-rw-r--r--resolv/nss_dns/dns-network.c6
-rw-r--r--resolv/res_query.c149
-rw-r--r--resolv/res_send.c323
7 files changed, 693 insertions, 133 deletions
diff --git a/resolv/Versions b/resolv/Versions
index fc2111a1cb..355d95d606 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -89,6 +89,7 @@ libnss_dns {
     _nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r;
     _nss_dns_getnetbyname_r; _nss_dns_getcanonname_r;
     _nss_dns_gethostbyaddr2_r;
+    _nss_dns_gethostbyname4_r;
   }
 }
 
diff --git a/resolv/gethnamaddr.c b/resolv/gethnamaddr.c
index 7be23158d0..2a9bd0b3c2 100644
--- a/resolv/gethnamaddr.c
+++ b/resolv/gethnamaddr.c
@@ -621,7 +621,7 @@ gethostbyname2(name, af)
 	buf.buf = origbuf = (querybuf *) alloca (1024);
 
 	if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024,
-				    &buf.ptr)) < 0) {
+				    &buf.ptr, NULL, NULL)) < 0) {
 		if (buf.buf != origbuf)
 			free (buf.buf);
 		Dprintf("res_nsearch failed (%d)\n", n);
@@ -716,12 +716,12 @@ gethostbyaddr(addr, len, af)
 	buf.buf = orig_buf = (querybuf *) alloca (1024);
 
 	n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024,
-			      &buf.ptr);
+			      &buf.ptr, NULL, NULL);
 	if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) {
 		strcpy(qp, "ip6.int");
 		n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf,
 				      buf.buf != orig_buf ? MAXPACKET : 1024,
-				      &buf.ptr);
+				      &buf.ptr, NULL, NULL);
 	}
 	if (n < 0) {
 		if (buf.buf != orig_buf)
diff --git a/resolv/nss_dns/dns-canon.c b/resolv/nss_dns/dns-canon.c
index fca6cd8997..47949b862f 100644
--- a/resolv/nss_dns/dns-canon.c
+++ b/resolv/nss_dns/dns-canon.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+/* Copyright (C) 2004, 2006, 2008 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -61,7 +61,7 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
   for (int i = 0; i < nqtypes; ++i)
     {
       int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
-				 buf, sizeof (buf), &ansp.ptr);
+				 buf, sizeof (buf), &ansp.ptr, NULL, NULL);
       if (r > 0)
 	{
 	  /* We need to decode the response.  Just one question record.
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index dfdf9c5dcb..c52f9f7f84 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -71,6 +71,7 @@
  * --Copyright--
  */
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <netdb.h>
@@ -127,6 +128,14 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen,
 				    size_t buflen, int *errnop, int *h_errnop,
 				    int map, int32_t *ttlp, char **canonp);
 
+static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
+				       const querybuf *answer2, int anslen2,
+				       const char *qname,
+				       struct gaih_addrtuple **pat,
+				       char *buffer, size_t buflen,
+				       int *errnop, int *h_errnop,
+				       int32_t *ttlp);
+
 extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af,
 						  struct hostent *result,
 						  char *buffer, size_t buflen,
@@ -186,11 +195,11 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
   host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
 
   n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf,
-			  1024, &host_buffer.ptr);
+			  1024, &host_buffer.ptr, NULL, NULL);
   if (n < 0)
     {
-      enum nss_status status = (errno == ECONNREFUSED
-				? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
+      status = (errno == ECONNREFUSED
+		? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
       *h_errnop = h_errno;
       if (h_errno == TRY_AGAIN)
 	*errnop = EAGAIN;
@@ -203,7 +212,8 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
       if (af == AF_INET6 && (_res.options & RES_USE_INET6))
 	n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf,
 				host_buffer.buf != orig_host_buffer
-				? MAXPACKET : 1024, &host_buffer.ptr);
+				? MAXPACKET : 1024, &host_buffer.ptr,
+				NULL, NULL);
 
       if (n < 0)
 	{
@@ -255,6 +265,70 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
 }
 
 
+enum nss_status
+_nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+			   char *buffer, size_t buflen, int *errnop,
+			   int *herrnop, int32_t *ttlp)
+{
+  if (__res_maybe_init (&_res, 0) == -1)
+    return NSS_STATUS_UNAVAIL;
+
+  char tmp[NS_MAXDNAME];
+
+  /*
+   * if there aren't any dots, it could be a user-level alias.
+   * this is also done in res_query() since we are not the only
+   * function that looks up host names.
+   */
+  if (strchr (name, '.') == NULL)
+    {
+      const char *cp = res_hostalias (&_res, name, tmp, sizeof (tmp));
+      if (cp != NULL)
+	name = cp;
+    }
+
+  union
+  {
+    querybuf *buf;
+    u_char *ptr;
+  } host_buffer;
+  querybuf *orig_host_buffer;
+  host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048);
+  u_char *ans2p = NULL;
+  int nans2p = 0;
+
+  int olderr = errno;
+  enum nss_status status;
+  int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC,
+			      host_buffer.buf->buf, 2048, &host_buffer.ptr,
+			      &ans2p, &nans2p);
+  if (n < 0)
+    {
+      status = (errno == ECONNREFUSED
+		? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
+      *herrnop = h_errno;
+      if (h_errno == TRY_AGAIN)
+	*errnop = EAGAIN;
+      else
+	__set_errno (olderr);
+
+      if (host_buffer.buf != orig_host_buffer)
+	free (host_buffer.buf);
+
+      return status;
+    }
+
+  status = gaih_getanswer(host_buffer.buf, n, (const querybuf *) ans2p,
+			  nans2p, name, pat, buffer, buflen,
+			  errnop, herrnop, ttlp);
+
+  if (host_buffer.buf != orig_host_buffer)
+    free (host_buffer.buf);
+
+  return status;
+}
+
+
 extern enum nss_status _nss_dns_gethostbyaddr2_r (const void *addr,
 						  socklen_t len, int af,
 						  struct hostent *result,
@@ -342,7 +416,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
 	    qp += sprintf (qp, "%02hhx", uaddr[n]);
 	  strcpy (qp, "].ip6.arpa");
 	  n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR,
-				 host_buffer.buf->buf, 1024, &host_buffer.ptr);
+				 host_buffer.buf->buf, 1024, &host_buffer.ptr,
+				 NULL, NULL);
 	  if (n >= 0)
 	    goto got_it_already;
 	}
@@ -363,13 +438,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
     }
 
   n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
-			 1024, &host_buffer.ptr);
+			 1024, &host_buffer.ptr, NULL, NULL);
   if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0)
     {
       strcpy (qp, "ip6.int");
       n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
 			     host_buffer.buf != orig_host_buffer
-			     ? MAXPACKET : 1024, &host_buffer.ptr);
+			     ? MAXPACKET : 1024, &host_buffer.ptr,
+			     NULL, NULL);
     }
   if (n < 0)
     {
@@ -555,7 +631,8 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
   if (n > 0 && bp[0] == '.')
     bp[0] = '\0';
 
-  if (n < 0 || (*name_ok) (bp) == 0)
+  if (__builtin_expect (n < 0 || ((*name_ok) (bp) == 0 && (errno = EBADMSG)),
+			0))
     {
       *errnop = errno;
       *h_errnop = NO_RECOVERY;
@@ -629,7 +706,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
 	  continue;			/* XXX - had_error++ ? */
 	}
 
-      if ((qtype ==T_A || qtype == T_AAAA) && type == T_CNAME)
+      if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME)
 	{
 	  if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1])
 	    continue;
@@ -857,3 +934,245 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
   return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
 	   ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
 }
+
+
+static enum nss_status
+gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
+		      struct gaih_addrtuple ***patp,
+		      char **bufferp, size_t *buflenp,
+		      int *errnop, int *h_errnop, int32_t *ttlp, int *firstp)
+{
+  char *buffer = *bufferp;
+  size_t buflen = *buflenp;
+
+  struct gaih_addrtuple **pat = *patp;
+  const HEADER *hp = &answer->hdr;
+  int ancount = ntohs (hp->ancount);
+  int qdcount = ntohs (hp->qdcount);
+  const u_char *cp = answer->buf + HFIXEDSZ;
+  const u_char *end_of_message = answer->buf + anslen;
+  if (__builtin_expect (qdcount != 1, 0))
+    {
+      *h_errnop = NO_RECOVERY;
+      return NSS_STATUS_UNAVAIL;
+    }
+
+   u_char packtmp[NS_MAXCDNAME];
+  int n = __ns_name_unpack (answer->buf, end_of_message, cp,
+			    packtmp, sizeof packtmp);
+  /* We unpack the name to check it for validity.  But we do not need
+     it later.  */
+  if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1)
+    {
+      if (__builtin_expect (errno, 0) == EMSGSIZE)
+	{
+	too_small:
+	  *errnop = ERANGE;
+	  *h_errnop = NETDB_INTERNAL;
+	  return NSS_STATUS_TRYAGAIN;
+	}
+
+      n = -1;
+    }
+
+  if (__builtin_expect (n < 0 || (res_hnok (buffer) == 0
+				  && (errno = EBADMSG)), 0))
+    {
+      *errnop = errno;
+      *h_errnop = NO_RECOVERY;
+      return NSS_STATUS_UNAVAIL;
+    }
+  cp += n + QFIXEDSZ;
+
+  int haveanswer = 0;
+  int had_error = 0;
+  char *canon = NULL;
+  char *h_name = NULL;
+  int h_namelen = 0;
+
+  while (ancount-- > 0 && cp < end_of_message && had_error == 0)
+    {
+      n = __ns_name_unpack (answer->buf, end_of_message, cp,
+			    packtmp, sizeof packtmp);
+      if (n != -1 &&
+	  (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1)
+	{
+	  if (__builtin_expect (errno, 0) == EMSGSIZE)
+	    goto too_small;
+
+	  n = -1;
+	}
+      if (n < 0 || res_hnok (buffer) == 0)
+	{
+	  ++had_error;
+	  continue;
+	}
+      if (*firstp)
+	{
+	  h_name = buffer;
+	  buffer += h_namelen;
+	  buflen -= h_namelen;
+	}
+
+      cp += n;				/* name */
+      int type = ns_get16 (cp);
+      cp += INT16SZ;			/* type */
+      int class = ns_get16 (cp);
+      cp += INT16SZ;			/* class */
+      int32_t ttl = ns_get32 (cp);
+      cp += INT32SZ;			/* TTL */
+      n = ns_get16 (cp);
+      cp += INT16SZ;			/* len */
+
+      if (class != C_IN)
+	{
+	  cp += n;
+	  continue;
+	}
+
+      if (type == T_CNAME)
+	{
+	  char tbuf[MAXDNAME];
+	  n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf);
+	  if (n < 0 || res_hnok (tbuf) == 0)
+	    {
+	      ++had_error;
+	      continue;
+	    }
+	  cp += n;
+
+	  if (*firstp)
+	    {
+	      /* Reclaim buffer space.  */
+	      if (h_name + h_namelen == buffer)
+		{
+		  buffer = h_name;
+		  buflen += h_namelen;
+		}
+
+	      n = strlen (tbuf) + 1;
+	      if (__builtin_expect (n > buflen, 0))
+		goto too_small;
+	      if (__builtin_expect (n >= MAXHOSTNAMELEN, 0))
+		{
+		  ++had_error;
+		  continue;
+		}
+
+	      canon = buffer;
+	      buffer = __mempcpy (buffer, tbuf, n);
+	      buflen -= n;
+	      h_namelen = 0;
+	    }
+	  continue;
+	}
+      if (__builtin_expect (type == T_SIG, 0)
+	  || __builtin_expect (type == T_KEY, 0)
+	  || __builtin_expect (type == T_NXT, 0)
+	  || __builtin_expect (type == T_PTR, 0))
+	{
+	  /* We don't support DNSSEC yet.  For now, ignore the record
+	     and send a low priority message to syslog.
+
+	     We also don't expect T_PTR messages.  */
+	  syslog (LOG_DEBUG | LOG_AUTH,
+		  "getaddrinfo*.gaih_getanswer: got type \"%s\"",
+		  p_type (type));
+	  cp += n;
+	  continue;
+	}
+      if (type != T_A && type != T_AAAA)
+	abort ();
+
+      if (*pat == NULL)
+	{
+	  uintptr_t pad = (-(uintptr_t) buffer
+			   % __alignof__ (struct gaih_addrtuple));
+	  buffer += pad;
+	  buflen = buflen > pad ? buflen - pad : 0;
+
+	  if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
+				0))
+	    {
+	      *errnop = ERANGE;
+	      *h_errnop = NETDB_INTERNAL;
+	      return NSS_STATUS_TRYAGAIN;
+	    }
+
+	  *pat = (struct gaih_addrtuple *) buffer;
+	  buffer += sizeof (struct gaih_addrtuple);
+	  buflen -= sizeof (struct gaih_addrtuple);
+	}
+
+      (*pat)->name = NULL;
+      (*pat)->next = NULL;
+
+      if (*firstp)
+	{
+	  if (ttl != 0 && ttlp != NULL)
+	    *ttlp = ttl;
+
+	  if (canon != NULL)
+	    {
+	      (*pat)->name = canon;
+
+	      /* Reclaim buffer space.  */
+	      if (h_name + h_namelen == buffer)
+		{
+		  buffer = h_name;
+		  buflen += h_namelen;
+		}
+	    }
+	  else
+	    (*pat)->name = h_name;
+
+	  *firstp = 0;
+	}
+
+      (*pat)->family = type == T_A ? AF_INET : AF_INET6;
+      memcpy ((*pat)->addr, cp, n);
+      cp += n;
+      (*pat)->scopeid = 0;
+
+      pat = &((*pat)->next);
+
+      haveanswer = 1;
+    }
+
+  if (haveanswer)
+    {
+      *patp = pat;
+      *bufferp = buffer;
+      *buflenp = buflen;
+
+      *h_errnop = NETDB_SUCCESS;
+      return NSS_STATUS_SUCCESS;
+    }
+
+  /* Special case here: if the resolver sent a result but it only
+     contains a CNAME while we are looking for a T_A or T_AAAA record,
+     we fail with NOTFOUND instead of TRYAGAIN.  */
+  return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
+}
+
+
+static enum nss_status
+gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
+		int anslen2, const char *qname,
+		struct gaih_addrtuple **pat, char *buffer, size_t buflen,
+		int *errnop, int *h_errnop, int32_t *ttlp)
+{
+  int first = 1;
+
+  enum nss_status status = gaih_getanswer_slice(answer1, anslen1, qname,
+						&pat, &buffer, &buflen,
+						errnop, h_errnop, ttlp,
+						&first);
+  if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
+      && answer2 != NULL)
+    status = gaih_getanswer_slice(answer2, anslen2, qname,
+				  &pat, &buffer, &buflen,
+				  errnop, h_errnop, ttlp, &first);
+
+  return status;
+}
diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c
index 4552b5b678..40736fbe94 100644
--- a/resolv/nss_dns/dns-network.c
+++ b/resolv/nss_dns/dns-network.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007
+/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007, 2008
    Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
@@ -130,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
 
   anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
-			       1024, &net_buffer.ptr);
+			       1024, &net_buffer.ptr, NULL, NULL);
   if (anslen < 0)
     {
       /* Nothing found.  */
@@ -206,7 +206,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
 
   anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
-			      1024, &net_buffer.ptr);
+			      1024, &net_buffer.ptr, NULL, NULL);
   if (anslen < 0)
     {
       /* Nothing found.  */
diff --git a/resolv/res_query.c b/resolv/res_query.c
index 4371af5b05..a8e8d7b739 100644
--- a/resolv/res_query.c
+++ b/resolv/res_query.c
@@ -97,7 +97,7 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
 static int
 __libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
 			int class, int type, u_char *answer, int anslen,
-			u_char **answerp);
+			u_char **answerp, u_char **answerp2, int *nanswerp2);
 
 /*
  * Formulate a normal query, send, and await answer.
@@ -115,15 +115,20 @@ __libc_res_nquery(res_state statp,
 		  int class, int type,	/* class and type of query */
 		  u_char *answer,	/* buffer to put answer */
 		  int anslen,		/* size of answer buffer */
-		  u_char **answerp)	/* if buffer needs to be enlarged */
+		  u_char **answerp,	/* if buffer needs to be enlarged */
+		  u_char **answerp2,
+		  int *nanswerp2)
 {
-	u_char *buf;
 	HEADER *hp = (HEADER *) answer;
 	int n, use_malloc = 0;
         u_int oflags = statp->_flags;
 
-	size_t bufsize = QUERYSIZE;
-	buf = alloca (bufsize);
+	size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
+	u_char *buf = alloca (bufsize);
+	u_char *query1 = buf;
+	int nquery1 = -1;
+	u_char *query2 = NULL;
+	int nquery2 = 0;
 
  again:
 	hp->rcode = NOERROR;	/* default */
@@ -133,18 +138,47 @@ __libc_res_nquery(res_state statp,
 		printf(";; res_query(%s, %d, %d)\n", name, class, type);
 #endif
 
-	n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
-			 buf, bufsize);
-	if (n > 0
-	    && (oflags & RES_F_EDNS0ERR) == 0
-	    && (statp->options & RES_USE_EDNS0) != 0)
-		n = __res_nopt(statp, n, buf, bufsize, anslen);
+	if (type == T_UNSPEC)
+	  {
+	    n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
+			     query1, bufsize);
+	    if (n > 0)
+	      {
+		if ((oflags & RES_F_EDNS0ERR) == 0
+		    && (statp->options & RES_USE_EDNS0) != 0)
+		  n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
+
+		nquery1 = n;
+		query2 = buf + nquery1;
+		n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
+				 NULL, query2, bufsize - n);
+		if (n > 0
+		    && (oflags & RES_F_EDNS0ERR) == 0
+		    && (statp->options & RES_USE_EDNS0) != 0)
+		  n = __res_nopt(statp, n, query2, bufsize - n, anslen / 2);
+		nquery2 = n;
+	      }
+	  }
+	else
+	  {
+	    n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
+			     query1, bufsize);
+
+	    if (n > 0
+		&& (oflags & RES_F_EDNS0ERR) == 0
+		&& (statp->options & RES_USE_EDNS0) != 0)
+	      n = __res_nopt(statp, n, query1, bufsize, anslen);
+
+	    nquery1 = n;
+	  }
+
 	if (__builtin_expect (n <= 0, 0) && !use_malloc) {
 		/* Retry just in case res_nmkquery failed because of too
 		   short buffer.  Shouldn't happen.  */
-		bufsize = MAXPACKET;
+		bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET;
 		buf = malloc (bufsize);
 		if (buf != NULL) {
+			query1 = buf;
 			use_malloc = 1;
 			goto again;
 		}
@@ -168,7 +202,8 @@ __libc_res_nquery(res_state statp,
 		return (n);
 	}
 	assert (answerp == NULL || (void *) *answerp == (void *) answer);
-	n = __libc_res_nsend(statp, buf, n, answer, anslen, answerp);
+	n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
+			     anslen, answerp, answerp2, nanswerp2);
 	if (use_malloc)
 		free (buf);
 	if (n < 0) {
@@ -184,20 +219,37 @@ __libc_res_nquery(res_state statp,
 	  /* __libc_res_nsend might have reallocated the buffer.  */
 	  hp = (HEADER *) *answerp;
 
-	if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+	/* We simplify the following tests by assigning HP to HP2.  It
+	   is easy to verify that this is the same as ignoring all
+	   tests of HP2.  */
+	HEADER *hp2 = answerp2 ? (HEADER *) *answerp2 : hp;
+
+	if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
+	    && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
 #ifdef DEBUG
-		if (statp->options & RES_DEBUG)
+		if (statp->options & RES_DEBUG) {
 			printf(";; rcode = %d, ancount=%d\n", hp->rcode,
 			    ntohs(hp->ancount));
+			if (hp != hp2)
+			  printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode,
+				 ntohs(hp2->ancount));
+		}
 #endif
-		switch (hp->rcode) {
+		switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
 		case NXDOMAIN:
+			if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
+			    || (hp2->rcode == NOERROR
+				&& ntohs (hp2->ancount) != 0))
+				goto success;
 			RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
 			break;
 		case SERVFAIL:
 			RES_SET_H_ERRNO(statp, TRY_AGAIN);
 			break;
 		case NOERROR:
+			if (ntohs (hp->ancount) != 0
+			    || ntohs (hp2->ancount) != 0)
+				goto success;
 			RES_SET_H_ERRNO(statp, NO_DATA);
 			break;
 		case FORMERR:
@@ -209,6 +261,7 @@ __libc_res_nquery(res_state statp,
 		}
 		return (-1);
 	}
+ success:
 	return (n);
 }
 libresolv_hidden_def (__libc_res_nquery)
@@ -221,7 +274,7 @@ res_nquery(res_state statp,
 	   int anslen)		/* size of answer buffer */
 {
 	return __libc_res_nquery(statp, name, class, type, answer, anslen,
-				 NULL);
+				 NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nquery)
 
@@ -233,11 +286,13 @@ libresolv_hidden_def (res_nquery)
  */
 int
 __libc_res_nsearch(res_state statp,
-	    const char *name,	/* domain name */
-	    int class, int type,	/* class and type of query */
-	    u_char *answer,	/* buffer to put answer */
-	    int anslen,		/* size of answer */
-	    u_char **answerp)
+		   const char *name,	/* domain name */
+		   int class, int type,	/* class and type of query */
+		   u_char *answer,	/* buffer to put answer */
+		   int anslen,		/* size of answer */
+		   u_char **answerp,
+		   u_char **answerp2,
+		   int *nanswerp2)
 {
 	const char *cp, * const *domain;
 	HEADER *hp = (HEADER *) answer;
@@ -260,7 +315,8 @@ __libc_res_nsearch(res_state statp,
 	/* If there aren't any dots, it could be a user-level alias. */
 	if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
 		return (__libc_res_nquery(statp, cp, class, type, answer,
-					  anslen, answerp));
+					  anslen, answerp, answerp2,
+					  nanswerp2));
 
 #ifdef DEBUG
 	if (statp->options & RES_DEBUG)
@@ -276,7 +332,8 @@ __libc_res_nsearch(res_state statp,
 	saved_herrno = -1;
 	if (dots >= statp->ndots || trailing_dot) {
 		ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
-					      answer, anslen, answerp);
+					      answer, anslen, answerp,
+					      answerp2, nanswerp2);
 		if (ret > 0 || trailing_dot)
 			return (ret);
 		saved_herrno = h_errno;
@@ -285,6 +342,12 @@ __libc_res_nsearch(res_state statp,
 			answer = *answerp;
 			anslen = MAXPACKET;
 		}
+		if (answerp2
+		    && (*answerp2 < answer || *answerp2 >= answer + anslen))
+		  {
+		    free (*answerp2);
+		    *answerp2 = NULL;
+		  }
 	}
 
 	/*
@@ -307,7 +370,8 @@ __libc_res_nsearch(res_state statp,
 
 			ret = __libc_res_nquerydomain(statp, name, *domain,
 						      class, type,
-						      answer, anslen, answerp);
+						      answer, anslen, answerp,
+						      answerp2, nanswerp2);
 			if (ret > 0)
 				return (ret);
 
@@ -315,6 +379,13 @@ __libc_res_nsearch(res_state statp,
 				answer = *answerp;
 				anslen = MAXPACKET;
 			}
+			if (answerp2
+			    && (*answerp2 < answer
+				|| *answerp2 >= answer + anslen))
+			  {
+			    free (*answerp2);
+			    *answerp2 = NULL;
+			  }
 
 			/*
 			 * If no server present, give up.
@@ -368,7 +439,8 @@ __libc_res_nsearch(res_state statp,
 	 */
 	if (dots && !(tried_as_is || root_on_list)) {
 		ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
-					      answer, anslen, answerp);
+					      answer, anslen, answerp,
+					      answerp2, nanswerp2);
 		if (ret > 0)
 			return (ret);
 	}
@@ -380,6 +452,11 @@ __libc_res_nsearch(res_state statp,
 	 * else send back meaningless H_ERRNO, that being the one from
 	 * the last DNSRCH we did.
 	 */
+	if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen))
+	  {
+	    free (*answerp2);
+	    *answerp2 = NULL;
+	  }
 	if (saved_herrno != -1)
 		RES_SET_H_ERRNO(statp, saved_herrno);
 	else if (got_nodata)
@@ -398,7 +475,7 @@ res_nsearch(res_state statp,
 	    int anslen)		/* size of answer */
 {
 	return __libc_res_nsearch(statp, name, class, type, answer,
-				  anslen, NULL);
+				  anslen, NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nsearch)
 
@@ -408,12 +485,14 @@ libresolv_hidden_def (res_nsearch)
  */
 static int
 __libc_res_nquerydomain(res_state statp,
-	    const char *name,
-	    const char *domain,
-	    int class, int type,	/* class and type of query */
-	    u_char *answer,		/* buffer to put answer */
-	    int anslen,			/* size of answer */
-	    u_char **answerp)
+			const char *name,
+			const char *domain,
+			int class, int type,	/* class and type of query */
+			u_char *answer,		/* buffer to put answer */
+			int anslen,			/* size of answer */
+			u_char **answerp,
+			u_char **answerp2,
+			int *nanswerp2)
 {
 	char nbuf[MAXDNAME];
 	const char *longname = nbuf;
@@ -450,7 +529,7 @@ __libc_res_nquerydomain(res_state statp,
 		sprintf(nbuf, "%s.%s", name, domain);
 	}
 	return (__libc_res_nquery(statp, longname, class, type, answer,
-				  anslen, answerp));
+				  anslen, answerp, answerp2, nanswerp2));
 }
 
 int
@@ -462,7 +541,7 @@ res_nquerydomain(res_state statp,
 	    int anslen)		/* size of answer */
 {
 	return __libc_res_nquerydomain(statp, name, domain, class, type,
-				       answer, anslen, NULL);
+				       answer, anslen, NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nquerydomain)
 
diff --git a/resolv/res_send.c b/resolv/res_send.c
index 23306a2fb4..b3dbd702a2 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -176,10 +176,14 @@ evNowTime(struct timespec *res) {
 /* Forward. */
 
 static int		send_vc(res_state, const u_char *, int,
-				u_char **, int *, int *, int, u_char **);
+				const u_char *, int,
+				u_char **, int *, int *, int, u_char **,
+				u_char **, int *, int *);
 static int		send_dg(res_state, const u_char *, int,
+				const u_char *, int,
 				u_char **, int *, int *, int,
-				int *, int *, u_char **);
+				int *, int *, u_char **,
+				u_char **, int *, int *);
 #ifdef DEBUG
 static void		Aerror(const res_state, FILE *, const char *, int,
 			       const struct sockaddr *);
@@ -334,33 +338,41 @@ libresolv_hidden_def (res_queriesmatch)
 
 int
 __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
-		 u_char *ans, int anssiz, u_char **ansp)
+		 const u_char *buf2, int buflen2,
+		 u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
+		 int *nansp2)
 {
-	int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
+  int gotsomewhere, terrno, try, v_circuit, resplen, resplen2, ns, n;
 
 	if (statp->nscount == 0) {
 		__set_errno (ESRCH);
 		return (-1);
 	}
 
-	if (anssiz < HFIXEDSZ) {
+	if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) {
 		__set_errno (EINVAL);
 		return (-1);
 	}
 
-	if ((statp->qhook || statp->rhook) && anssiz < MAXPACKET && ansp) {
-		u_char *buf = malloc (MAXPACKET);
-		if (buf == NULL)
-			return (-1);
-		memcpy (buf, ans, HFIXEDSZ);
-		*ansp = buf;
-		ans = buf;
-		anssiz = MAXPACKET;
+#ifdef USE_HOOKS
+	if (__builtin_expect (statp->qhook || statp->rhook, 0)) {
+		if (anssiz < MAXPACKET && ansp) {
+			u_char *buf = malloc (MAXPACKET);
+			if (buf == NULL)
+				return (-1);
+			memcpy (buf, ans, HFIXEDSZ);
+			*ansp = buf;
+			ans = buf;
+			anssiz = MAXPACKET;
+		}
 	}
+#endif
 
 	DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
 		(stdout, ";; res_send()\n"), buf, buflen);
-	v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
+	v_circuit = ((statp->options & RES_USEVC)
+		     || buflen > PACKETSZ
+		     || buflen2 > PACKETSZ);
 	gotsomewhere = 0;
 	terrno = ETIMEDOUT;
 
@@ -442,7 +454,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 	 * Some resolvers want to even out the load on their nameservers.
 	 * Note that RES_BLAST overrides RES_ROTATE.
 	 */
-	if ((statp->options & RES_ROTATE) != 0 &&
+	if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) &&
 	    (statp->options & RES_BLAST) == 0) {
 		struct sockaddr_in6 *ina;
 		unsigned int map;
@@ -479,8 +491,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 
 		if (nsap == NULL)
 			goto next_ns;
- same_ns:
-		if (statp->qhook) {
+	    same_ns:
+#ifdef USE_HOOKS
+		if (__builtin_expect (statp->qhook != NULL, 0)) {
 			int done = 0, loops = 0;
 
 			do {
@@ -512,6 +525,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 				}
 			} while (!done);
 		}
+#endif
 
 #ifdef DEBUG
 		char tmpbuf[40];
@@ -521,29 +535,34 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 			ns + 1, inet_ntop(AF_INET6, &nsap->sin6_addr,
 					  tmpbuf, sizeof (tmpbuf))));
 
-		if (v_circuit) {
+		if (__builtin_expect (v_circuit, 0)) {
 			/* Use VC; at most one attempt per server. */
 			try = statp->retry;
-			n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno,
-				    ns, ansp);
+			n = send_vc(statp, buf, buflen, buf2, buflen2,
+				    &ans, &anssiz, &terrno,
+				    ns, ansp, ansp2, nansp2, &resplen2);
 			if (n < 0)
 				return (-1);
 			if (n == 0)
 				goto next_ns;
-			resplen = n;
 		} else {
 			/* Use datagrams. */
-			n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno,
-				    ns, &v_circuit, &gotsomewhere, ansp);
+			n = send_dg(statp, buf, buflen, buf2, buflen2,
+				    &ans, &anssiz, &terrno,
+				    ns, &v_circuit, &gotsomewhere, ansp,
+				    ansp2, nansp2, &resplen2);
 			if (n < 0)
 				return (-1);
 			if (n == 0)
 				goto next_ns;
 			if (v_circuit)
+			  // XXX Check whether both requests failed or
+			  // XXX whether one have been answered successfully
 				goto same_ns;
-			resplen = n;
 		}
 
+		resplen = n;
+
 		Dprint((statp->options & RES_DEBUG) ||
 		       ((statp->pfcode & RES_PRF_REPLY) &&
 			(statp->pfcode & RES_PRF_HEAD1)),
@@ -553,6 +572,11 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 			(statp->pfcode & RES_PRF_REPLY),
 			(stdout, "%s", ""),
 			ans, (resplen > anssiz) ? anssiz : resplen);
+		if (buf2 != NULL)
+		  DprintQ((statp->options & RES_DEBUG) ||
+			  (statp->pfcode & RES_PRF_REPLY),
+			  (stdout, "%s", ""),
+			  *ansp2, (resplen2 > *nansp2) ? *nansp2 : resplen2);
 
 		/*
 		 * If we have temporarily opened a virtual circuit,
@@ -563,7 +587,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 		    (statp->options & RES_STAYOPEN) == 0) {
 			__res_iclose(statp, false);
 		}
-		if (statp->rhook) {
+#ifdef USE_HOOKS
+		if (__builtin_expect (statp->rhook, 0)) {
 			int done = 0, loops = 0;
 
 			do {
@@ -593,6 +618,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 			} while (!done);
 
 		}
+#endif
 		return (resplen);
  next_ns: ;
 	   } /*foreach ns*/
@@ -612,7 +638,8 @@ int
 res_nsend(res_state statp,
 	  const u_char *buf, int buflen, u_char *ans, int anssiz)
 {
-	return __libc_res_nsend(statp, buf, buflen, ans, anssiz, NULL);
+  return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
+			  NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nsend)
 
@@ -620,17 +647,23 @@ libresolv_hidden_def (res_nsend)
 
 static int
 send_vc(res_state statp,
-	const u_char *buf, int buflen, u_char **ansp, int *anssizp,
-	int *terrno, int ns, u_char **anscp)
+	const u_char *buf, int buflen, const u_char *buf2, int buflen2,
+	u_char **ansp, int *anssizp,
+	int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2,
+	int *resplen2)
 {
 	const HEADER *hp = (HEADER *) buf;
+	const HEADER *hp2 = (HEADER *) buf2;
 	u_char *ans = *ansp;
-	int anssiz = *anssizp;
+	int orig_anssizp = *anssizp;
+	// XXX REMOVE
+	// int anssiz = *anssizp;
 	HEADER *anhp = (HEADER *) ans;
 	struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
 	int truncating, connreset, resplen, n;
-	struct iovec iov[2];
+	struct iovec iov[4];
 	u_short len;
+	u_short len2;
 	u_char *cp;
 
 	connreset = 0;
@@ -677,11 +710,19 @@ send_vc(res_state statp,
 	/*
 	 * Send length & message
 	 */
-	ns_put16((u_short)buflen, (u_char*)&len);
+	len = htons ((u_short) buflen);
 	evConsIovec(&len, INT16SZ, &iov[0]);
 	evConsIovec((void*)buf, buflen, &iov[1]);
-	if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, 2))
-	    != (INT16SZ + buflen)) {
+	int niov = 2;
+	ssize_t explen = INT16SZ + buflen;
+	if (buf2 != NULL) {
+		len2 = htons ((u_short) buflen2);
+		evConsIovec(&len2, INT16SZ, &iov[2]);
+		evConsIovec((void*)buf2, buflen2, &iov[3]);
+		niov = 4;
+		explen += INT16SZ + buflen2;
+	}
+	if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, niov)) != explen) {
 		*terrno = errno;
 		Perror(statp, stderr, "write failed", errno);
 		__res_iclose(statp, false);
@@ -690,6 +731,8 @@ send_vc(res_state statp,
 	/*
 	 * Receive length & response
 	 */
+	int recvresp1 = 0;
+	int recvresp2 = buf2 == NULL;
  read_len:
 	cp = ans;
 	len = INT16SZ;
@@ -718,30 +761,66 @@ send_vc(res_state statp,
 		}
 		return (0);
 	}
+#ifdef _STRING_ARCH_unaligned
+	resplen = ntohs (*(uint16_t *) ans);
+#else
 	resplen = ns_get16(ans);
-	if (resplen > anssiz) {
+#endif
+
+	int *thisanssizp;
+	u_char **thisansp;
+	int *thisresplenp;
+	if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
+		thisanssizp = anssizp;
+		thisansp = anscp ?: ansp;
+		assert (anscp != NULL || ansp2 == NULL);
+		thisresplenp = &resplen;
+	} else {
+		if (*anssizp != MAXPACKET) {
+			/* No buffer allocated for the first
+			   reply.  We can try to use the rest
+			   of the user-provided buffer.  */
+			*anssizp2 = orig_anssizp - resplen;
+			*ansp2 = *ansp + resplen;
+		} else {
+			/* The first reply did not fit into the
+			   user-provided buffer.  Maybe the second
+			   answer will.  */
+			*anssizp2 = orig_anssizp;
+			*ansp2 = *ansp;
+		}
+
+		thisanssizp = anssizp2;
+		thisansp = ansp2;
+		thisresplenp = resplen2;
+	}
+	anhp = (HEADER *) *thisansp;
+
+	*thisresplenp = resplen;
+	if (resplen > *thisanssizp) {
+		/* Yes, we test ANSCP here.  If we have two buffers
+		   both will be allocatable.  */
 		if (anscp) {
-			ans = malloc (MAXPACKET);
-			if (ans == NULL) {
+			u_char *newp = malloc (MAXPACKET);
+			if (newp == NULL) {
 				*terrno = ENOMEM;
 				__res_iclose(statp, false);
 				return (0);
 			}
-			anssiz = MAXPACKET;
-			*anssizp = MAXPACKET;
-			*ansp = ans;
-			*anscp = ans;
-			anhp = (HEADER *) ans;
+			*thisanssizp = MAXPACKET;
+			*thisansp = newp;
+			anhp = (HEADER *) newp;
 			len = resplen;
 		} else {
 			Dprint(statp->options & RES_DEBUG,
 				(stdout, ";; response truncated\n")
 			);
 			truncating = 1;
-			len = anssiz;
+			len = *thisanssizp;
 		}
 	} else
 		len = resplen;
+
 	if (len < HFIXEDSZ) {
 		/*
 		 * Undersized message.
@@ -752,7 +831,8 @@ send_vc(res_state statp,
 		__res_iclose(statp, false);
 		return (0);
 	}
-	cp = ans;
+
+	cp = *thisansp;
 	while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
 		cp += n;
 		len -= n;
@@ -768,7 +848,7 @@ send_vc(res_state statp,
 		 * Flush rest of answer so connection stays in synch.
 		 */
 		anhp->tc = 1;
-		len = resplen - anssiz;
+		len = resplen - *thisanssizp;
 		while (len != 0) {
 			char junk[PACKETSZ];
 
@@ -787,14 +867,25 @@ send_vc(res_state statp,
 	 * itself confused, then drop the packet and
 	 * wait for the correct one.
 	 */
-	if (hp->id != anhp->id) {
+	if ((recvresp1 || hp->id != anhp->id)
+	    && (recvresp2 || hp2->id != anhp->id)) {
 		DprintQ((statp->options & RES_DEBUG) ||
 			(statp->pfcode & RES_PRF_REPLY),
 			(stdout, ";; old answer (unexpected):\n"),
-			ans, (resplen > anssiz) ? anssiz: resplen);
+			*thisansp,
+			(resplen > *thisanssiz) ? *thisanssiz: resplen);
 		goto read_len;
 	}
 
+	/* Mark which reply we received.  */
+	if (recvresp1 == 0 && hp->id == anhp->id)
+	  recvresp1 = 1;
+	else
+	  recvresp2 = 1;
+	/* Repeat waiting if we have a second answer to arrive.  */
+	if ((recvresp1 & recvresp2) == 0)
+		goto read_len;
+
 	/*
 	 * All is well, or the error is fatal.  Signal that the
 	 * next nameserver ought not be tried.
@@ -804,19 +895,20 @@ send_vc(res_state statp,
 
 static int
 send_dg(res_state statp,
-	const u_char *buf, int buflen, u_char **ansp, int *anssizp,
-	int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp)
+	const u_char *buf, int buflen, const u_char *buf2, int buflen2,
+	u_char **ansp, int *anssizp,
+	int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp,
+	u_char **ansp2, int *anssizp2, int *resplen2)
 {
 	const HEADER *hp = (HEADER *) buf;
+	const HEADER *hp2 = (HEADER *) buf2;
 	u_char *ans = *ansp;
-	int anssiz = *anssizp;
-	HEADER *anhp = (HEADER *) ans;
+	int orig_anssizp = *anssizp;
 	struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
 	struct timespec now, timeout, finish;
 	struct pollfd pfd[1];
         int ptimeout;
 	struct sockaddr_in6 from;
-	socklen_t fromlen;
 	int resplen, seconds, n;
 
 	if (EXT(statp).nssocks[ns] == -1) {
@@ -879,6 +971,8 @@ send_dg(res_state statp,
 	evAddTime(&finish, &now, &timeout);
 	int need_recompute = 0;
 	int nwritten = 0;
+	int recvresp1 = 0;
+	int recvresp2 = buf2 == NULL;
 	pfd[0].fd = EXT(statp).nssocks[ns];
 	pfd[0].events = POLLOUT;
  wait:
@@ -918,35 +1012,73 @@ send_dg(res_state statp,
 	}
 	__set_errno (0);
 	if (pfd[0].revents & POLLOUT) {
-		if (send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL) != buflen) {
+		ssize_t sr;
+		if (nwritten != 0)
+		  sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
+		else
+		  sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
+
+		if (sr != buflen) {
 			if (errno == EINTR || errno == EAGAIN)
 				goto recompute_resend;
 			Perror(statp, stderr, "send", errno);
 			goto err_out;
 		}
-		pfd[0].events = POLLIN;
+		if (nwritten != 0 || buf2 == NULL)
+		  pfd[0].events = POLLIN;
+		else
+		  pfd[0].events = POLLIN | POLLOUT;
 		++nwritten;
 		goto wait;
 	} else if (pfd[0].revents & POLLIN) {
-		fromlen = sizeof(struct sockaddr_in6);
-		if (anssiz < MAXPACKET
+		int *thisanssizp;
+		u_char **thisansp;
+		int *thisresplenp;
+
+		if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
+			thisanssizp = anssizp;
+			thisansp = anscp ?: ansp;
+			assert (anscp != NULL || ansp2 == NULL);
+			thisresplenp = &resplen;
+		} else {
+			if (*anssizp != MAXPACKET) {
+				/* No buffer allocated for the first
+				   reply.  We can try to use the rest
+				   of the user-provided buffer.  */
+				*anssizp2 = orig_anssizp - resplen;
+				*ansp2 = *ansp + resplen;
+			} else {
+				/* The first reply did not fit into the
+				   user-provided buffer.  Maybe the second
+				   answer will.  */
+				*anssizp2 = orig_anssizp;
+				*ansp2 = *ansp;
+			}
+
+			thisanssizp = anssizp2;
+			thisansp = ansp2;
+			thisresplenp = resplen2;
+		}
+
+		if (*thisanssizp < MAXPACKET
+		    /* Yes, we test ANSCP here.  If we have two buffers
+		       both will be allocatable.  */
 		    && anscp
-		    && (ioctl (pfd[0].fd, FIONREAD, &resplen) < 0
-		|| anssiz < resplen)) {
-			ans = malloc (MAXPACKET);
-			if (ans == NULL)
-				ans = *ansp;
-			else {
-				anssiz = MAXPACKET;
+		    && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
+			|| *thisanssizp < *thisresplenp)) {
+			u_char *newp = malloc (MAXPACKET);
+			if (newp != NULL) {
 				*anssizp = MAXPACKET;
-				*ansp = ans;
-				*anscp = ans;
-				anhp = (HEADER *) ans;
+				*thisansp = ans = newp;
 			}
 		}
-		resplen = recvfrom(pfd[0].fd, (char*)ans, anssiz,0,
-				   (struct sockaddr *)&from, &fromlen);
-		if (resplen <= 0) {
+		HEADER *anhp = (HEADER *) *thisansp;
+		socklen_t fromlen = sizeof(struct sockaddr_in6);
+		assert (sizeof(from) <= fromlen);
+		*thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp,
+					 *thisanssizp, 0,
+					(struct sockaddr *)&from, &fromlen);
+		if (*thisresplenp <= 0) {
 			if (errno == EINTR || errno == EAGAIN) {
 				need_recompute = 1;
 				goto wait;
@@ -955,17 +1087,18 @@ send_dg(res_state statp,
 			goto err_out;
 		}
 		*gotsomewhere = 1;
-		if (resplen < HFIXEDSZ) {
+		if (*thisresplenp < HFIXEDSZ) {
 			/*
 			 * Undersized message.
 			 */
 			Dprint(statp->options & RES_DEBUG,
 			       (stdout, ";; undersized: %d\n",
-				resplen));
+				*thisresplen));
 			*terrno = EMSGSIZE;
 			goto err_out;
 		}
-		if (hp->id != anhp->id) {
+		if ((recvresp1 || hp->id != anhp->id)
+		    && (recvresp2 || hp2->id != anhp->id)) {
 			/*
 			 * response from old query, ignore it.
 			 * XXX - potential security hazard could
@@ -974,7 +1107,9 @@ send_dg(res_state statp,
 			DprintQ((statp->options & RES_DEBUG) ||
 				(statp->pfcode & RES_PRF_REPLY),
 				(stdout, ";; old answer:\n"),
-				ans, (resplen > anssiz) ? anssiz : resplen);
+				thisansp,
+				(*thisresplen > *thisanssiz)
+				? *thisanssiz : *thisresplen);
 			goto wait;
 		}
 		if (!(statp->options & RES_INSECURE1) &&
@@ -987,14 +1122,16 @@ send_dg(res_state statp,
 			DprintQ((statp->options & RES_DEBUG) ||
 				(statp->pfcode & RES_PRF_REPLY),
 				(stdout, ";; not our server:\n"),
-				ans, (resplen > anssiz) ? anssiz : resplen);
+				thisansp,
+				(*thisresplen > *thisanssiz)
+				? *thisanssiz : *thisresplen);
 			goto wait;
 		}
 #ifdef RES_USE_EDNS0
 		if (anhp->rcode == FORMERR
 		    && (statp->options & RES_USE_EDNS0) != 0U) {
 			/*
-			 * Do not retry if the server do not understand
+			 * Do not retry if the server does not understand
 			 * EDNS0.  The case has to be captured here, as
 			 * FORMERR packet do not carry query section, hence
 			 * res_queriesmatch() returns 0.
@@ -1002,15 +1139,23 @@ send_dg(res_state statp,
 			DprintQ(statp->options & RES_DEBUG,
 				(stdout,
 				 "server rejected query with EDNS0:\n"),
-				ans, (resplen > anssiz) ? anssiz : resplen);
+				thisans,
+				(*thisresplen > *thisanssiz)
+				? *thisanssiz : *thisresplen);
 			/* record the error */
 			statp->_flags |= RES_F_EDNS0ERR;
 			goto err_out;
         }
 #endif
-		if (!(statp->options & RES_INSECURE2) &&
-		    !res_queriesmatch(buf, buf + buflen,
-				      ans, ans + anssiz)) {
+		if (!(statp->options & RES_INSECURE2)
+		    && (recvresp1 || !res_queriesmatch(buf, buf + buflen,
+						       *thisansp,
+						       *thisansp
+						       + *thisanssizp))
+		    && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2,
+						       *thisansp,
+						       *thisansp
+						       + *thisanssizp))) {
 			/*
 			 * response contains wrong query? ignore it.
 			 * XXX - potential security hazard could
@@ -1019,7 +1164,9 @@ send_dg(res_state statp,
 			DprintQ((statp->options & RES_DEBUG) ||
 				(statp->pfcode & RES_PRF_REPLY),
 				(stdout, ";; wrong query name:\n"),
-				ans, (resplen > anssiz) ? anssiz : resplen);
+				thisansp,
+				(*thisresplen > *thisanssiz)
+				? *thisanssiz : *thisresplen);
 			goto wait;
 		}
 		if (anhp->rcode == SERVFAIL ||
@@ -1027,7 +1174,9 @@ send_dg(res_state statp,
 		    anhp->rcode == REFUSED) {
 			DprintQ(statp->options & RES_DEBUG,
 				(stdout, "server rejected query:\n"),
-				ans, (resplen > anssiz) ? anssiz : resplen);
+				thisansp,
+				(*thisresplen > *thisanssiz)
+				? *thisanssiz : *thisresplen);
 		next_ns:
 			__res_iclose(statp, false);
 			/* don't retry if called from dig */
@@ -1038,7 +1187,9 @@ send_dg(res_state statp,
 		    && anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
 			DprintQ(statp->options & RES_DEBUG,
 				(stdout, "referred query:\n"),
-				ans, (resplen > anssiz) ? anssiz : resplen);
+				thisansp,
+				(*thisresplen > *thisanssiz)
+				? *thisanssiz : *thisresplen);
 			goto next_ns;
 		}
 		if (!(statp->options & RES_IGNTC) && anhp->tc) {
@@ -1050,8 +1201,18 @@ send_dg(res_state statp,
 			       (stdout, ";; truncated answer\n"));
 			*v_circuit = 1;
 			__res_iclose(statp, false);
+			// XXX if we have received one reply we could
+			// XXX use it and not repeat it over TCP...
 			return (1);
 		}
+		/* Mark which reply we received.  */
+		if (recvresp1 == 0 && hp->id == anhp->id)
+			recvresp1 = 1;
+		else
+			recvresp2 = 1;
+		/* Repeat waiting if we have a second answer to arrive.  */
+		if ((recvresp1 & recvresp2) == 0)
+			goto wait;
 		/*
 		 * All is well, or the error is fatal.  Signal that the
 		 * next nameserver ought not be tried.