6ca6e8
commit d9c979abf9307ef3e27dbe65317430977bb322c7
6ca6e8
Author: Florian Weimer <fweimer@redhat.com>
6ca6e8
Date:   Tue Aug 30 10:02:49 2022 +0200
6ca6e8
6ca6e8
    nss_dns: Split getanswer_ptr from getanswer_r
6ca6e8
    
6ca6e8
    And expand the use of name_ok and qtype in getanswer_ptr (the
6ca6e8
    former also in getanswer_r).
6ca6e8
    
6ca6e8
    After further cleanups, not much code will be shared between the
6ca6e8
    two functions.
6ca6e8
    
6ca6e8
    Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
6ca6e8
    (cherry picked from commit 0dcc43e9981005540bf39dc7bf33fbab62cf9e84)
6ca6e8
6ca6e8
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
6ca6e8
index 6e83fca1c5b1f98c..a6bf73a091968358 100644
6ca6e8
--- a/resolv/nss_dns/dns-host.c
6ca6e8
+++ b/resolv/nss_dns/dns-host.c
6ca6e8
@@ -117,6 +117,11 @@ static enum nss_status getanswer_r (struct resolv_context *ctx,
6ca6e8
 				    struct hostent *result, char *buffer,
6ca6e8
 				    size_t buflen, int *errnop, int *h_errnop,
6ca6e8
 				    int map, int32_t *ttlp, char **canonp);
6ca6e8
+static enum nss_status getanswer_ptr (const querybuf *answer, int anslen,
6ca6e8
+				      const char *qname,
6ca6e8
+				      struct hostent *result, char *buffer,
6ca6e8
+				      size_t buflen, int *errnop,
6ca6e8
+				      int *h_errnop, int32_t *ttlp);
6ca6e8
 
6ca6e8
 static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
6ca6e8
 				       const querybuf *answer2, int anslen2,
6ca6e8
@@ -562,9 +567,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
6ca6e8
       return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
6ca6e8
     }
6ca6e8
 
6ca6e8
-  status = getanswer_r
6ca6e8
-    (ctx, host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen,
6ca6e8
-     errnop, h_errnop, 0 /* XXX */, ttlp, NULL);
6ca6e8
+  status = getanswer_ptr (host_buffer.buf, n, qbuf, result,
6ca6e8
+			  buffer, buflen, errnop, h_errnop, ttlp);
6ca6e8
   if (host_buffer.buf != orig_host_buffer)
6ca6e8
     free (host_buffer.buf);
6ca6e8
   if (status != NSS_STATUS_SUCCESS)
6ca6e8
@@ -660,8 +664,6 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
   int haveanswer, had_error;
6ca6e8
   char *bp, **ap, **hap;
6ca6e8
   char tbuf[MAXDNAME];
6ca6e8
-  const char *tname;
6ca6e8
-  int (*name_ok) (const char *);
6ca6e8
   u_char packtmp[NS_MAXCDNAME];
6ca6e8
   int have_to_map = 0;
6ca6e8
   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
6ca6e8
@@ -680,22 +682,8 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
   if (buflen - sizeof (struct host_data) != linebuflen)
6ca6e8
     linebuflen = INT_MAX;
6ca6e8
 
6ca6e8
-  tname = qname;
6ca6e8
   result->h_name = NULL;
6ca6e8
   end_of_message = answer->buf + anslen;
6ca6e8
-  switch (qtype)
6ca6e8
-    {
6ca6e8
-    case T_A:
6ca6e8
-    case T_AAAA:
6ca6e8
-      name_ok = __libc_res_hnok;
6ca6e8
-      break;
6ca6e8
-    case T_PTR:
6ca6e8
-      name_ok = __libc_res_dnok;
6ca6e8
-      break;
6ca6e8
-    default:
6ca6e8
-      *errnop = ENOENT;
6ca6e8
-      return NSS_STATUS_UNAVAIL;  /* XXX should be abort(); */
6ca6e8
-    }
6ca6e8
 
6ca6e8
   /*
6ca6e8
    * find first satisfactory answer
6ca6e8
@@ -730,7 +718,7 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
       *h_errnop = NO_RECOVERY;
6ca6e8
       return NSS_STATUS_UNAVAIL;
6ca6e8
     }
6ca6e8
-  if (__glibc_unlikely (name_ok (bp) == 0))
6ca6e8
+  if (__glibc_unlikely (__libc_res_hnok (bp) == 0))
6ca6e8
     {
6ca6e8
       errno = EBADMSG;
6ca6e8
       *errnop = EBADMSG;
6ca6e8
@@ -784,7 +772,7 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	  n = -1;
6ca6e8
 	}
6ca6e8
 
6ca6e8
-      if (__glibc_unlikely (n < 0 || (*name_ok) (bp) == 0))
6ca6e8
+      if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0))
6ca6e8
 	{
6ca6e8
 	  ++had_error;
6ca6e8
 	  continue;
6ca6e8
@@ -817,7 +805,7 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	  continue;			/* XXX - had_error++ ? */
6ca6e8
 	}
6ca6e8
 
6ca6e8
-      if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME)
6ca6e8
+      if (type == T_CNAME)
6ca6e8
 	{
6ca6e8
 	  /* A CNAME could also have a TTL entry.  */
6ca6e8
 	  if (ttlp != NULL && ttl < *ttlp)
6ca6e8
@@ -827,7 +815,7 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	    continue;
6ca6e8
 	  n = __libc_dn_expand (answer->buf, end_of_message, cp,
6ca6e8
 				tbuf, sizeof tbuf);
6ca6e8
-	  if (__glibc_unlikely (n < 0 || (*name_ok) (tbuf) == 0))
6ca6e8
+	  if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0))
6ca6e8
 	    {
6ca6e8
 	      ++had_error;
6ca6e8
 	      continue;
6ca6e8
@@ -858,7 +846,260 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	  continue;
6ca6e8
 	}
6ca6e8
 
6ca6e8
-      if (qtype == T_PTR && type == T_CNAME)
6ca6e8
+      if (type == T_A && qtype == T_AAAA && map)
6ca6e8
+	have_to_map = 1;
6ca6e8
+      else if (__glibc_unlikely (type != qtype))
6ca6e8
+	{
6ca6e8
+	  cp += n;
6ca6e8
+	  continue;			/* XXX - had_error++ ? */
6ca6e8
+	}
6ca6e8
+
6ca6e8
+      switch (type)
6ca6e8
+	{
6ca6e8
+	case T_A:
6ca6e8
+	case T_AAAA:
6ca6e8
+	  if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0))
6ca6e8
+	    {
6ca6e8
+	      cp += n;
6ca6e8
+	      continue;			/* XXX - had_error++ ? */
6ca6e8
+	    }
6ca6e8
+
6ca6e8
+	  /* Stop parsing at a record whose length is incorrect.  */
6ca6e8
+	  if (n != rrtype_to_rdata_length (type))
6ca6e8
+	    {
6ca6e8
+	      ++had_error;
6ca6e8
+	      break;
6ca6e8
+	    }
6ca6e8
+
6ca6e8
+	  /* Skip records of the wrong type.  */
6ca6e8
+	  if (n != result->h_length)
6ca6e8
+	    {
6ca6e8
+	      cp += n;
6ca6e8
+	      continue;
6ca6e8
+	    }
6ca6e8
+	  if (!haveanswer)
6ca6e8
+	    {
6ca6e8
+	      int nn;
6ca6e8
+
6ca6e8
+	      /* We compose a single hostent out of the entire chain of
6ca6e8
+	         entries, so the TTL of the hostent is essentially the lowest
6ca6e8
+		 TTL in the chain.  */
6ca6e8
+	      if (ttlp != NULL && ttl < *ttlp)
6ca6e8
+		*ttlp = ttl;
6ca6e8
+	      if (canonp != NULL)
6ca6e8
+		*canonp = bp;
6ca6e8
+	      result->h_name = bp;
6ca6e8
+	      nn = strlen (bp) + 1;	/* for the \0 */
6ca6e8
+	      bp += nn;
6ca6e8
+	      linebuflen -= nn;
6ca6e8
+	    }
6ca6e8
+
6ca6e8
+	  /* Provide sufficient alignment for both address
6ca6e8
+	     families.  */
6ca6e8
+	  enum { align = 4 };
6ca6e8
+	  _Static_assert ((align % __alignof__ (struct in_addr)) == 0,
6ca6e8
+			  "struct in_addr alignment");
6ca6e8
+	  _Static_assert ((align % __alignof__ (struct in6_addr)) == 0,
6ca6e8
+			  "struct in6_addr alignment");
6ca6e8
+	  {
6ca6e8
+	    char *new_bp = PTR_ALIGN_UP (bp, align);
6ca6e8
+	    linebuflen -= new_bp - bp;
6ca6e8
+	    bp = new_bp;
6ca6e8
+	  }
6ca6e8
+
6ca6e8
+	  if (__glibc_unlikely (n > linebuflen))
6ca6e8
+	    goto too_small;
6ca6e8
+	  bp = __mempcpy (*hap++ = bp, cp, n);
6ca6e8
+	  cp += n;
6ca6e8
+	  linebuflen -= n;
6ca6e8
+	  break;
6ca6e8
+	default:
6ca6e8
+	  abort ();
6ca6e8
+	}
6ca6e8
+      if (had_error == 0)
6ca6e8
+	++haveanswer;
6ca6e8
+    }
6ca6e8
+
6ca6e8
+  if (haveanswer > 0)
6ca6e8
+    {
6ca6e8
+      *ap = NULL;
6ca6e8
+      *hap = NULL;
6ca6e8
+      /*
6ca6e8
+       * Note: we sort even if host can take only one address
6ca6e8
+       * in its return structures - should give it the "best"
6ca6e8
+       * address in that case, not some random one
6ca6e8
+       */
6ca6e8
+      if (haveanswer > 1 && qtype == T_A
6ca6e8
+	  && __resolv_context_sort_count (ctx) > 0)
6ca6e8
+	addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
6ca6e8
+
6ca6e8
+      if (result->h_name == NULL)
6ca6e8
+	{
6ca6e8
+	  n = strlen (qname) + 1;	/* For the \0.  */
6ca6e8
+	  if (n > linebuflen)
6ca6e8
+	    goto too_small;
6ca6e8
+	  if (n >= MAXHOSTNAMELEN)
6ca6e8
+	    goto no_recovery;
6ca6e8
+	  result->h_name = bp;
6ca6e8
+	  bp = __mempcpy (bp, qname, n);	/* Cannot overflow.  */
6ca6e8
+	  linebuflen -= n;
6ca6e8
+	}
6ca6e8
+
6ca6e8
+      if (have_to_map)
6ca6e8
+	if (map_v4v6_hostent (result, &bp, &linebuflen))
6ca6e8
+	  goto too_small;
6ca6e8
+      *h_errnop = NETDB_SUCCESS;
6ca6e8
+      return NSS_STATUS_SUCCESS;
6ca6e8
+    }
6ca6e8
+ no_recovery:
6ca6e8
+  *h_errnop = NO_RECOVERY;
6ca6e8
+  *errnop = ENOENT;
6ca6e8
+  /* Special case here: if the resolver sent a result but it only
6ca6e8
+     contains a CNAME while we are looking for a T_A or T_AAAA record,
6ca6e8
+     we fail with NOTFOUND instead of TRYAGAIN.  */
6ca6e8
+  return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
6ca6e8
+	   ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
6ca6e8
+}
6ca6e8
+
6ca6e8
+static enum nss_status
6ca6e8
+getanswer_ptr (const querybuf *answer, int anslen, const char *qname,
6ca6e8
+	       struct hostent *result, char *buffer, size_t buflen,
6ca6e8
+	       int *errnop, int *h_errnop, int32_t *ttlp)
6ca6e8
+{
6ca6e8
+  struct host_data
6ca6e8
+  {
6ca6e8
+    char *aliases[MAX_NR_ALIASES];
6ca6e8
+    unsigned char host_addr[16];	/* IPv4 or IPv6 */
6ca6e8
+    char *h_addr_ptrs[0];
6ca6e8
+  } *host_data;
6ca6e8
+  int linebuflen;
6ca6e8
+  const HEADER *hp;
6ca6e8
+  const u_char *end_of_message, *cp;
6ca6e8
+  int n, ancount, qdcount;
6ca6e8
+  int haveanswer, had_error;
6ca6e8
+  char *bp, **ap, **hap;
6ca6e8
+  char tbuf[MAXDNAME];
6ca6e8
+  const char *tname;
6ca6e8
+  u_char packtmp[NS_MAXCDNAME];
6ca6e8
+  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
6ca6e8
+  buffer += pad;
6ca6e8
+  buflen = buflen > pad ? buflen - pad : 0;
6ca6e8
+  if (__glibc_unlikely (buflen < sizeof (struct host_data)))
6ca6e8
+    {
6ca6e8
+      /* The buffer is too small.  */
6ca6e8
+    too_small:
6ca6e8
+      *errnop = ERANGE;
6ca6e8
+      *h_errnop = NETDB_INTERNAL;
6ca6e8
+      return NSS_STATUS_TRYAGAIN;
6ca6e8
+    }
6ca6e8
+  host_data = (struct host_data *) buffer;
6ca6e8
+  linebuflen = buflen - sizeof (struct host_data);
6ca6e8
+  if (buflen - sizeof (struct host_data) != linebuflen)
6ca6e8
+    linebuflen = INT_MAX;
6ca6e8
+
6ca6e8
+  tname = qname;
6ca6e8
+  result->h_name = NULL;
6ca6e8
+  end_of_message = answer->buf + anslen;
6ca6e8
+
6ca6e8
+  /*
6ca6e8
+   * find first satisfactory answer
6ca6e8
+   */
6ca6e8
+  hp = &answer->hdr;
6ca6e8
+  ancount = ntohs (hp->ancount);
6ca6e8
+  qdcount = ntohs (hp->qdcount);
6ca6e8
+  cp = answer->buf + HFIXEDSZ;
6ca6e8
+  if (__glibc_unlikely (qdcount != 1))
6ca6e8
+    {
6ca6e8
+      *h_errnop = NO_RECOVERY;
6ca6e8
+      return NSS_STATUS_UNAVAIL;
6ca6e8
+    }
6ca6e8
+  if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen)
6ca6e8
+    goto too_small;
6ca6e8
+  bp = (char *) &host_data->h_addr_ptrs[ancount + 1];
6ca6e8
+  linebuflen -= (ancount + 1) * sizeof (char *);
6ca6e8
+
6ca6e8
+  n = __ns_name_unpack (answer->buf, end_of_message, cp,
6ca6e8
+			packtmp, sizeof packtmp);
6ca6e8
+  if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
6ca6e8
+    {
6ca6e8
+      if (__glibc_unlikely (errno == EMSGSIZE))
6ca6e8
+	goto too_small;
6ca6e8
+
6ca6e8
+      n = -1;
6ca6e8
+    }
6ca6e8
+
6ca6e8
+  if (__glibc_unlikely (n < 0))
6ca6e8
+    {
6ca6e8
+      *errnop = errno;
6ca6e8
+      *h_errnop = NO_RECOVERY;
6ca6e8
+      return NSS_STATUS_UNAVAIL;
6ca6e8
+    }
6ca6e8
+  if (__glibc_unlikely (__libc_res_dnok (bp) == 0))
6ca6e8
+    {
6ca6e8
+      errno = EBADMSG;
6ca6e8
+      *errnop = EBADMSG;
6ca6e8
+      *h_errnop = NO_RECOVERY;
6ca6e8
+      return NSS_STATUS_UNAVAIL;
6ca6e8
+    }
6ca6e8
+  cp += n + QFIXEDSZ;
6ca6e8
+
6ca6e8
+  ap = host_data->aliases;
6ca6e8
+  *ap = NULL;
6ca6e8
+  result->h_aliases = host_data->aliases;
6ca6e8
+  hap = host_data->h_addr_ptrs;
6ca6e8
+  *hap = NULL;
6ca6e8
+  result->h_addr_list = host_data->h_addr_ptrs;
6ca6e8
+  haveanswer = 0;
6ca6e8
+  had_error = 0;
6ca6e8
+
6ca6e8
+  while (ancount-- > 0 && cp < end_of_message && had_error == 0)
6ca6e8
+    {
6ca6e8
+      int type, class;
6ca6e8
+
6ca6e8
+      n = __ns_name_unpack (answer->buf, end_of_message, cp,
6ca6e8
+			    packtmp, sizeof packtmp);
6ca6e8
+      if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
6ca6e8
+	{
6ca6e8
+	  if (__glibc_unlikely (errno == EMSGSIZE))
6ca6e8
+	    goto too_small;
6ca6e8
+
6ca6e8
+	  n = -1;
6ca6e8
+	}
6ca6e8
+
6ca6e8
+      if (__glibc_unlikely (n < 0 || __libc_res_dnok (bp) == 0))
6ca6e8
+	{
6ca6e8
+	  ++had_error;
6ca6e8
+	  continue;
6ca6e8
+	}
6ca6e8
+      cp += n;				/* name */
6ca6e8
+
6ca6e8
+      if (__glibc_unlikely (cp + 10 > end_of_message))
6ca6e8
+	{
6ca6e8
+	  ++had_error;
6ca6e8
+	  continue;
6ca6e8
+	}
6ca6e8
+
6ca6e8
+      NS_GET16 (type, cp);
6ca6e8
+      NS_GET16 (class, cp);
6ca6e8
+      int32_t ttl;
6ca6e8
+      NS_GET32 (ttl, cp);
6ca6e8
+      NS_GET16 (n, cp);		/* RDATA length.  */
6ca6e8
+
6ca6e8
+      if (end_of_message - cp < n)
6ca6e8
+	{
6ca6e8
+	  /* RDATA extends beyond the end of the packet.  */
6ca6e8
+	  ++had_error;
6ca6e8
+	  continue;
6ca6e8
+	}
6ca6e8
+
6ca6e8
+      if (__glibc_unlikely (class != C_IN))
6ca6e8
+	{
6ca6e8
+	  /* XXX - debug? syslog? */
6ca6e8
+	  cp += n;
6ca6e8
+	  continue;			/* XXX - had_error++ ? */
6ca6e8
+	}
6ca6e8
+
6ca6e8
+      if (type == T_CNAME)
6ca6e8
 	{
6ca6e8
 	  /* A CNAME could also have a TTL entry.  */
6ca6e8
 	  if (ttlp != NULL && ttl < *ttlp)
6ca6e8
@@ -887,14 +1128,6 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	  continue;
6ca6e8
 	}
6ca6e8
 
6ca6e8
-      if (type == T_A && qtype == T_AAAA && map)
6ca6e8
-	have_to_map = 1;
6ca6e8
-      else if (__glibc_unlikely (type != qtype))
6ca6e8
-	{
6ca6e8
-	  cp += n;
6ca6e8
-	  continue;			/* XXX - had_error++ ? */
6ca6e8
-	}
6ca6e8
-
6ca6e8
       switch (type)
6ca6e8
 	{
6ca6e8
 	case T_PTR:
6ca6e8
@@ -956,8 +1189,6 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 		 TTL in the chain.  */
6ca6e8
 	      if (ttlp != NULL && ttl < *ttlp)
6ca6e8
 		*ttlp = ttl;
6ca6e8
-	      if (canonp != NULL)
6ca6e8
-		*canonp = bp;
6ca6e8
 	      result->h_name = bp;
6ca6e8
 	      nn = strlen (bp) + 1;	/* for the \0 */
6ca6e8
 	      bp += nn;
6ca6e8
@@ -984,7 +1215,8 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	  linebuflen -= n;
6ca6e8
 	  break;
6ca6e8
 	default:
6ca6e8
-	  abort ();
6ca6e8
+	  cp += n;
6ca6e8
+	  continue;			/* XXX - had_error++ ? */
6ca6e8
 	}
6ca6e8
       if (had_error == 0)
6ca6e8
 	++haveanswer;
6ca6e8
@@ -994,14 +1226,6 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
     {
6ca6e8
       *ap = NULL;
6ca6e8
       *hap = NULL;
6ca6e8
-      /*
6ca6e8
-       * Note: we sort even if host can take only one address
6ca6e8
-       * in its return structures - should give it the "best"
6ca6e8
-       * address in that case, not some random one
6ca6e8
-       */
6ca6e8
-      if (haveanswer > 1 && qtype == T_A
6ca6e8
-	  && __resolv_context_sort_count (ctx) > 0)
6ca6e8
-	addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
6ca6e8
 
6ca6e8
       if (result->h_name == NULL)
6ca6e8
 	{
6ca6e8
@@ -1015,23 +1239,15 @@ getanswer_r (struct resolv_context *ctx,
6ca6e8
 	  linebuflen -= n;
6ca6e8
 	}
6ca6e8
 
6ca6e8
-      if (have_to_map)
6ca6e8
-	if (map_v4v6_hostent (result, &bp, &linebuflen))
6ca6e8
-	  goto too_small;
6ca6e8
       *h_errnop = NETDB_SUCCESS;
6ca6e8
       return NSS_STATUS_SUCCESS;
6ca6e8
     }
6ca6e8
  no_recovery:
6ca6e8
   *h_errnop = NO_RECOVERY;
6ca6e8
   *errnop = ENOENT;
6ca6e8
-  /* Special case here: if the resolver sent a result but it only
6ca6e8
-     contains a CNAME while we are looking for a T_A or T_AAAA record,
6ca6e8
-     we fail with NOTFOUND instead of TRYAGAIN.  */
6ca6e8
-  return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
6ca6e8
-	   ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
6ca6e8
+  return NSS_STATUS_TRYAGAIN;
6ca6e8
 }
6ca6e8
 
6ca6e8
-
6ca6e8
 static enum nss_status
6ca6e8
 gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
6ca6e8
 		      struct gaih_addrtuple ***patp,