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 (__glibc_unlikely (qdcount != 1))
    {
      *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;

  if (ancount == 0)
    {
      *h_errnop = HOST_NOT_FOUND;
      return NSS_STATUS_NOTFOUND;
    }

  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 (__glibc_unlikely (n < 0 || res_hnok (buffer) == 0))
	{
	  ++had_error;
	  continue;
	}
      if (*firstp && canon == NULL)
	{
	  h_name = buffer;
	  buffer += h_namelen;
	  buflen -= h_namelen;
	}

      cp += n;				/* name */

      if (__glibc_unlikely (cp + 10 > end_of_message))
	{
	  ++had_error;
	  continue;
	}

      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 (end_of_message - cp < n)
	{
	  /* RDATA extends beyond the end of the packet.  */
	  ++had_error;
	  continue;
	}

      if (class != C_IN)
	{
	  cp += n;
	  continue;
	}

      if (type == T_CNAME)
	{
	  char tbuf[MAXDNAME];

	  /* A CNAME could also have a TTL entry.  */
	  if (ttlp != NULL && ttl < *ttlp)
	      *ttlp = ttl;

	  n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf);
	  if (__glibc_unlikely (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 (__glibc_unlikely (n > buflen))
		goto too_small;
	      if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
		{
		  ++had_error;
		  continue;
		}

	      canon = buffer;
	      buffer = __mempcpy (buffer, tbuf, n);
	      buflen -= n;
	      h_namelen = 0;
	    }
	  continue;
	}

      /* Stop parsing if we encounter a record with incorrect RDATA
	 length.  */
      if (type == T_A || type == T_AAAA)
	{
	  if (n != rrtype_to_rdata_length (type))
	    {
	      ++had_error;
	      continue;
	    }
	}
      else
	{
	  /* Skip unknown records.  */
	  cp += n;
	  continue;
	}

      assert (type == T_A || type == T_AAAA);
      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))
	    goto too_small;

	  *pat = (struct gaih_addrtuple *) buffer;
	  buffer += sizeof (struct gaih_addrtuple);
	  buflen -= sizeof (struct gaih_addrtuple);
	}

      (*pat)->name = NULL;
      (*pat)->next = NULL;

      if (*firstp)
	{
	  /* We compose a single hostent out of the entire chain of
	     entries, so the TTL of the hostent is essentially the lowest
	     TTL in the chain.  */
	  if (ttlp != NULL && ttl < *ttlp)
	    *ttlp = ttl;

	  (*pat)->name = canon ?: 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.  */
  if (canon != NULL)
    {
      *h_errnop = HOST_NOT_FOUND;
      return NSS_STATUS_NOTFOUND;
    }

  *h_errnop = NETDB_INTERNAL;
  return NSS_STATUS_TRYAGAIN;
}