1feee8
commit 9abc40d9b514fc51cd1a052d32d092a827c6e21a
1feee8
Author: Florian Weimer <fweimer@redhat.com>
1feee8
Date:   Tue Aug 30 10:02:49 2022 +0200
1feee8
1feee8
    nss_dns: Rewrite getanswer_r to match getanswer_ptr (bug 12154, bug 29305)
1feee8
    
1feee8
    Allocate the pointer arrays only at the end, when their sizes
1feee8
    are known.  This addresses bug 29305.
1feee8
    
1feee8
    Skip over invalid names instead of failing lookups.  This partially
1feee8
    fixes bug 12154 (for gethostbyname, fixing getaddrinfo requires
1feee8
    different changes).
1feee8
    
1feee8
    Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
1feee8
    (cherry picked from commit d101d836e7e4bd1d4e4972b0e0bd0a55c9b650fa)
1feee8
1feee8
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
1feee8
index f8dc5a0a7d1eb156..10c21e1e827cde12 100644
1feee8
--- a/resolv/nss_dns/dns-host.c
1feee8
+++ b/resolv/nss_dns/dns-host.c
1feee8
@@ -108,12 +108,19 @@ typedef union querybuf
1feee8
   u_char buf[MAXPACKET];
1feee8
 } querybuf;
1feee8
 
1feee8
-static enum nss_status getanswer_r (struct resolv_context *ctx,
1feee8
-				    const querybuf *answer, int anslen,
1feee8
-				    const char *qname, int qtype,
1feee8
-				    struct hostent *result, char *buffer,
1feee8
-				    size_t buflen, int *errnop, int *h_errnop,
1feee8
-				    int32_t *ttlp, char **canonp);
1feee8
+/* For historic reasons, pointers to IP addresses are char *, so use a
1feee8
+   single list type for addresses and host names.  */
1feee8
+#define DYNARRAY_STRUCT ptrlist
1feee8
+#define DYNARRAY_ELEMENT char *
1feee8
+#define DYNARRAY_PREFIX ptrlist_
1feee8
+#include <malloc/dynarray-skeleton.c>
1feee8
+
1feee8
+static enum nss_status getanswer_r (unsigned char *packet, size_t packetlen,
1feee8
+				    uint16_t qtype, struct alloc_buffer *abuf,
1feee8
+				    struct ptrlist *addresses,
1feee8
+				    struct ptrlist *aliases,
1feee8
+				    int *errnop, int *h_errnop, int32_t *ttlp);
1feee8
+static void addrsort (struct resolv_context *ctx, char **ap, int num);
1feee8
 static enum nss_status getanswer_ptr (unsigned char *packet, size_t packetlen,
1feee8
 				      struct alloc_buffer *abuf,
1feee8
 				      char **hnamep, int *errnop,
1feee8
@@ -185,12 +192,6 @@ gethostbyname3_context (struct resolv_context *ctx,
1feee8
 			char *buffer, size_t buflen, int *errnop,
1feee8
 			int *h_errnop, int32_t *ttlp, char **canonp)
1feee8
 {
1feee8
-  union
1feee8
-  {
1feee8
-    querybuf *buf;
1feee8
-    u_char *ptr;
1feee8
-  } host_buffer;
1feee8
-  querybuf *orig_host_buffer;
1feee8
   char tmp[NS_MAXDNAME];
1feee8
   int size, type, n;
1feee8
   const char *cp;
1feee8
@@ -224,10 +225,12 @@ gethostbyname3_context (struct resolv_context *ctx,
1feee8
       && (cp = __res_context_hostalias (ctx, name, tmp, sizeof (tmp))) != NULL)
1feee8
     name = cp;
1feee8
 
1feee8
-  host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
1feee8
+  unsigned char dns_packet_buffer[1024];
1feee8
+  unsigned char *alt_dns_packet_buffer = dns_packet_buffer;
1feee8
 
1feee8
-  n = __res_context_search (ctx, name, C_IN, type, host_buffer.buf->buf,
1feee8
-			    1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
1feee8
+  n = __res_context_search (ctx, name, C_IN, type,
1feee8
+			    dns_packet_buffer, sizeof (dns_packet_buffer),
1feee8
+			    &alt_dns_packet_buffer, NULL, NULL, NULL, NULL);
1feee8
   if (n < 0)
1feee8
     {
1feee8
       switch (errno)
1feee8
@@ -256,12 +259,77 @@ gethostbyname3_context (struct resolv_context *ctx,
1feee8
 	__set_errno (olderr);
1feee8
     }
1feee8
   else
1feee8
-    status = getanswer_r
1feee8
-      (ctx, host_buffer.buf, n, name, type, result, buffer, buflen,
1feee8
-       errnop, h_errnop, ttlp, canonp);
1feee8
+    {
1feee8
+      struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen);
1feee8
 
1feee8
-  if (host_buffer.buf != orig_host_buffer)
1feee8
-    free (host_buffer.buf);
1feee8
+      struct ptrlist addresses;
1feee8
+      ptrlist_init (&addresses);
1feee8
+      struct ptrlist aliases;
1feee8
+      ptrlist_init (&aliases);
1feee8
+
1feee8
+      status = getanswer_r (alt_dns_packet_buffer, n, type,
1feee8
+			    &abuf, &addresses, &aliases,
1feee8
+			    errnop, h_errnop, ttlp);
1feee8
+      if (status == NSS_STATUS_SUCCESS)
1feee8
+	{
1feee8
+	  if (ptrlist_has_failed (&addresses)
1feee8
+	      || ptrlist_has_failed (&aliases))
1feee8
+	    {
1feee8
+	      /* malloc failure.  Do not retry using the ERANGE protocol.  */
1feee8
+	      *errnop = ENOMEM;
1feee8
+	      *h_errnop = NETDB_INTERNAL;
1feee8
+	      status = NSS_STATUS_UNAVAIL;
1feee8
+	    }
1feee8
+
1feee8
+	  /* Reserve the address and alias arrays in the result
1feee8
+	     buffer.  Both are NULL-terminated, but the first element
1feee8
+	     of the alias array is stored in h_name, so no extra space
1feee8
+	     for the NULL terminator is needed there.  */
1feee8
+	  result->h_addr_list
1feee8
+	    = alloc_buffer_alloc_array (&abuf, char *,
1feee8
+					ptrlist_size (&addresses) + 1);
1feee8
+	  result->h_aliases
1feee8
+	    = alloc_buffer_alloc_array (&abuf, char *,
1feee8
+					ptrlist_size (&aliases));
1feee8
+	  if (alloc_buffer_has_failed (&abuf))
1feee8
+	    {
1feee8
+	      /* Retry using the ERANGE protocol.  */
1feee8
+	      *errnop = ERANGE;
1feee8
+	      *h_errnop = NETDB_INTERNAL;
1feee8
+	      status = NSS_STATUS_TRYAGAIN;
1feee8
+	    }
1feee8
+	  else
1feee8
+	    {
1feee8
+	      /* Copy the address list and NULL-terminate it.  */
1feee8
+	      memcpy (result->h_addr_list, ptrlist_begin (&addresses),
1feee8
+		      ptrlist_size (&addresses) * sizeof (char *));
1feee8
+	      result->h_addr_list[ptrlist_size (&addresses)] = NULL;
1feee8
+
1feee8
+	      /* Sort the address list if requested.  */
1feee8
+	      if (type == T_A && __resolv_context_sort_count (ctx) > 0)
1feee8
+		addrsort (ctx, result->h_addr_list, ptrlist_size (&addresses));
1feee8
+
1feee8
+	      /* Copy the aliases,  excluding the last one. */
1feee8
+	      memcpy (result->h_aliases, ptrlist_begin (&aliases),
1feee8
+		      (ptrlist_size (&aliases) - 1) * sizeof (char *));
1feee8
+	      result->h_aliases[ptrlist_size (&aliases) - 1] = NULL;
1feee8
+
1feee8
+	      /* The last alias goes into h_name.  */
1feee8
+	      assert (ptrlist_size (&aliases) >= 1);
1feee8
+	      result->h_name = ptrlist_end (&aliases)[-1];
1feee8
+
1feee8
+	      /* This is also the canonical name.  */
1feee8
+	      if (canonp != NULL)
1feee8
+		*canonp = result->h_name;
1feee8
+	    }
1feee8
+	}
1feee8
+
1feee8
+      ptrlist_free (&aliases);
1feee8
+      ptrlist_free (&addresses);
1feee8
+    }
1feee8
+
1feee8
+  if (alt_dns_packet_buffer != dns_packet_buffer)
1feee8
+    free (alt_dns_packet_buffer);
1feee8
   return status;
1feee8
 }
1feee8
 
1feee8
@@ -615,314 +683,128 @@ addrsort (struct resolv_context *ctx, char **ap, int num)
1feee8
 	break;
1feee8
 }
1feee8
 
1feee8
-static enum nss_status
1feee8
-getanswer_r (struct resolv_context *ctx,
1feee8
-	     const querybuf *answer, int anslen, const char *qname, int qtype,
1feee8
-	     struct hostent *result, char *buffer, size_t buflen,
1feee8
-	     int *errnop, int *h_errnop, int32_t *ttlp, char **canonp)
1feee8
+/* Convert the uncompressed, binary domain name CDNAME into its
1feee8
+   textual representation and add it to the end of ALIASES, allocating
1feee8
+   space for a copy of the name from ABUF.  Skip adding the name if it
1feee8
+   is not a valid host name, and return false in that case, otherwise
1feee8
+   true.  */
1feee8
+static bool
1feee8
+getanswer_r_store_alias (const unsigned char *cdname,
1feee8
+			 struct alloc_buffer *abuf,
1feee8
+			 struct ptrlist *aliases)
1feee8
 {
1feee8
-  struct host_data
1feee8
-  {
1feee8
-    char *aliases[MAX_NR_ALIASES];
1feee8
-    unsigned char host_addr[16];	/* IPv4 or IPv6 */
1feee8
-    char *h_addr_ptrs[0];
1feee8
-  } *host_data;
1feee8
-  int linebuflen;
1feee8
-  const HEADER *hp;
1feee8
-  const u_char *end_of_message, *cp;
1feee8
-  int n, ancount, qdcount;
1feee8
-  int haveanswer, had_error;
1feee8
-  char *bp, **ap, **hap;
1feee8
-  char tbuf[MAXDNAME];
1feee8
-  u_char packtmp[NS_MAXCDNAME];
1feee8
-  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
1feee8
-  buffer += pad;
1feee8
-  buflen = buflen > pad ? buflen - pad : 0;
1feee8
-  if (__glibc_unlikely (buflen < sizeof (struct host_data)))
1feee8
-    {
1feee8
-      /* The buffer is too small.  */
1feee8
-    too_small:
1feee8
-      *errnop = ERANGE;
1feee8
-      *h_errnop = NETDB_INTERNAL;
1feee8
-      return NSS_STATUS_TRYAGAIN;
1feee8
-    }
1feee8
-  host_data = (struct host_data *) buffer;
1feee8
-  linebuflen = buflen - sizeof (struct host_data);
1feee8
-  if (buflen - sizeof (struct host_data) != linebuflen)
1feee8
-    linebuflen = INT_MAX;
1feee8
-
1feee8
-  result->h_name = NULL;
1feee8
-  end_of_message = answer->buf + anslen;
1feee8
-
1feee8
-  /*
1feee8
-   * find first satisfactory answer
1feee8
-   */
1feee8
-  hp = &answer->hdr;
1feee8
-  ancount = ntohs (hp->ancount);
1feee8
-  qdcount = ntohs (hp->qdcount);
1feee8
-  cp = answer->buf + HFIXEDSZ;
1feee8
-  if (__glibc_unlikely (qdcount != 1))
1feee8
-    {
1feee8
-      *h_errnop = NO_RECOVERY;
1feee8
-      return NSS_STATUS_UNAVAIL;
1feee8
-    }
1feee8
-  if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen)
1feee8
-    goto too_small;
1feee8
-  bp = (char *) &host_data->h_addr_ptrs[ancount + 1];
1feee8
-  linebuflen -= (ancount + 1) * sizeof (char *);
1feee8
-
1feee8
-  n = __ns_name_unpack (answer->buf, end_of_message, cp,
1feee8
-			packtmp, sizeof packtmp);
1feee8
-  if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
1feee8
-    {
1feee8
-      if (__glibc_unlikely (errno == EMSGSIZE))
1feee8
-	goto too_small;
1feee8
-
1feee8
-      n = -1;
1feee8
-    }
1feee8
+  /* Filter out domain names that are not host names.  */
1feee8
+  if (!__res_binary_hnok (cdname))
1feee8
+    return false;
1feee8
+
1feee8
+  /* Note: Not NS_MAXCDNAME, so that __ns_name_ntop implicitly checks
1feee8
+     for length.  */
1feee8
+  char dname[MAXHOSTNAMELEN + 1];
1feee8
+  if (__ns_name_ntop (cdname, dname, sizeof (dname)) < 0)
1feee8
+    return false;
1feee8
+  /* Do not report an error on allocation failure, instead store NULL
1feee8
+     or do nothing.  getanswer_r's caller will see NSS_STATUS_SUCCESS
1feee8
+     and detect the memory allocation failure or buffer space
1feee8
+     exhaustion, and report it accordingly.  */
1feee8
+  ptrlist_add (aliases, alloc_buffer_copy_string (abuf, dname));
1feee8
+  return true;
1feee8
+}
1feee8
 
1feee8
-  if (__glibc_unlikely (n < 0))
1feee8
-    {
1feee8
-      *errnop = errno;
1feee8
-      *h_errnop = NO_RECOVERY;
1feee8
-      return NSS_STATUS_UNAVAIL;
1feee8
-    }
1feee8
-  if (__glibc_unlikely (__libc_res_hnok (bp) == 0))
1feee8
+static enum nss_status __attribute__ ((noinline))
1feee8
+getanswer_r (unsigned char *packet, size_t packetlen, uint16_t qtype,
1feee8
+	     struct alloc_buffer *abuf,
1feee8
+	     struct ptrlist *addresses, struct ptrlist *aliases,
1feee8
+	     int *errnop, int *h_errnop, int32_t *ttlp)
1feee8
+{
1feee8
+  struct ns_rr_cursor c;
1feee8
+  if (!__ns_rr_cursor_init (&c, packet, packetlen))
1feee8
     {
1feee8
-      errno = EBADMSG;
1feee8
-      *errnop = EBADMSG;
1feee8
+      /* This should not happen because __res_context_query already
1feee8
+	 perfroms response validation.  */
1feee8
       *h_errnop = NO_RECOVERY;
1feee8
       return NSS_STATUS_UNAVAIL;
1feee8
     }
1feee8
-  cp += n + QFIXEDSZ;
1feee8
 
1feee8
-  if (qtype == T_A || qtype == T_AAAA)
1feee8
+  /* Treat the QNAME just like an alias.  Error out if it is not a
1feee8
+     valid host name.  */
1feee8
+  if (ns_rr_cursor_rcode (&c) == NXDOMAIN
1feee8
+      || !getanswer_r_store_alias (ns_rr_cursor_qname (&c), abuf, aliases))
1feee8
     {
1feee8
-      /* res_send() has already verified that the query name is the
1feee8
-       * same as the one we sent; this just gets the expanded name
1feee8
-       * (i.e., with the succeeding search-domain tacked on).
1feee8
-       */
1feee8
-      n = strlen (bp) + 1;             /* for the \0 */
1feee8
-      if (n >= MAXHOSTNAMELEN)
1feee8
-	{
1feee8
-	  *h_errnop = NO_RECOVERY;
1feee8
-	  *errnop = ENOENT;
1feee8
-	  return NSS_STATUS_TRYAGAIN;
1feee8
-	}
1feee8
-      result->h_name = bp;
1feee8
-      bp += n;
1feee8
-      linebuflen -= n;
1feee8
-      if (linebuflen < 0)
1feee8
-	goto too_small;
1feee8
-      /* The qname can be abbreviated, but h_name is now absolute. */
1feee8
-      qname = result->h_name;
1feee8
+      if (ttlp != NULL)
1feee8
+	/* No negative caching.  */
1feee8
+	*ttlp = 0;
1feee8
+      *h_errnop = HOST_NOT_FOUND;
1feee8
+      *errnop = ENOENT;
1feee8
+      return NSS_STATUS_NOTFOUND;
1feee8
     }
1feee8
 
1feee8
-  ap = host_data->aliases;
1feee8
-  *ap = NULL;
1feee8
-  result->h_aliases = host_data->aliases;
1feee8
-  hap = host_data->h_addr_ptrs;
1feee8
-  *hap = NULL;
1feee8
-  result->h_addr_list = host_data->h_addr_ptrs;
1feee8
-  haveanswer = 0;
1feee8
-  had_error = 0;
1feee8
+  int ancount = ns_rr_cursor_ancount (&c);
1feee8
+  const unsigned char *expected_name = ns_rr_cursor_qname (&c);
1feee8
+  /* expected_name may be updated to point into this buffer.  */
1feee8
+  unsigned char name_buffer[NS_MAXCDNAME];
1feee8
 
1feee8
-  while (ancount-- > 0 && cp < end_of_message && had_error == 0)
1feee8
+  for (; ancount > 0; --ancount)
1feee8
     {
1feee8
-      int type, class;
1feee8
-
1feee8
-      n = __ns_name_unpack (answer->buf, end_of_message, cp,
1feee8
-			    packtmp, sizeof packtmp);
1feee8
-      if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
1feee8
-	{
1feee8
-	  if (__glibc_unlikely (errno == EMSGSIZE))
1feee8
-	    goto too_small;
1feee8
-
1feee8
-	  n = -1;
1feee8
-	}
1feee8
-
1feee8
-      if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0))
1feee8
-	{
1feee8
-	  ++had_error;
1feee8
-	  continue;
1feee8
-	}
1feee8
-      cp += n;				/* name */
1feee8
-
1feee8
-      if (__glibc_unlikely (cp + 10 > end_of_message))
1feee8
+      struct ns_rr_wire rr;
1feee8
+      if (!__ns_rr_cursor_next (&c, &rr))
1feee8
 	{
1feee8
-	  ++had_error;
1feee8
-	  continue;
1feee8
+	  *h_errnop = NO_RECOVERY;
1feee8
+	  return NSS_STATUS_UNAVAIL;
1feee8
 	}
1feee8
 
1feee8
-      NS_GET16 (type, cp);
1feee8
-      NS_GET16 (class, cp);
1feee8
-      int32_t ttl;
1feee8
-      NS_GET32 (ttl, cp);
1feee8
-      NS_GET16 (n, cp);		/* RDATA length.  */
1feee8
-
1feee8
-      if (end_of_message - cp < n)
1feee8
-	{
1feee8
-	  /* RDATA extends beyond the end of the packet.  */
1feee8
-	  ++had_error;
1feee8
-	  continue;
1feee8
-	}
1feee8
+      /* Skip over records with the wrong class.  */
1feee8
+      if (rr.rclass != C_IN)
1feee8
+	continue;
1feee8
 
1feee8
-      if (__glibc_unlikely (class != C_IN))
1feee8
-	{
1feee8
-	  /* XXX - debug? syslog? */
1feee8
-	  cp += n;
1feee8
-	  continue;			/* XXX - had_error++ ? */
1feee8
-	}
1feee8
+      /* Update TTL for recognized record types.  */
1feee8
+      if ((rr.rtype == T_CNAME || rr.rtype == qtype)
1feee8
+	  && ttlp != NULL && *ttlp > rr.ttl)
1feee8
+	*ttlp = rr.ttl;
1feee8
 
1feee8
-      if (type == T_CNAME)
1feee8
+      if (rr.rtype == T_CNAME)
1feee8
 	{
1feee8
-	  /* A CNAME could also have a TTL entry.  */
1feee8
-	  if (ttlp != NULL && ttl < *ttlp)
1feee8
-	      *ttlp = ttl;
1feee8
-
1feee8
-	  if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1])
1feee8
-	    continue;
1feee8
-	  n = __libc_dn_expand (answer->buf, end_of_message, cp,
1feee8
-				tbuf, sizeof tbuf);
1feee8
-	  if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0))
1feee8
-	    {
1feee8
-	      ++had_error;
1feee8
-	      continue;
1feee8
-	    }
1feee8
-	  cp += n;
1feee8
-	  /* Store alias.  */
1feee8
-	  *ap++ = bp;
1feee8
-	  n = strlen (bp) + 1;		/* For the \0.  */
1feee8
-	  if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
1feee8
-	    {
1feee8
-	      ++had_error;
1feee8
-	      continue;
1feee8
-	    }
1feee8
-	  bp += n;
1feee8
-	  linebuflen -= n;
1feee8
-	  /* Get canonical name.  */
1feee8
-	  n = strlen (tbuf) + 1;	/* For the \0.  */
1feee8
-	  if (__glibc_unlikely (n > linebuflen))
1feee8
-	    goto too_small;
1feee8
-	  if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
1feee8
+	  /* NB: No check for owner name match, based on historic
1feee8
+	     precedent.  Record the CNAME target as the new expected
1feee8
+	     name.  */
1feee8
+	  int n = __ns_name_unpack (c.begin, c.end, rr.rdata,
1feee8
+				    name_buffer, sizeof (name_buffer));
1feee8
+	  if (n < 0)
1feee8
 	    {
1feee8
-	      ++had_error;
1feee8
-	      continue;
1feee8
+	      *h_errnop = NO_RECOVERY;
1feee8
+	      return NSS_STATUS_UNAVAIL;
1feee8
 	    }
1feee8
-	  result->h_name = bp;
1feee8
-	  bp = __mempcpy (bp, tbuf, n);	/* Cannot overflow.  */
1feee8
-	  linebuflen -= n;
1feee8
-	  continue;
1feee8
+	  /* And store the new name as an alias.  */
1feee8
+	  getanswer_r_store_alias (name_buffer, abuf, aliases);
1feee8
+	  expected_name = name_buffer;
1feee8
 	}
1feee8
-
1feee8
-      if (__glibc_unlikely (type != qtype))
1feee8
+      else if (rr.rtype == qtype
1feee8
+	       && __ns_samebinaryname (rr.rname, expected_name)
1feee8
+	       && rr.rdlength == rrtype_to_rdata_length (qtype))
1feee8
 	{
1feee8
-	  cp += n;
1feee8
-	  continue;			/* XXX - had_error++ ? */
1feee8
+	  /* Make a copy of the address and store it.  Increase the
1feee8
+	     alignment to 4, in case there are applications out there
1feee8
+	     that expect at least this level of address alignment.  */
1feee8
+	  ptrlist_add (addresses, (char *) alloc_buffer_next (abuf, uint32_t));
1feee8
+	  alloc_buffer_copy_bytes (abuf, rr.rdata, rr.rdlength);
1feee8
 	}
1feee8
-
1feee8
-      switch (type)
1feee8
-	{
1feee8
-	case T_A:
1feee8
-	case T_AAAA:
1feee8
-	  if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0))
1feee8
-	    {
1feee8
-	      cp += n;
1feee8
-	      continue;			/* XXX - had_error++ ? */
1feee8
-	    }
1feee8
-
1feee8
-	  /* Stop parsing at a record whose length is incorrect.  */
1feee8
-	  if (n != rrtype_to_rdata_length (type))
1feee8
-	    {
1feee8
-	      ++had_error;
1feee8
-	      break;
1feee8
-	    }
1feee8
-
1feee8
-	  /* Skip records of the wrong type.  */
1feee8
-	  if (n != result->h_length)
1feee8
-	    {
1feee8
-	      cp += n;
1feee8
-	      continue;
1feee8
-	    }
1feee8
-	  if (!haveanswer)
1feee8
-	    {
1feee8
-	      int nn;
1feee8
-
1feee8
-	      /* We compose a single hostent out of the entire chain of
1feee8
-	         entries, so the TTL of the hostent is essentially the lowest
1feee8
-		 TTL in the chain.  */
1feee8
-	      if (ttlp != NULL && ttl < *ttlp)
1feee8
-		*ttlp = ttl;
1feee8
-	      if (canonp != NULL)
1feee8
-		*canonp = bp;
1feee8
-	      result->h_name = bp;
1feee8
-	      nn = strlen (bp) + 1;	/* for the \0 */
1feee8
-	      bp += nn;
1feee8
-	      linebuflen -= nn;
1feee8
-	    }
1feee8
-
1feee8
-	  /* Provide sufficient alignment for both address
1feee8
-	     families.  */
1feee8
-	  enum { align = 4 };
1feee8
-	  _Static_assert ((align % __alignof__ (struct in_addr)) == 0,
1feee8
-			  "struct in_addr alignment");
1feee8
-	  _Static_assert ((align % __alignof__ (struct in6_addr)) == 0,
1feee8
-			  "struct in6_addr alignment");
1feee8
-	  {
1feee8
-	    char *new_bp = PTR_ALIGN_UP (bp, align);
1feee8
-	    linebuflen -= new_bp - bp;
1feee8
-	    bp = new_bp;
1feee8
-	  }
1feee8
-
1feee8
-	  if (__glibc_unlikely (n > linebuflen))
1feee8
-	    goto too_small;
1feee8
-	  bp = __mempcpy (*hap++ = bp, cp, n);
1feee8
-	  cp += n;
1feee8
-	  linebuflen -= n;
1feee8
-	  break;
1feee8
-	default:
1feee8
-	  abort ();
1feee8
-	}
1feee8
-      if (had_error == 0)
1feee8
-	++haveanswer;
1feee8
     }
1feee8
 
1feee8
-  if (haveanswer > 0)
1feee8
+  if (ptrlist_size (addresses) == 0)
1feee8
     {
1feee8
-      *ap = NULL;
1feee8
-      *hap = NULL;
1feee8
-      /*
1feee8
-       * Note: we sort even if host can take only one address
1feee8
-       * in its return structures - should give it the "best"
1feee8
-       * address in that case, not some random one
1feee8
-       */
1feee8
-      if (haveanswer > 1 && qtype == T_A
1feee8
-	  && __resolv_context_sort_count (ctx) > 0)
1feee8
-	addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
1feee8
-
1feee8
-      if (result->h_name == NULL)
1feee8
-	{
1feee8
-	  n = strlen (qname) + 1;	/* For the \0.  */
1feee8
-	  if (n > linebuflen)
1feee8
-	    goto too_small;
1feee8
-	  if (n >= MAXHOSTNAMELEN)
1feee8
-	    goto no_recovery;
1feee8
-	  result->h_name = bp;
1feee8
-	  bp = __mempcpy (bp, qname, n);	/* Cannot overflow.  */
1feee8
-	  linebuflen -= n;
1feee8
-	}
1feee8
+      /* No address record found.  */
1feee8
+      if (ttlp != NULL)
1feee8
+	/* No caching of negative responses.  */
1feee8
+	*ttlp = 0;
1feee8
 
1feee8
+      *h_errnop = NO_RECOVERY;
1feee8
+      *errnop = ENOENT;
1feee8
+      return NSS_STATUS_TRYAGAIN;
1feee8
+    }
1feee8
+  else
1feee8
+    {
1feee8
       *h_errnop = NETDB_SUCCESS;
1feee8
       return NSS_STATUS_SUCCESS;
1feee8
     }
1feee8
- no_recovery:
1feee8
-  *h_errnop = NO_RECOVERY;
1feee8
-  *errnop = ENOENT;
1feee8
-  /* Special case here: if the resolver sent a result but it only
1feee8
-     contains a CNAME while we are looking for a T_A or T_AAAA record,
1feee8
-     we fail with NOTFOUND instead of TRYAGAIN.  */
1feee8
-  return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
1feee8
-	   ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
1feee8
 }
1feee8
 
1feee8
 static enum nss_status