about summary refs log tree commit diff
path: root/resolv
diff options
context:
space:
mode:
Diffstat (limited to 'resolv')
-rw-r--r--resolv/Versions1
-rw-r--r--resolv/gethnamaddr.c46
-rw-r--r--resolv/nss_dns/dns-host.c60
-rw-r--r--resolv/nss_dns/dns-network.c50
-rw-r--r--resolv/res_query.c119
-rw-r--r--resolv/res_send.c96
6 files changed, 219 insertions, 153 deletions
diff --git a/resolv/Versions b/resolv/Versions
index ab6a386802..09dca6aa2a 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -72,6 +72,7 @@ libresolv {
     # Needed in libnss_dns.
     __ns_name_unpack; __ns_name_ntop;
     __ns_get16; __ns_samename;
+    __libc_res_nquery; __libc_res_nsearch;
   }
 }
 
diff --git a/resolv/gethnamaddr.c b/resolv/gethnamaddr.c
index 797af94874..e941b5c1ac 100644
--- a/resolv/gethnamaddr.c
+++ b/resolv/gethnamaddr.c
@@ -510,10 +510,10 @@ gethostbyname2(name, af)
 	const char *name;
 	int af;
 {
-	querybuf *buf;
+	querybuf *buf, *origbuf;
 	register const char *cp;
 	char *bp;
-	int n, size, type, len, use_malloc = 0;
+	int n, size, type, len;
 	struct hostent *ret;
 	extern struct hostent *_gethtbyname2();
 
@@ -616,24 +616,19 @@ gethostbyname2(name, af)
 				break;
 		}
 
-	if (!__libc_use_alloca (MAXPACKET)) {
-		buf = (querybuf *) malloc (sizeof (*buf));
-		if (buf == NULL) {
-			__set_h_errno (NETDB_INTERNAL);
-			return NULL;
-		}
-		use_malloc = 1;
-	} else
-		buf = (querybuf *) alloca (sizeof (*buf));
+	buf = origbuf = (querybuf *) alloca (1024);
 
-	if ((n = res_nsearch(&_res, name, C_IN, type, buf->buf, sizeof(buf->buf))) < 0) {
+	if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf->buf, 1024,
+				    (u_char **) &buf)) < 0) {
+		if (buf != origbuf)
+			free (buf);
 		dprintf("res_nsearch failed (%d)\n", n);
 		if (errno == ECONNREFUSED)
 			return (_gethtbyname2(name, af));
 		return (NULL);
 	}
 	ret = getanswer(buf, n, name, type);
-	if (use_malloc)
+	if (buf != origbuf)
 		free (buf);
 	return ret;
 }
@@ -647,9 +642,9 @@ gethostbyaddr(addr, len, af)
 	const u_char *uaddr = (const u_char *)addr;
 	static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
 	static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
-	int n, use_malloc = 0;
+	int n;
 	socklen_t size;
-	querybuf *buf;
+	querybuf *buf, *orig_buf;
 	register struct hostent *hp;
 	char qbuf[MAXDNAME+1], *qp = NULL;
 #ifdef SUNSECURITY
@@ -711,23 +706,18 @@ gethostbyaddr(addr, len, af)
 		abort();
 	}
 
-	if (!__libc_use_alloca (MAXPACKET)) {
-		buf = (querybuf *) malloc (sizeof (*buf));
-		if (buf == NULL) {
-			__set_h_errno (NETDB_INTERNAL);
-			return NULL;
-		}
-		use_malloc = 1;
-	} else
-		buf = (querybuf *) alloca (sizeof (*buf));
+	buf = orig_buf = (querybuf *) alloca (1024);
 
-	n = res_nquery(&_res, qbuf, C_IN, T_PTR, buf->buf, sizeof buf->buf);
+	n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf->buf, 1024,
+			      (u_char **) &buf);
 	if (n < 0 && af == AF_INET6) {
 		strcpy(qp, "ip6.int");
-		n = res_nquery(&_res, qbuf, C_IN, T_PTR, buf->buf, sizeof buf->buf);
+		n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf->buf,
+				      buf != orig_buf ? MAXPACKET : 1024,
+				      (u_char **) &buf);
 	}
 	if (n < 0) {
-		if (use_malloc)
+		if (buf != orig_buf)
 			free (buf);
 		dprintf("res_nquery failed (%d)\n", n);
 		if (errno == ECONNREFUSED)
@@ -735,7 +725,7 @@ gethostbyaddr(addr, len, af)
 		return (NULL);
 	}
 	hp = getanswer(buf, n, qbuf, T_PTR);
-	if (use_malloc)
+	if (buf != orig_buf)
 		free (buf);
 	if (!hp)
 		return (NULL);	/* h_errno was set by getanswer() */
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 39eccabbdf..b01dc54993 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -132,11 +132,11 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
 			   char *buffer, size_t buflen, int *errnop,
 			   int *h_errnop)
 {
-  querybuf *host_buffer;
+  querybuf *host_buffer, *orig_host_buffer;
   char tmp[NS_MAXDNAME];
   int size, type, n;
   const char *cp;
-  int map = 0, use_malloc = 0;
+  int map = 0;
   int olderr = errno;
   enum nss_status status;
 
@@ -170,21 +170,10 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
       && (cp = res_hostalias (&_res, name, tmp, sizeof (tmp))) != NULL)
     name = cp;
 
-  if (!__libc_use_alloca (MAXPACKET))
-    {
-      host_buffer = (querybuf *) malloc (sizeof (querybuf));
-      if (host_buffer == NULL)
-	{
-	  *errnop = ENOMEM;
-	  return NSS_STATUS_UNAVAIL;
-	}
-      use_malloc = 1;
-    }
-  else
-    host_buffer = (querybuf *) alloca (sizeof (querybuf));
+  host_buffer = orig_host_buffer = (querybuf *) alloca (1024);
 
-  n = res_nsearch (&_res, name, C_IN, type, host_buffer->buf,
-		   sizeof (host_buffer->buf));
+  n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer->buf,
+			  1024, (u_char **) &host_buffer);
   if (n < 0)
     {
       enum nss_status status = (errno == ECONNREFUSED
@@ -199,12 +188,13 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
 	 by having the RES_USE_INET6 bit in _res.options set, we try
 	 another lookup.  */
       if (af == AF_INET6 && (_res.options & RES_USE_INET6))
-	n = res_nsearch (&_res, name, C_IN, T_A, host_buffer->buf,
-			 sizeof (host_buffer->buf));
+	n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer->buf,
+				host_buffer != orig_host_buffer
+				? MAXPACKET : 1024, (u_char **) &host_buffer);
 
       if (n < 0)
 	{
-	  if (use_malloc)
+	  if (host_buffer != orig_host_buffer)
 	    free (host_buffer);
 	  return status;
 	}
@@ -217,7 +207,7 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
 
   status = getanswer_r (host_buffer, n, name, type, result, buffer, buflen,
 			errnop, h_errnop, map);
-  if (use_malloc)
+  if (host_buffer != orig_host_buffer)
     free (host_buffer);
   return status;
 }
@@ -257,10 +247,10 @@ _nss_dns_gethostbyaddr_r (const void *addr, socklen_t len, int af,
     char *h_addr_ptrs[MAX_NR_ADDRS + 1];
     char linebuffer[0];
   } *host_data = (struct host_data *) buffer;
-  querybuf *host_buffer;
+  querybuf *host_buffer, *orig_host_buffer;
   char qbuf[MAXDNAME+1], *qp = NULL;
   size_t size;
-  int n, status, use_malloc = 0;
+  int n, status;
   int olderr = errno;
 
   if ((_res.options & RES_INIT) == 0 && __res_ninit (&_res) == -1)
@@ -315,39 +305,29 @@ _nss_dns_gethostbyaddr_r (const void *addr, socklen_t len, int af,
       break;
     }
 
-  if (!__libc_use_alloca (MAXPACKET))
-    {
-      host_buffer = (querybuf *) malloc (sizeof (querybuf));
-      if (host_buffer == NULL)
-	{
-	  *errnop = ENOMEM;
-	  return NSS_STATUS_UNAVAIL;
-	}
-      use_malloc = 1;
-    }
-  else
-    host_buffer = (querybuf *) alloca (sizeof (querybuf));
+  host_buffer = orig_host_buffer = (querybuf *) alloca (1024);
 
-  n = res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer->buf,
-		  sizeof (host_buffer->buf));
+  n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer->buf,
+			 1024, (u_char **) &host_buffer);
   if (n < 0 && af == AF_INET6)
     {
       strcpy (qp, "ip6.int");
-      n = res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer->buf,
-		      sizeof (host_buffer->buf));
+      n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer->buf,
+			     host_buffer != orig_host_buffer
+			     ? MAXPACKET : 1024, (u_char **) &host_buffer);
     }
   if (n < 0)
     {
       *h_errnop = h_errno;
       __set_errno (olderr);
-      if (use_malloc)
+      if (host_buffer != orig_host_buffer)
 	free (host_buffer);
       return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
     }
 
   status = getanswer_r (host_buffer, n, qbuf, T_PTR, result, buffer, buflen,
 			errnop, h_errnop, 0 /* XXX */);
-  if (use_malloc)
+  if (host_buffer != orig_host_buffer)
     free (host_buffer);
   if (status != NSS_STATUS_SUCCESS)
     {
diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c
index 9a366aca65..5956c84382 100644
--- a/resolv/nss_dns/dns-network.c
+++ b/resolv/nss_dns/dns-network.c
@@ -110,8 +110,8 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
 			 int *herrnop)
 {
   /* Return entry for network with NAME.  */
-  querybuf *net_buffer;
-  int anslen, use_malloc = 0;
+  querybuf *net_buffer, *orig_net_buffer;
+  int anslen;
   char *qbuf;
   enum nss_status status;
 
@@ -120,26 +120,15 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
 
   qbuf = strdupa (name);
 
-  if (!__libc_use_alloca (MAXPACKET))
-    {
-      net_buffer = (querybuf *) malloc (sizeof (querybuf));
-      if (net_buffer == NULL)
-	{
-	  *errnop = ENOMEM;
-	  return NSS_STATUS_UNAVAIL;
-	}
-      use_malloc = 1;
-    }
-  else
-    net_buffer = (querybuf *) alloca (sizeof (querybuf));
+  net_buffer = orig_net_buffer = (querybuf *) alloca (1024);
 
-  anslen = res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer->buf,
-			sizeof (net_buffer->buf));
+  anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer->buf,
+			       1024, (u_char **) &net_buffer);
   if (anslen < 0)
     {
       /* Nothing found.  */
       *errnop = errno;
-      if (use_malloc)
+      if (net_buffer != orig_net_buffer)
 	free (net_buffer);
       return (errno == ECONNREFUSED
 	      || errno == EPFNOSUPPORT
@@ -148,7 +137,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
     }
 
   status = getanswer_r (net_buffer, anslen, result, buffer, buflen, BYNAME);
-  if (use_malloc)
+  if (net_buffer != orig_net_buffer)
     free (net_buffer);
   return status;
 }
@@ -161,10 +150,10 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
 {
   /* Return entry for network with NAME.  */
   enum nss_status status;
-  querybuf *net_buffer;
+  querybuf *net_buffer, *orig_net_buffer;
   unsigned int net_bytes[4];
   char qbuf[MAXDNAME];
-  int cnt, anslen, use_malloc = 0;
+  int cnt, anslen;
   u_int32_t net2;
   int olderr = errno;
 
@@ -201,27 +190,16 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
       break;
     }
 
-  if (!__libc_use_alloca (MAXPACKET))
-    {
-      net_buffer = (querybuf *) malloc (sizeof (querybuf));
-      if (net_buffer == NULL)
-	{
-	  *errnop = ENOMEM;
-	  return NSS_STATUS_UNAVAIL;
-	}
-      use_malloc = 1;
-    }
-  else
-    net_buffer = (querybuf *) alloca (sizeof (querybuf));
+  net_buffer = orig_net_buffer = (querybuf *) alloca (1024);
 
-  anslen = res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer->buf,
-		       sizeof (net_buffer->buf));
+  anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer->buf,
+			      1024, (u_char **) &net_buffer);
   if (anslen < 0)
     {
       /* Nothing found.  */
       int err = errno;
       __set_errno (olderr);
-      if (use_malloc)
+      if (net_buffer != orig_net_buffer)
 	free (net_buffer);
       return (err == ECONNREFUSED
 	      || err == EPFNOSUPPORT
@@ -230,7 +208,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
     }
 
   status = getanswer_r (net_buffer, anslen, result, buffer, buflen, BYADDR);
-  if (use_malloc)
+  if (net_buffer != orig_net_buffer)
     free (net_buffer);
   if (status == NSS_STATUS_SUCCESS)
     {
diff --git a/resolv/res_query.c b/resolv/res_query.c
index 968ba8b4e8..ab4e02a559 100644
--- a/resolv/res_query.c
+++ b/resolv/res_query.c
@@ -91,6 +91,13 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
 #define MAXPACKET	65536
 #endif
 
+#define QUERYSIZE	(HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1)
+
+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);
+
 /*
  * Formulate a normal query, send, and await answer.
  * Returned answer is placed in supplied buffer "answer".
@@ -102,11 +109,12 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
  * Caller must parse answer and determine whether it answers the question.
  */
 int
-res_nquery(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 buffer */
+__libc_res_nquery(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 buffer */
+		  u_char **answerp)	/* if buffer needs to be enlarged */
 {
 	u_char *buf;
 	HEADER *hp = (HEADER *) answer;
@@ -114,15 +122,7 @@ res_nquery(res_state statp,
 
 	hp->rcode = NOERROR;	/* default */
 
-	if (!__libc_use_alloca (MAXPACKET)) {
-		buf = malloc (MAXPACKET);
-		if (buf == NULL) {
-			__set_h_errno (NETDB_INTERNAL);
-			return -1;
-		}
-		use_malloc = 1;
-	} else
-		buf = alloca (MAXPACKET);
+	buf = alloca (QUERYSIZE);
 
 #ifdef DEBUG
 	if (statp->options & RES_DEBUG)
@@ -130,8 +130,18 @@ res_nquery(res_state statp,
 #endif
 
 	n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
-			 buf, MAXPACKET);
-	if (n <= 0) {
+			 buf, QUERYSIZE);
+	if (__builtin_expect (n <= 0, 0)) {
+		/* Retry just in case res_nmkquery failed because of too
+		   short buffer.  Shouldn't happen.  */
+		buf = malloc (MAXPACKET);
+		if (buf != NULL) {
+			use_malloc = 1;
+			n = res_nmkquery(statp, QUERY, name, class, type, NULL,
+					 0, NULL, buf, MAXPACKET);
+		}		
+	}
+	if (__builtin_expect (n <= 0, 0)) {
 #ifdef DEBUG
 		if (statp->options & RES_DEBUG)
 			printf(";; res_query: mkquery failed\n");
@@ -141,7 +151,7 @@ res_nquery(res_state statp,
 			free (buf);
 		return (n);
 	}
-	n = res_nsend(statp, buf, n, answer, anslen);
+	n = __libc_res_nsend(statp, buf, n, answer, anslen, answerp);
 	if (use_malloc)
 		free (buf);
 	if (n < 0) {
@@ -181,6 +191,17 @@ res_nquery(res_state statp,
 	return (n);
 }
 
+int
+res_nquery(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 buffer */
+{
+	return __libc_res_nquery(statp, name, class, type, answer, anslen,
+				 NULL);
+}
+
 /*
  * Formulate a normal query, send, and retrieve answer in supplied buffer.
  * Return the size of the response on success, -1 on error.
@@ -188,11 +209,12 @@ res_nquery(res_state statp,
  * is detected.  Error code, if any, is left in H_ERRNO.
  */
 int
-res_nsearch(res_state statp,
+__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 */
+	    int anslen,		/* size of answer */
+	    u_char **answerp)
 {
 	const char *cp, * const *domain;
 	HEADER *hp = (HEADER *) answer;
@@ -214,7 +236,8 @@ 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 (res_nquery(statp, cp, class, type, answer, anslen));
+		return (__libc_res_nquery(statp, cp, class, type, answer,
+					  anslen, answerp));
 
 	/*
 	 * If there are enough dots in the name, let's just give it a
@@ -223,12 +246,16 @@ res_nsearch(res_state statp,
 	 */
 	saved_herrno = -1;
 	if (dots >= statp->ndots || trailing_dot) {
-		ret = res_nquerydomain(statp, name, NULL, class, type,
-					 answer, anslen);
+		ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
+					      answer, anslen, answerp);
 		if (ret > 0 || trailing_dot)
 			return (ret);
 		saved_herrno = h_errno;
 		tried_as_is++;
+		if (answerp && *answerp != answer) {
+			answer = *answerp;
+			anslen = MAXPACKET;
+		}
 	}
 
 	/*
@@ -249,12 +276,17 @@ res_nsearch(res_state statp,
 			    (domain[0][0] == '.' && domain[0][1] == '\0'))
 				root_on_list++;
 
-			ret = res_nquerydomain(statp, name, *domain,
-					       class, type,
-					       answer, anslen);
+			ret = __libc_res_nquerydomain(statp, name, *domain,
+						      class, type,
+						      answer, anslen, answerp);
 			if (ret > 0)
 				return (ret);
 
+			if (answerp && *answerp != answer) {
+				answer = *answerp;
+				anslen = MAXPACKET;
+			}
+
 			/*
 			 * If no server present, give up.
 			 * If name isn't found in this domain,
@@ -306,8 +338,8 @@ res_nsearch(res_state statp,
 	 * query now.
 	 */
 	if (statp->ndots && !(tried_as_is || root_on_list)) {
-		ret = res_nquerydomain(statp, name, NULL, class, type,
-				       answer, anslen);
+		ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
+					      answer, anslen, answerp);
 		if (ret > 0)
 			return (ret);
 	}
@@ -328,17 +360,29 @@ res_nsearch(res_state statp,
 	return (-1);
 }
 
+int
+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 */
+{
+	return __libc_res_nsearch(statp, name, class, type, answer,
+				  anslen, NULL);
+}
+
 /*
  * Perform a call on res_query on the concatenation of name and domain,
  * removing a trailing dot from name if domain is NULL.
  */
-int
-res_nquerydomain(res_state statp,
+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 */
+	    int anslen,			/* size of answer */
+	    u_char **answerp)
 {
 	char nbuf[MAXDNAME];
 	const char *longname = nbuf;
@@ -374,7 +418,20 @@ res_nquerydomain(res_state statp,
 		}
 		sprintf(nbuf, "%s.%s", name, domain);
 	}
-	return (res_nquery(statp, longname, class, type, answer, anslen));
+	return (__libc_res_nquery(statp, longname, class, type, answer,
+				  anslen, answerp));
+}
+
+int
+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 */
+{
+	return __libc_res_nquerydomain(statp, name, domain, class, type,
+				       answer, anslen, NULL);
 }
 
 const char *
diff --git a/resolv/res_send.c b/resolv/res_send.c
index a163d06a0a..d237c9a537 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -85,6 +85,7 @@ static const char rcsid[] = "$BINDId: res_send.c,v 8.38 2000/03/30 20:16:51 vixi
 #include <netinet/in.h>
 #include <arpa/nameser.h>
 #include <arpa/inet.h>
+#include <sys/ioctl.h>
 
 #include <errno.h>
 #include <netdb.h>
@@ -95,6 +96,12 @@ static const char rcsid[] = "$BINDId: res_send.c,v 8.38 2000/03/30 20:16:51 vixi
 #include <string.h>
 #include <unistd.h>
 
+#if PACKETSZ > 65536
+#define MAXPACKET       PACKETSZ
+#else
+#define MAXPACKET       65536
+#endif
+
 #ifndef _LIBC
 #include <isc/eventlib.h>
 #else
@@ -193,10 +200,10 @@ static const int highestFD = FD_SETSIZE - 1;
 /* Forward. */
 
 static int		send_vc(res_state, const u_char *, int,
-				u_char *, int, int *, int);
+				u_char **, int *, int *, int, u_char **);
 static int		send_dg(res_state, const u_char *, int,
-				u_char *, int, int *, int,
-				int *, int *);
+				u_char **, int *, int *, int,
+				int *, int *, u_char **);
 #ifdef DEBUG
 static void		Aerror(const res_state, FILE *, const char *, int,
 			       struct sockaddr_in);
@@ -371,8 +378,8 @@ res_queriesmatch(const u_char *buf1, const u_char *eom1,
 }
 
 int
-res_nsend(res_state statp,
-	  const u_char *buf, int buflen, u_char *ans, int anssiz)
+__libc_res_nsend(res_state statp, const u_char *buf, int buflen,
+		 u_char *ans, int anssiz, u_char **ansp)
 {
 	int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
 
@@ -380,10 +387,22 @@ res_nsend(res_state statp,
 		__set_errno (ESRCH);
 		return (-1);
 	}
+
 	if (anssiz < 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;
+	}
+
 	DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
 		(stdout, ";; res_send()\n"), buf, buflen);
 	v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
@@ -578,8 +597,8 @@ res_nsend(res_state statp,
 		if (v_circuit) {
 			/* Use VC; at most one attempt per server. */
 			try = statp->retry;
-			n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
-				    ns);
+			n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno,
+				    ns, ansp);
 			if (n < 0)
 				return (-1);
 			if (n == 0)
@@ -587,8 +606,8 @@ res_nsend(res_state statp,
 			resplen = n;
 		} else {
 			/* Use datagrams. */
-			n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
-				    ns, &v_circuit, &gotsomewhere);
+			n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno,
+				    ns, &v_circuit, &gotsomewhere, ansp);
 			if (n < 0)
 				return (-1);
 			if (n == 0)
@@ -667,14 +686,23 @@ res_nsend(res_state statp,
 	return (-1);
 }
 
+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);
+}
+
 /* Private */
 
 static int
 send_vc(res_state statp,
-	const u_char *buf, int buflen, u_char *ans, int anssiz,
-	int *terrno, int ns)
+	const u_char *buf, int buflen, u_char **ansp, int *anssizp,
+	int *terrno, int ns, u_char **anscp)
 {
 	const HEADER *hp = (HEADER *) buf;
+	u_char *ans = *ansp;
+	int anssiz = *anssizp;
 	HEADER *anhp = (HEADER *) ans;
 #ifdef _LIBC
 	struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
@@ -782,11 +810,26 @@ send_vc(res_state statp,
 	}
 	resplen = ns_get16(ans);
 	if (resplen > anssiz) {
-		Dprint(statp->options & RES_DEBUG,
-		       (stdout, ";; response truncated\n")
-		       );
-		truncating = 1;
-		len = anssiz;
+		if (anscp) {
+			ans = malloc (MAXPACKET);
+			if (ans == NULL) {
+				*terrno = ENOMEM;
+				res_nclose(statp);
+				return (0);
+			}
+			anssiz = MAXPACKET;
+			*anssizp = MAXPACKET;
+			*ansp = ans;
+			*anscp = ans;
+			anhp = (HEADER *) ans;
+			len = resplen;
+		} else {
+			Dprint(statp->options & RES_DEBUG,
+				(stdout, ";; response truncated\n")
+			);
+			truncating = 1;
+			len = anssiz;
+		}
 	} else
 		len = resplen;
 	if (len < HFIXEDSZ) {
@@ -851,10 +894,12 @@ send_vc(res_state statp,
 
 static int
 send_dg(res_state statp,
-	const u_char *buf, int buflen, u_char *ans, int anssiz,
-	int *terrno, int ns, int *v_circuit, int *gotsomewhere)
+	const u_char *buf, int buflen, u_char **ansp, int *anssizp,
+	int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp)
 {
 	const HEADER *hp = (HEADER *) buf;
+	u_char *ans = *ansp;
+	int anssiz = *anssizp;
 	HEADER *anhp = (HEADER *) ans;
 #ifdef _LIBC
 	struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
@@ -992,6 +1037,21 @@ send_dg(res_state statp,
 #else
 	fromlen = sizeof(struct sockaddr_in);
 #endif
+	if (anssiz < MAXPACKET
+	    && anscp
+	    && (ioctl (s, FIONREAD, &resplen) < 0
+		|| anssiz < resplen)) {
+		ans = malloc (MAXPACKET);
+		if (ans == NULL)
+			ans = *ansp;
+		else {
+			anssiz = MAXPACKET;
+			*anssizp = MAXPACKET;
+			*ansp = ans;
+			*anscp = ans;
+			anhp = (HEADER *) ans;
+		}
+	}
 	resplen = recvfrom(s, (char*)ans, anssiz,0,
 			   (struct sockaddr *)&from, &fromlen);
 	if (resplen <= 0) {