public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/12] getaddrinfo facelift and fixes
@ 2022-03-08 10:07 Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
                   ` (14 more replies)
  0 siblings, 15 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

The gaih_inet implementation, which forms the core of getaddrinfo, is
quite complex in its implementation, making it hard to follow or debug.
Particularly, allocations for gaih_addrtuples to store intermediate
results are particularly hard to track because of the way in which it is
written.

This patchset is an attempt at cleaning up the implementation to make it
much easier to follow.  In the process, it also fixes a couple of bugs,
one that was the trigger for this cleanup and another that was
discovered during the cleanup.

Testing:

The leak reproducer in BZ #28852 could not be converted into a testsuite
test since mtrace isn't able to track the leak, so I separately verified
that it is fixed.  There is a new test for #28931 which tests a variety
of combinations with SUCCESS=merge and SUCCESS=continue.  Further, I
have built and done some smoke testing on Fedora with and without nscd
to ensure that there are no regressions resulting from this patchset.

Finally, I did a scratch build for Fedora and verified that there are no
new regressions on i686, x86_64, s390x, ppc64le and aarch64.

Siddhesh Poyarekar (12):
  Simplify allocations and fix merge and continue actions [BZ #28931]
  gaih_inet: Simplify canon name resolution
  getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  gaih_inet: Simplify service resolution
  gaih_inet: make numeric lookup a separate routine
  gaih_inet: Split simple gethostbyname into its own function
  gaih_inet: Split nscd lookup code into its own function.
  gaih_inet: separate nss lookup loop into its own function
  gaih_inet: make gethosts into a function
  gaih_inet: split loopback lookup into its own function
  gaih_inet: Split result generation into its own function
  gethosts: Return EAI_MEMORY on allocation failure

 nss/Makefile                |    1 +
 nss/tst-nss-gai-actions.c   |  242 ++++++
 sysdeps/posix/getaddrinfo.c | 1518 ++++++++++++++++++-----------------
 3 files changed, 1046 insertions(+), 715 deletions(-)
 create mode 100644 nss/tst-nss-gai-actions.c

-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 13:52   ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Allocations for address tuples is currently a bit confusing because of
the pointer chasing through PAT, making it hard to observe the sequence
in which allocations have been made.  Narrow scope of the pointer
chasing through PAT so that it is only used where necessary.

This also tightens actions behaviour with the hosts database in
getaddrinfo to comply with the manual text.  The "continue" action
discards previous results and the "merge" action results in an immedate
lookup failure.  Consequently, chaining of allocations across modules is
no longer necessary, thus opening up cleanup opportunities.

A test has been added that checks some combinations to ensure that they
work correctly.  The "dns [SUCCESS=continue] dns" for example results in
a segfault without this fix.

Resolves: BZ #28931

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 nss/Makefile                |   1 +
 nss/tst-nss-gai-actions.c   | 242 ++++++++++++++++++++++++++++++++++++
 sysdeps/posix/getaddrinfo.c | 142 +++++++++++++--------
 3 files changed, 333 insertions(+), 52 deletions(-)
 create mode 100644 nss/tst-nss-gai-actions.c

diff --git a/nss/Makefile b/nss/Makefile
index 552e5d03e1..43a0b9defe 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -78,6 +78,7 @@ tests += tst-nss-files-hosts-multi
 tests += tst-nss-files-hosts-getent
 tests += tst-nss-files-alias-leak
 tests += tst-nss-files-alias-truncated
+tests += tst-nss-gai-actions
 endif
 
 # If we have a thread library then we can test cancellation against
diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
new file mode 100644
index 0000000000..b5ba5f0138
--- /dev/null
+++ b/nss/tst-nss-gai-actions.c
@@ -0,0 +1,242 @@
+/* Test continue and merge NSS actions for getaddrinfo.
+   Copyright The GNU Toolchain Authors.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <gnu/lib-names.h>
+#include <nss.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+enum
+{
+  ACTION_MERGE = 0,
+  ACTION_CONTINUE,
+};
+
+enum
+{
+  DB_FILES = 0,
+  DB_DNS,
+};
+
+struct test_params
+{
+  int db;
+  int action;
+  int family;
+  bool canon;
+};
+
+struct support_chroot *chroot_env;
+
+static void
+prepare (int argc, char **argv)
+{
+  chroot_env = support_chroot_create
+    ((struct support_chroot_configuration)
+     {
+       .resolv_conf = "",
+       .hosts = "",
+       .host_conf = "multi on\n",
+     });
+}
+
+/* Create the /etc/hosts file from outside the chroot.  */
+static void
+write_hosts (void)
+{
+  const int count = 512;
+
+  FILE *fp = xfopen (chroot_env->path_hosts, "w");
+  fputs ("127.0.0.1   localhost localhost.localdomain\n"
+         "::1         localhost localhost.localdomain\n",
+         fp);
+  for (int i = 1; i < count; ++i)
+    fprintf (fp, "192.0.%d.%d gnu.org\n", (i / 256) & 0xff, i & 0xff);
+  xfclose (fp);
+}
+
+static const char *
+family_str (int family)
+{
+  switch (family)
+    {
+    case AF_UNSPEC:
+      return "AF_UNSPEC";
+    case AF_INET:
+      return "AF_INET";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static const char *
+db_str (int db)
+{
+  switch (db)
+    {
+    case DB_DNS:
+      return "dns";
+    case DB_FILES:
+      return "files";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static const char *
+action_str (int action)
+{
+  switch (action)
+    {
+    case ACTION_MERGE:
+      return "merge";
+    case ACTION_CONTINUE:
+      return "continue";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+/* getaddrinfo test.  To be run from a subprocess.  */
+static void
+test_gai (void *closure)
+{
+  struct test_params *params = closure;
+
+  struct addrinfo hints =
+    {
+      .ai_family = params->family,
+    };
+
+  struct addrinfo *ai;
+
+  if (params->canon)
+    hints.ai_flags = AI_CANONNAME;
+
+  /* Use /etc/hosts in the chroot.  */
+  if (params->db == DB_FILES)
+    xchroot (chroot_env->path_chroot);
+
+  printf ("***** Testing \"%s [SUCCESS=%s] %s\" for family %s, %s\n",
+	  db_str (params->db), action_str (params->action),
+	  db_str (params->db), family_str (params->family),
+	  params->canon ? "AI_CANONNAME" : "");
+
+  int ret = getaddrinfo ("gnu.org", "80", &hints, &ai);
+
+  switch (params->action)
+    {
+    case ACTION_MERGE:
+      if (ret == 0)
+	{
+	  char *formatted = support_format_addrinfo (ai, ret);
+
+	  printf ("merge unexpectedly succeeded:\n %s\n", formatted);
+	  support_record_failure ();
+	  free (formatted);
+	}
+      else
+	return;
+    case ACTION_CONTINUE:
+	{
+	  char *formatted = support_format_addrinfo (ai, ret);
+
+	  if (params->db == DB_FILES)
+	    {
+	      /* Verify that the result appears exactly once.  */
+	      const char *expected = "address: STREAM/TCP 192.0.0.1 80\n"
+		"address: DGRAM/UDP 192.0.0.1 80\n"
+		"address: RAW/IP 192.0.0.1 80\n";
+
+	      const char *contains = strstr (formatted, expected);
+	      const char *contains2 = NULL;
+
+	      if (contains != NULL)
+		contains2 = strstr (contains + strlen (expected), expected);
+
+	      if (contains == NULL || contains2 != NULL)
+		{
+		  printf ("continue failed:\n%s\n", formatted);
+		  support_record_failure ();
+		}
+	    }
+	  else if (ret != 0)
+	    {
+	      printf ("continue unexpectedly failed:\n%s\n", formatted);
+	      support_record_failure ();
+	    }
+
+	  free (formatted);
+	  break;
+	}
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static void
+test_in_subprocess (int db, int action)
+{
+  char buf[32];
+
+  snprintf (buf, sizeof (buf), "%s [SUCCESS=%s] %s",
+	    db_str (db), action_str (action), db_str (db));
+  __nss_configure_lookup ("hosts", buf);
+
+  struct test_params params =
+    {
+      .db = db,
+      .action = action,
+      .family = AF_UNSPEC,
+      .canon = false,
+    };
+  support_isolate_in_subprocess (test_gai, &params);
+  params.family = AF_INET;
+  support_isolate_in_subprocess (test_gai, &params);
+  params.canon = true;
+  support_isolate_in_subprocess (test_gai, &params);
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  if (!support_can_chroot ())
+    FAIL_UNSUPPORTED ("Cannot chroot\n");
+
+  write_hosts ();
+  test_in_subprocess (DB_FILES, ACTION_CONTINUE);
+  test_in_subprocess (DB_FILES, ACTION_MERGE);
+  test_in_subprocess (DB_DNS, ACTION_CONTINUE);
+  test_in_subprocess (DB_DNS, ACTION_MERGE);
+
+  support_chroot_free (chroot_env);
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 18dccd5924..454a27eb2f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -458,11 +458,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   if (name != NULL)
     {
-      at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      at->family = AF_UNSPEC;
-      at->scopeid = 0;
-      at->next = NULL;
-
       if (req->ai_flags & AI_IDN)
 	{
 	  char *out;
@@ -473,13 +468,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+      uint32_t addr[4];
+      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
 	{
+	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+	  at->scopeid = 0;
+	  at->next = NULL;
+
 	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    at->family = AF_INET;
+	    {
+	      memcpy (at->addr, addr, sizeof (at->addr));
+	      at->family = AF_INET;
+	    }
 	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
 	    {
-	      at->addr[3] = at->addr[0];
+	      at->addr[3] = addr[0];
 	      at->addr[2] = htonl (0xffff);
 	      at->addr[1] = 0;
 	      at->addr[0] = 0;
@@ -493,49 +496,62 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  if (req->ai_flags & AI_CANONNAME)
 	    canon = name;
+
+	  goto process_list;
 	}
-      else if (at->family == AF_UNSPEC)
+
+      char *scope_delim = strchr (name, SCOPE_DELIMITER);
+      int e;
+
+      if (scope_delim == NULL)
+	e = inet_pton (AF_INET6, name, addr);
+      else
+	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
+
+      if (e > 0)
 	{
-	  char *scope_delim = strchr (name, SCOPE_DELIMITER);
-	  int e;
-	  if (scope_delim == NULL)
-	    e = inet_pton (AF_INET6, name, at->addr);
+	  at = alloca_account (sizeof (struct gaih_addrtuple),
+			       alloca_used);
+	  at->scopeid = 0;
+	  at->next = NULL;
+
+	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+	    {
+	      memcpy (at->addr, addr, sizeof (at->addr));
+	      at->family = AF_INET6;
+	    }
+	  else if (req->ai_family == AF_INET
+		   && IN6_IS_ADDR_V4MAPPED (addr))
+	    {
+	      at->addr[0] = addr[3];
+	      at->addr[1] = addr[1];
+	      at->addr[2] = addr[2];
+	      at->addr[3] = addr[3];
+	      at->family = AF_INET;
+	    }
 	  else
-	    e = __inet_pton_length (AF_INET6, name, scope_delim - name,
-				    at->addr);
-	  if (e > 0)
 	    {
-	      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-		at->family = AF_INET6;
-	      else if (req->ai_family == AF_INET
-		       && IN6_IS_ADDR_V4MAPPED (at->addr))
-		{
-		  at->addr[0] = at->addr[3];
-		  at->family = AF_INET;
-		}
-	      else
-		{
-		  result = -EAI_ADDRFAMILY;
-		  goto free_and_return;
-		}
-
-	      if (scope_delim != NULL
-		  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
-					   scope_delim + 1,
-					   &at->scopeid) != 0)
-		{
-		  result = -EAI_NONAME;
-		  goto free_and_return;
-		}
+	      result = -EAI_ADDRFAMILY;
+	      goto free_and_return;
+	    }
 
-	      if (req->ai_flags & AI_CANONNAME)
-		canon = name;
+	  if (scope_delim != NULL
+	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+				       scope_delim + 1,
+				       &at->scopeid) != 0)
+	    {
+	      result = -EAI_NONAME;
+	      goto free_and_return;
 	    }
+
+	  if (req->ai_flags & AI_CANONNAME)
+	    canon = name;
+
+	  goto process_list;
 	}
 
-      if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
+      if ((req->ai_flags & AI_NUMERICHOST) == 0)
 	{
-	  struct gaih_addrtuple **pat = &at;
 	  int no_data = 0;
 	  int no_inet6_data = 0;
 	  nss_action_list nip;
@@ -543,6 +559,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  enum nss_status status = NSS_STATUS_UNAVAIL;
 	  int no_more;
 	  struct resolv_context *res_ctx = NULL;
+	  bool do_merge = false;
 
 	  /* If we do not have to look for IPv6 addresses or the canonical
 	     name, use the simple, old functions, which do not support
@@ -579,7 +596,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  result = -EAI_MEMORY;
 			  goto free_and_return;
 			}
-		      *pat = addrmem;
+		      at = addrmem;
 		    }
 		  else
 		    {
@@ -632,6 +649,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		    }
 
 		  struct gaih_addrtuple *addrfree = addrmem;
+		  struct gaih_addrtuple **pat = &at;
+
 		  for (int i = 0; i < air->naddrs; ++i)
 		    {
 		      socklen_t size = (air->family[i] == AF_INET
@@ -695,12 +714,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 		  free (air);
 
-		  if (at->family == AF_UNSPEC)
-		    {
-		      result = -EAI_NONAME;
-		      goto free_and_return;
-		    }
-
 		  goto process_list;
 		}
 	      else if (err == 0)
@@ -732,6 +745,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  while (!no_more)
 	    {
+	      /* Always start afresh; continue should discard previous results
+		 and the hosts database does not support merge.  */
+	      at = NULL;
+	      free (canonbuf);
+	      free (addrmem);
+	      canon = canonbuf = NULL;
+	      addrmem = NULL;
+
+	      if (do_merge)
+		{
+		  __set_h_errno (NETDB_INTERNAL);
+		  __set_errno (EBUSY);
+		  break;
+		}
+
 	      no_data = 0;
 	      nss_gethostbyname4_r *fct4 = NULL;
 
@@ -744,12 +772,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		{
 		  while (1)
 		    {
-		      status = DL_CALL_FCT (fct4, (name, pat,
+		      status = DL_CALL_FCT (fct4, (name, &at,
 						   tmpbuf->data, tmpbuf->length,
 						   &errno, &h_errno,
 						   NULL));
 		      if (status == NSS_STATUS_SUCCESS)
 			break;
+		      /* gethostbyname4_r may write into AT, so reset it.  */
+		      at = NULL;
 		      if (status != NSS_STATUS_TRYAGAIN
 			  || errno != ERANGE || h_errno != NETDB_INTERNAL)
 			{
@@ -774,7 +804,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      no_data = 1;
 
 		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
-			canon = (*pat)->name;
+			canon = at->name;
+
+		      struct gaih_addrtuple **pat = &at;
 
 		      while (*pat != NULL)
 			{
@@ -826,6 +858,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 		  if (fct != NULL)
 		    {
+		      struct gaih_addrtuple **pat = &at;
+
 		      if (req->ai_family == AF_INET6
 			  || req->ai_family == AF_UNSPEC)
 			{
@@ -899,6 +933,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
 		break;
 
+	      /* The hosts database does not support MERGE.  */
+	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+		do_merge = true;
+
 	      nip++;
 	      if (nip->module == NULL)
 		no_more = -1;
@@ -930,7 +968,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
 
     process_list:
-      if (at->family == AF_UNSPEC)
+      if (at == NULL)
 	{
 	  result = -EAI_NONAME;
 	  goto free_and_return;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 02/12] gaih_inet: Simplify canon name resolution
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Simplify logic for allocation of canon to remove the canonbuf variable;
canon now always points to an allocated block.  Also pull the canon name
set into a separate function.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 130 +++++++++++++++++++++---------------
 1 file changed, 75 insertions(+), 55 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 454a27eb2f..df164a3e96 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -285,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 									      \
       if (localcanon != NULL && canon == NULL)				      \
 	{								      \
-	  canonbuf = __strdup (localcanon);				      \
+	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
 	    {								      \
 	      __resolv_context_put (res_ctx);				      \
@@ -323,6 +323,41 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
   return __strdup (name);
 }
 
+/* Process looked up canonical name and if necessary, decode to IDNA.  Result
+   is a new string written to CANONP and the earlier string is freed.  */
+
+static int
+process_canonname (const struct addrinfo *req, const char *orig_name,
+		   char **canonp)
+{
+  char *canon = *canonp;
+
+  if ((req->ai_flags & AI_CANONNAME) != 0)
+    {
+      bool do_idn = req->ai_flags & AI_CANONIDN;
+      if (do_idn)
+	{
+	  char *out;
+	  int rc = __idna_from_dns_encoding (canon ?: orig_name, &out);
+	  if (rc == 0)
+	    {
+	      free (canon);
+	      canon = out;
+	    }
+	  else if (rc == EAI_IDN_ENCODE)
+	    /* Use the punycode name as a fallback.  */
+	    do_idn = false;
+	  else
+	    return -rc;
+	}
+      if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL)
+	return -EAI_MEMORY;
+    }
+
+  *canonp = canon;
+  return 0;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -332,7 +367,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
   struct gaih_addrtuple *at = NULL;
   bool got_ipv6 = false;
-  const char *canon = NULL;
+  char *canon = NULL;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -453,7 +488,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   bool malloc_name = false;
   struct gaih_addrtuple *addrmem = NULL;
-  char *canonbuf = NULL;
   int result = 0;
 
   if (name != NULL)
@@ -495,7 +529,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    }
 
 	  if (req->ai_flags & AI_CANONNAME)
-	    canon = name;
+	    {
+	      char *canonbuf = __strdup (name);
+	      if (canonbuf == NULL)
+		{
+		  result = -EAI_MEMORY;
+		  goto free_and_return;
+		}
+	      canon = canonbuf;
+	    }
 
 	  goto process_list;
 	}
@@ -545,7 +587,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    }
 
 	  if (req->ai_flags & AI_CANONNAME)
-	    canon = name;
+	    {
+	      char *canonbuf = __strdup (name);
+	      if (canonbuf == NULL)
+		{
+		  result = -EAI_MEMORY;
+		  goto free_and_return;
+		}
+	      canon = canonbuf;
+	    }
 
 	  goto process_list;
 	}
@@ -676,9 +726,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      (*pat)->next = NULL;
 		      if (added_canon || air->canon == NULL)
 			(*pat)->name = NULL;
-		      else if (canonbuf == NULL)
+		      else if (canon == NULL)
 			{
-			  canonbuf = __strdup (air->canon);
+			  char *canonbuf = __strdup (air->canon);
 			  if (canonbuf == NULL)
 			    {
 			      result = -EAI_MEMORY;
@@ -748,9 +798,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      /* Always start afresh; continue should discard previous results
 		 and the hosts database does not support merge.  */
 	      at = NULL;
-	      free (canonbuf);
+	      free (canon);
 	      free (addrmem);
-	      canon = canonbuf = NULL;
+	      canon = NULL;
 	      addrmem = NULL;
 
 	      if (do_merge)
@@ -804,7 +854,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      no_data = 1;
 
 		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
-			canon = at->name;
+			{
+			  char *canonbuf = __strdup (at->name);
+			  if (canonbuf == NULL)
+			    {
+			      __resolv_context_put (res_ctx);
+			      result = -EAI_MEMORY;
+			      goto free_and_return;
+			    }
+			  canon = canonbuf;
+			}
 
 		      struct gaih_addrtuple **pat = &at;
 
@@ -892,7 +951,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  if ((req->ai_flags & AI_CANONNAME) != 0
 			      && canon == NULL)
 			    {
-			      canonbuf = getcanonname (nip, at, name);
+			      char *canonbuf = getcanonname (nip, at, name);
 			      if (canonbuf == NULL)
 				{
 				  __resolv_context_put (res_ctx);
@@ -1003,6 +1062,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
     }
 
   {
+    /* Set up the canonical name if we need it.  */
+    if ((result = process_canonname (req, orig_name, &canon)) != 0)
+      goto free_and_return;
+
     struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
     size_t socklen;
@@ -1013,48 +1076,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
      */
     while (at2 != NULL)
       {
-	/* Only the first entry gets the canonical name.  */
-	if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
-	  {
-	    if (canon == NULL)
-	      /* If the canonical name cannot be determined, use
-		 the passed in string.  */
-	      canon = orig_name;
-
-	    bool do_idn = req->ai_flags & AI_CANONIDN;
-	    if (do_idn)
-	      {
-		char *out;
-		int rc = __idna_from_dns_encoding (canon, &out);
-		if (rc == 0)
-		  canon = out;
-		else if (rc == EAI_IDN_ENCODE)
-		  /* Use the punycode name as a fallback.  */
-		  do_idn = false;
-		else
-		  {
-		    result = -rc;
-		    goto free_and_return;
-		  }
-	      }
-	    if (!do_idn)
-	      {
-		if (canonbuf != NULL)
-		  /* We already allocated the string using malloc, but
-		     the buffer is now owned by canon.  */
-		  canonbuf = NULL;
-		else
-		  {
-		    canon = __strdup (canon);
-		    if (canon == NULL)
-		      {
-			result = -EAI_MEMORY;
-			goto free_and_return;
-		      }
-		  }
-	      }
-	  }
-
 	family = at2->family;
 	if (family == AF_INET6)
 	  {
@@ -1077,7 +1098,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
 	    if (ai == NULL)
 	      {
-		free ((char *) canon);
 		result = -EAI_MEMORY;
 		goto free_and_return;
 	      }
@@ -1137,7 +1157,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-  free (canonbuf);
+  free (canon);
 
   return result;
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 11:00   ` Andreas Schwab
  2022-03-08 10:07 ` [PATCH 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Use realloc in convert_hostent_to_gaih_addrtuple and fix up pointers in
the result list so that a single block is maintained for
hostbyname3_r/hostbyname2_r and freed in gaih_inet.  This result is
never merged with any other results, since the hosts database does not
permit merging.

Resolves BZ #28852.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index df164a3e96..0ec85dc4bd 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -199,9 +199,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 				   struct hostent *h,
 				   struct gaih_addrtuple **result)
 {
-  while (*result)
-    result = &(*result)->next;
-
   /* Count the number of addresses in h->h_addr_list.  */
   size_t count = 0;
   for (char **p = h->h_addr_list; *p != NULL; ++p)
@@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
     return true;
 
-  struct gaih_addrtuple *array = calloc (count, sizeof (*array));
+  struct gaih_addrtuple *array = *result;
+  size_t old = 0;
+
+  while (array)
+    {
+      old++;
+      array = array->next;
+    }
+
+  array = realloc (*result, (old + count) * sizeof (*array));
+
   if (array == NULL)
     return false;
 
+  *result = array;
+
+  /* Update the next pointers on reallocation.  */
+  for (size_t i = 0; i < old; i++)
+    array[i].next = array + i + 1;
+
+  array += old;
+
+  memset (array, 0, count * sizeof (*array));
+
   for (size_t i = 0; i < count; ++i)
     {
       if (family == AF_INET && req->ai_family == AF_INET6)
@@ -235,7 +252,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   array[0].name = h->h_name;
   array[count - 1].next = NULL;
 
-  *result = array;
   return true;
 }
 
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 04/12] gaih_inet: Simplify service resolution
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (2 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Refactor the code to split out the service resolution code into a
separate function.  Allocate the service tuples array just once to the
size of the typeproto array, thus avoiding the unnecessary pointer
chasing and stack allocations.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 178 ++++++++++++++++--------------------
 1 file changed, 78 insertions(+), 100 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 0ec85dc4bd..6bc1d6b8d2 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -100,14 +100,12 @@ struct gaih_service
 
 struct gaih_servtuple
   {
-    struct gaih_servtuple *next;
     int socktype;
     int protocol;
     int port;
+    bool set;
   };
 
-static const struct gaih_servtuple nullserv;
-
 
 struct gaih_typeproto
   {
@@ -180,11 +178,11 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
     }
   while (r);
 
-  st->next = NULL;
   st->socktype = tp->socktype;
   st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
 		  ? req->ai_protocol : tp->protocol);
   st->port = s->s_port;
+  st->set = true;
 
   return 0;
 }
@@ -375,20 +373,11 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
 }
 
 static int
-gaih_inet (const char *name, const struct gaih_service *service,
-	   const struct addrinfo *req, struct addrinfo **pai,
-	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
+get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
+		struct gaih_servtuple *st, struct scratch_buffer *tmpbuf)
 {
+  int i;
   const struct gaih_typeproto *tp = gaih_inet_typeproto;
-  struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
-  struct gaih_addrtuple *at = NULL;
-  bool got_ipv6 = false;
-  char *canon = NULL;
-  const char *orig_name = name;
-
-  /* Reserve stack memory for the scratch buffer in the getaddrinfo
-     function.  */
-  size_t alloca_used = sizeof (struct scratch_buffer);
 
   if (req->ai_protocol || req->ai_socktype)
     {
@@ -410,98 +399,88 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
     }
 
-  int port = 0;
-  if (service != NULL)
+  if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+    return -EAI_SERVICE;
+
+  if (service == NULL || service->num >= 0)
     {
-      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-	return -EAI_SERVICE;
+      int port = service != NULL ? htons (service->num) : 0;
 
-      if (service->num < 0)
+      if (req->ai_socktype || req->ai_protocol)
 	{
-	  if (tp->name[0])
-	    {
-	      st = (struct gaih_servtuple *)
-		alloca_account (sizeof (struct gaih_servtuple), alloca_used);
-
-	      int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf);
-	      if (__glibc_unlikely (rc != 0))
-		return rc;
-	    }
-	  else
-	    {
-	      struct gaih_servtuple **pst = &st;
-	      for (tp++; tp->name[0]; tp++)
-		{
-		  struct gaih_servtuple *newp;
+	  st[0].socktype = tp->socktype;
+	  st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+			  ? req->ai_protocol : tp->protocol);
+	  st[0].port = port;
+	  st[0].set = true;
 
-		  if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-		    continue;
+	  return 0;
+	}
 
-		  if (req->ai_socktype != 0
-		      && req->ai_socktype != tp->socktype)
-		    continue;
-		  if (req->ai_protocol != 0
-		      && !(tp->protoflag & GAI_PROTO_PROTOANY)
-		      && req->ai_protocol != tp->protocol)
-		    continue;
+      /* Neither socket type nor protocol is set.  Return all socket types
+	 we know about.  */
+      for (i = 0, ++tp; tp->name[0]; ++tp)
+	if (tp->defaultflag)
+	  {
+	    st[i].socktype = tp->socktype;
+	    st[i].protocol = tp->protocol;
+	    st[i].port = port;
+	    st[i++].set = true;
+	  }
 
-		  newp = (struct gaih_servtuple *)
-		    alloca_account (sizeof (struct gaih_servtuple),
-				    alloca_used);
+      return 0;
+    }
 
-		  if (gaih_inet_serv (service->name,
-				      tp, req, newp, tmpbuf) != 0)
-		    continue;
+  if (tp->name[0])
+    return gaih_inet_serv (service->name, tp, req, st, tmpbuf);
 
-		  *pst = newp;
-		  pst = &(newp->next);
-		}
-	      if (st == (struct gaih_servtuple *) &nullserv)
-		return -EAI_SERVICE;
-	    }
-	}
-      else
-	{
-	  port = htons (service->num);
-	  goto got_port;
-	}
-    }
-  else
+  for (i = 0, tp++; tp->name[0]; tp++)
     {
-    got_port:
+      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+	continue;
 
-      if (req->ai_socktype || req->ai_protocol)
-	{
-	  st = alloca_account (sizeof (struct gaih_servtuple), alloca_used);
-	  st->next = NULL;
-	  st->socktype = tp->socktype;
-	  st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
-			  ? req->ai_protocol : tp->protocol);
-	  st->port = port;
-	}
-      else
-	{
-	  /* Neither socket type nor protocol is set.  Return all socket types
-	     we know about.  */
-	  struct gaih_servtuple **lastp = &st;
-	  for (++tp; tp->name[0]; ++tp)
-	    if (tp->defaultflag)
-	      {
-		struct gaih_servtuple *newp;
+      if (req->ai_socktype != 0
+	  && req->ai_socktype != tp->socktype)
+	continue;
+      if (req->ai_protocol != 0
+	  && !(tp->protoflag & GAI_PROTO_PROTOANY)
+	  && req->ai_protocol != tp->protocol)
+	continue;
 
-		newp = alloca_account (sizeof (struct gaih_servtuple),
-				       alloca_used);
-		newp->next = NULL;
-		newp->socktype = tp->socktype;
-		newp->protocol = tp->protocol;
-		newp->port = port;
+      if (gaih_inet_serv (service->name,
+			  tp, req, &st[i], tmpbuf) != 0)
+	continue;
 
-		*lastp = newp;
-		lastp = &newp->next;
-	      }
-	}
+      i++;
     }
 
+  if (!st[0].set)
+    return -EAI_SERVICE;
+
+  return 0;
+}
+
+static int
+gaih_inet (const char *name, const struct gaih_service *service,
+	   const struct addrinfo *req, struct addrinfo **pai,
+	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
+{
+  struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
+			   / sizeof (struct gaih_typeproto)] = {0};
+
+  struct gaih_addrtuple *at = NULL;
+  bool got_ipv6 = false;
+  char *canon = NULL;
+  const char *orig_name = name;
+
+  /* Reserve stack memory for the scratch buffer in the getaddrinfo
+     function.  */
+  size_t alloca_used = sizeof (struct scratch_buffer);
+
+  int rc;
+  if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
+    return rc;
+
   bool malloc_name = false;
   struct gaih_addrtuple *addrmem = NULL;
   int result = 0;
@@ -1082,7 +1061,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
     if ((result = process_canonname (req, orig_name, &canon)) != 0)
       goto free_and_return;
 
-    struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
     size_t socklen;
     sa_family_t family;
@@ -1108,7 +1086,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	else
 	  socklen = sizeof (struct sockaddr_in);
 
-	for (st2 = st; st2 != NULL; st2 = st2->next)
+	for (int i = 0; st[i].set; i++)
 	  {
 	    struct addrinfo *ai;
 	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
@@ -1120,8 +1098,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	    ai->ai_flags = req->ai_flags;
 	    ai->ai_family = family;
-	    ai->ai_socktype = st2->socktype;
-	    ai->ai_protocol = st2->protocol;
+	    ai->ai_socktype = st[i].socktype;
+	    ai->ai_protocol = st[i].protocol;
 	    ai->ai_addrlen = socklen;
 	    ai->ai_addr = (void *) (ai + 1);
 
@@ -1143,7 +1121,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		struct sockaddr_in6 *sin6p =
 		  (struct sockaddr_in6 *) ai->ai_addr;
 
-		sin6p->sin6_port = st2->port;
+		sin6p->sin6_port = st[i].port;
 		sin6p->sin6_flowinfo = 0;
 		memcpy (&sin6p->sin6_addr,
 			at2->addr, sizeof (struct in6_addr));
@@ -1153,7 +1131,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      {
 		struct sockaddr_in *sinp =
 		  (struct sockaddr_in *) ai->ai_addr;
-		sinp->sin_port = st2->port;
+		sinp->sin_port = st[i].port;
 		memcpy (&sinp->sin_addr,
 			at2->addr, sizeof (struct in_addr));
 		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 05/12] gaih_inet: make numeric lookup a separate routine
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (3 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Introduce the gaih_result structure and general paradigm for cleanups
that follow to process the lookup request and return a result.  A lookup
function (like text_to_binary_address), should return an integer error
code and set members of gaih_result based on what it finds.  If the
function does not have a result and no errors have occurred during the
lookup, it should return 0 and res.at should be set to NULL, allowing a
subsequent function to do the lookup until we run out of options.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 889 ++++++++++++++++++------------------
 1 file changed, 451 insertions(+), 438 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 6bc1d6b8d2..868c7d222f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -116,6 +116,12 @@ struct gaih_typeproto
     char name[8];
   };
 
+struct gaih_result
+{
+  struct gaih_addrtuple *at;
+  char *canon;
+};
+
 /* Values for `protoflag'.  */
 #define GAI_PROTO_NOSERVICE	1
 #define GAI_PROTO_PROTOANY	2
@@ -297,7 +303,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	}								      \
       *pat = addrmem;							      \
 									      \
-      if (localcanon != NULL && canon == NULL)				      \
+      if (localcanon != NULL && res.canon == NULL)			      \
 	{								      \
 	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
@@ -306,7 +312,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	      result = -EAI_SYSTEM;					      \
 	      goto free_and_return;					      \
 	    }								      \
-	  canon = canonbuf;						      \
+	  res.canon = canonbuf;						      \
 	}								      \
       if (_family == AF_INET6 && *pat != NULL)				      \
 	got_ipv6 = true;						      \
@@ -342,9 +348,9 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
 
 static int
 process_canonname (const struct addrinfo *req, const char *orig_name,
-		   char **canonp)
+		   struct gaih_result *res)
 {
-  char *canon = *canonp;
+  char *canon = res->canon;
 
   if ((req->ai_flags & AI_CANONNAME) != 0)
     {
@@ -368,7 +374,7 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
 	return -EAI_MEMORY;
     }
 
-  *canonp = canon;
+  res->canon = canon;
   return 0;
 }
 
@@ -460,6 +466,105 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
   return 0;
 }
 
+/* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
+   NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
+   the function cannot determine a result, RES->AT is set to NULL and 0
+   returned.  */
+
+static int
+text_to_binary_address (const char *name, const struct addrinfo *req,
+			struct gaih_result *res)
+{
+  struct gaih_addrtuple *at = res->at;
+  int result = 0;
+
+  assert (at != NULL);
+
+  memset (at->addr, 0, sizeof (at->addr));
+  if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+    {
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
+	at->family = AF_INET;
+      else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
+	{
+	  at->addr[3] = at->addr[0];
+	  at->addr[2] = htonl (0xffff);
+	  at->addr[1] = 0;
+	  at->addr[0] = 0;
+	  at->family = AF_INET6;
+	}
+      else
+	{
+	  result = -EAI_ADDRFAMILY;
+	  goto out;
+	}
+
+      if (req->ai_flags & AI_CANONNAME)
+	{
+	  char *canonbuf = __strdup (name);
+	  if (canonbuf == NULL)
+	    {
+	      result = -EAI_MEMORY;
+	      goto out;
+	    }
+	  res->canon = canonbuf;
+	}
+      return 0;
+    }
+
+  char *scope_delim = strchr (name, SCOPE_DELIMITER);
+  int e;
+
+  if (scope_delim == NULL)
+    e = inet_pton (AF_INET6, name, at->addr);
+  else
+    e = __inet_pton_length (AF_INET6, name, scope_delim - name, at->addr);
+
+  if (e > 0)
+    {
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+	at->family = AF_INET6;
+      else if (req->ai_family == AF_INET
+	       && IN6_IS_ADDR_V4MAPPED (at->addr))
+	{
+	  at->addr[0] = at->addr[3];
+	  at->family = AF_INET;
+	}
+      else
+	{
+	  result = -EAI_ADDRFAMILY;
+	  goto out;
+	}
+
+      if (scope_delim != NULL
+	  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+				   scope_delim + 1, &at->scopeid) != 0)
+	{
+	  result = -EAI_NONAME;
+	  goto out;
+	}
+
+      if (req->ai_flags & AI_CANONNAME)
+	{
+	  char *canonbuf = __strdup (name);
+	  if (canonbuf == NULL)
+	    {
+	      result = -EAI_MEMORY;
+	      goto out;
+	    }
+	  res->canon = canonbuf;
+	}
+      return 0;
+    }
+
+  if ((req->ai_flags & AI_NUMERICHOST))
+    result = -EAI_NONAME;
+
+out:
+  res->at = NULL;
+  return result;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -468,9 +573,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
 			   / sizeof (struct gaih_typeproto)] = {0};
 
-  struct gaih_addrtuple *at = NULL;
   bool got_ipv6 = false;
-  char *canon = NULL;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -485,6 +588,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_addrtuple *addrmem = NULL;
   int result = 0;
 
+  struct gaih_result res = {0};
   if (name != NULL)
     {
       if (req->ai_flags & AI_IDN)
@@ -497,532 +601,440 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      uint32_t addr[4];
-      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
+      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+      res.at->scopeid = 0;
+      res.at->next = NULL;
+
+      if ((result = text_to_binary_address (name, req, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+
+      int no_data = 0;
+      int no_inet6_data = 0;
+      nss_action_list nip;
+      enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+      enum nss_status status = NSS_STATUS_UNAVAIL;
+      int no_more;
+      struct resolv_context *res_ctx = NULL;
+      bool do_merge = false;
+
+      /* If we do not have to look for IPv6 addresses or the canonical
+	 name, use the simple, old functions, which do not support
+	 IPv6 scope ids, nor retrieving the canonical name.  */
+      if (req->ai_family == AF_INET
+	  && (req->ai_flags & AI_CANONNAME) == 0)
 	{
-	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-	  at->scopeid = 0;
-	  at->next = NULL;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    {
-	      memcpy (at->addr, addr, sizeof (at->addr));
-	      at->family = AF_INET;
-	    }
-	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
-	    {
-	      at->addr[3] = addr[0];
-	      at->addr[2] = htonl (0xffff);
-	      at->addr[1] = 0;
-	      at->addr[0] = 0;
-	      at->family = AF_INET6;
-	    }
-	  else
-	    {
-	      result = -EAI_ADDRFAMILY;
-	      goto free_and_return;
-	    }
+	  int rc;
+	  struct hostent th;
+	  struct hostent *h;
 
-	  if (req->ai_flags & AI_CANONNAME)
+	  while (1)
 	    {
-	      char *canonbuf = __strdup (name);
-	      if (canonbuf == NULL)
+	      rc = __gethostbyname2_r (name, AF_INET, &th,
+				       tmpbuf->data, tmpbuf->length,
+				       &h, &h_errno);
+	      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
+		break;
+	      if (!scratch_buffer_grow (tmpbuf))
 		{
 		  result = -EAI_MEMORY;
 		  goto free_and_return;
 		}
-	      canon = canonbuf;
 	    }
 
-	  goto process_list;
-	}
-
-      char *scope_delim = strchr (name, SCOPE_DELIMITER);
-      int e;
-
-      if (scope_delim == NULL)
-	e = inet_pton (AF_INET6, name, addr);
-      else
-	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
-
-      if (e > 0)
-	{
-	  at = alloca_account (sizeof (struct gaih_addrtuple),
-			       alloca_used);
-	  at->scopeid = 0;
-	  at->next = NULL;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	    {
-	      memcpy (at->addr, addr, sizeof (at->addr));
-	      at->family = AF_INET6;
-	    }
-	  else if (req->ai_family == AF_INET
-		   && IN6_IS_ADDR_V4MAPPED (addr))
+	  if (rc == 0)
 	    {
-	      at->addr[0] = addr[3];
-	      at->addr[1] = addr[1];
-	      at->addr[2] = addr[2];
-	      at->addr[3] = addr[3];
-	      at->family = AF_INET;
+	      if (h != NULL)
+		{
+		  /* We found data, convert it.  */
+		  if (!convert_hostent_to_gaih_addrtuple
+		      (req, AF_INET, h, &addrmem))
+		    {
+		      result = -EAI_MEMORY;
+		      goto free_and_return;
+		    }
+		  res.at = addrmem;
+		}
+	      else
+		{
+		  if (h_errno == NO_DATA)
+		    result = -EAI_NODATA;
+		  else
+		    result = -EAI_NONAME;
+		  goto free_and_return;
+		}
 	    }
 	  else
 	    {
-	      result = -EAI_ADDRFAMILY;
-	      goto free_and_return;
-	    }
+	      if (h_errno == NETDB_INTERNAL)
+		result = -EAI_SYSTEM;
+	      else if (h_errno == TRY_AGAIN)
+		result = -EAI_AGAIN;
+	      else
+		/* We made requests but they turned out no data.
+		   The name is known, though.  */
+		result = -EAI_NODATA;
 
-	  if (scope_delim != NULL
-	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
-				       scope_delim + 1,
-				       &at->scopeid) != 0)
-	    {
-	      result = -EAI_NONAME;
 	      goto free_and_return;
 	    }
 
-	  if (req->ai_flags & AI_CANONNAME)
+	  goto process_list;
+	}
+
+#ifdef USE_NSCD
+      if (__nss_not_use_nscd_hosts > 0
+	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+	__nss_not_use_nscd_hosts = 0;
+
+      if (!__nss_not_use_nscd_hosts
+	  && !__nss_database_custom[NSS_DBSIDX_hosts])
+	{
+	  /* Try to use nscd.  */
+	  struct nscd_ai_result *air = NULL;
+	  int err = __nscd_getai (name, &air, &h_errno);
+	  if (air != NULL)
 	    {
-	      char *canonbuf = __strdup (name);
-	      if (canonbuf == NULL)
+	      /* Transform into gaih_addrtuple list.  */
+	      bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
+	      char *addrs = air->addrs;
+
+	      addrmem = calloc (air->naddrs, sizeof (*addrmem));
+	      if (addrmem == NULL)
 		{
 		  result = -EAI_MEMORY;
 		  goto free_and_return;
 		}
-	      canon = canonbuf;
-	    }
 
-	  goto process_list;
-	}
-
-      if ((req->ai_flags & AI_NUMERICHOST) == 0)
-	{
-	  int no_data = 0;
-	  int no_inet6_data = 0;
-	  nss_action_list nip;
-	  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
-	  enum nss_status status = NSS_STATUS_UNAVAIL;
-	  int no_more;
-	  struct resolv_context *res_ctx = NULL;
-	  bool do_merge = false;
-
-	  /* If we do not have to look for IPv6 addresses or the canonical
-	     name, use the simple, old functions, which do not support
-	     IPv6 scope ids, nor retrieving the canonical name.  */
-	  if (req->ai_family == AF_INET
-	      && (req->ai_flags & AI_CANONNAME) == 0)
-	    {
-	      int rc;
-	      struct hostent th;
-	      struct hostent *h;
+	      struct gaih_addrtuple *addrfree = addrmem;
+	      struct gaih_addrtuple **pat = &res.at;
 
-	      while (1)
+	      for (int i = 0; i < air->naddrs; ++i)
 		{
-		  rc = __gethostbyname2_r (name, AF_INET, &th,
-					   tmpbuf->data, tmpbuf->length,
-					   &h, &h_errno);
-		  if (rc != ERANGE || h_errno != NETDB_INTERNAL)
-		    break;
-		  if (!scratch_buffer_grow (tmpbuf))
+		  socklen_t size = (air->family[i] == AF_INET
+				    ? INADDRSZ : IN6ADDRSZ);
+
+		  if (!((air->family[i] == AF_INET
+			 && req->ai_family == AF_INET6
+			 && (req->ai_flags & AI_V4MAPPED) != 0)
+			|| req->ai_family == AF_UNSPEC
+			|| air->family[i] == req->ai_family))
 		    {
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
+		      /* Skip over non-matching result.  */
+		      addrs += size;
+		      continue;
 		    }
-		}
 
-	      if (rc == 0)
-		{
-		  if (h != NULL)
+		  if (*pat == NULL)
+		    {
+		      *pat = addrfree++;
+		      (*pat)->scopeid = 0;
+		    }
+		  uint32_t *pataddr = (*pat)->addr;
+		  (*pat)->next = NULL;
+		  if (added_canon || air->canon == NULL)
+		    (*pat)->name = NULL;
+		  else if (res.canon == NULL)
 		    {
-		      /* We found data, convert it.  */
-		      if (!convert_hostent_to_gaih_addrtuple
-			  (req, AF_INET, h, &addrmem))
+		      char *canonbuf = __strdup (air->canon);
+		      if (canonbuf == NULL)
 			{
 			  result = -EAI_MEMORY;
 			  goto free_and_return;
 			}
-		      at = addrmem;
+		      res.canon = (*pat)->name = canonbuf;
 		    }
-		  else
+
+		  if (air->family[i] == AF_INET
+		      && req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED))
 		    {
-		      if (h_errno == NO_DATA)
-			result = -EAI_NODATA;
-		      else
-			result = -EAI_NONAME;
-		      goto free_and_return;
+		      (*pat)->family = AF_INET6;
+		      pataddr[3] = *(uint32_t *) addrs;
+		      pataddr[2] = htonl (0xffff);
+		      pataddr[1] = 0;
+		      pataddr[0] = 0;
+		      pat = &((*pat)->next);
+		      added_canon = true;
+		    }
+		  else if (req->ai_family == AF_UNSPEC
+			   || air->family[i] == req->ai_family)
+		    {
+		      (*pat)->family = air->family[i];
+		      memcpy (pataddr, addrs, size);
+		      pat = &((*pat)->next);
+		      added_canon = true;
+		      if (air->family[i] == AF_INET6)
+			got_ipv6 = true;
 		    }
+		  addrs += size;
 		}
-	      else
-		{
-		  if (h_errno == NETDB_INTERNAL)
-		    result = -EAI_SYSTEM;
-		  else if (h_errno == TRY_AGAIN)
-		    result = -EAI_AGAIN;
-		  else
-		    /* We made requests but they turned out no data.
-		       The name is known, though.  */
-		    result = -EAI_NODATA;
 
-		  goto free_and_return;
-		}
+	      free (air);
 
 	      goto process_list;
 	    }
+	  else if (err == 0)
+	    /* The database contains a negative entry.  */
+	    goto free_and_return;
+	  else if (__nss_not_use_nscd_hosts == 0)
+	    {
+	      if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+		result = -EAI_MEMORY;
+	      else if (h_errno == TRY_AGAIN)
+		result = -EAI_AGAIN;
+	      else
+		result = -EAI_SYSTEM;
 
-#ifdef USE_NSCD
-	  if (__nss_not_use_nscd_hosts > 0
-	      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-	    __nss_not_use_nscd_hosts = 0;
+	      goto free_and_return;
+	    }
+	}
+#endif
+
+      no_more = !__nss_database_get (nss_database_hosts, &nip);
 
-	  if (!__nss_not_use_nscd_hosts
-	      && !__nss_database_custom[NSS_DBSIDX_hosts])
+      /* If we are looking for both IPv4 and IPv6 address we don't
+	 want the lookup functions to automatically promote IPv4
+	 addresses to IPv6 addresses, so we use the no_inet6
+	 function variant.  */
+      res_ctx = __resolv_context_get ();
+      if (res_ctx == NULL)
+	no_more = 1;
+
+      while (!no_more)
+	{
+	  /* Always start afresh; continue should discard previous results
+	     and the hosts database does not support merge.  */
+	  res.at = NULL;
+	  free (res.canon);
+	  free (addrmem);
+	  res.canon = NULL;
+	  addrmem = NULL;
+
+	  if (do_merge)
 	    {
-	      /* Try to use nscd.  */
-	      struct nscd_ai_result *air = NULL;
-	      int err = __nscd_getai (name, &air, &h_errno);
-	      if (air != NULL)
+	      __set_h_errno (NETDB_INTERNAL);
+	      __set_errno (EBUSY);
+	      break;
+	    }
+
+	  no_data = 0;
+	  nss_gethostbyname4_r *fct4 = NULL;
+
+	  /* gethostbyname4_r sends out parallel A and AAAA queries and
+	     is thus only suitable for PF_UNSPEC.  */
+	  if (req->ai_family == PF_UNSPEC)
+	    fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+
+	  if (fct4 != NULL)
+	    {
+	      while (1)
 		{
-		  /* Transform into gaih_addrtuple list.  */
-		  bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
-		  char *addrs = air->addrs;
+		  status = DL_CALL_FCT (fct4, (name, &res.at,
+					       tmpbuf->data, tmpbuf->length,
+					       &errno, &h_errno,
+					       NULL));
+		  if (status == NSS_STATUS_SUCCESS)
+		    break;
+		  /* gethostbyname4_r may write into AT, so reset it.  */
+		  res.at = NULL;
+		  if (status != NSS_STATUS_TRYAGAIN
+		      || errno != ERANGE || h_errno != NETDB_INTERNAL)
+		    {
+		      if (h_errno == TRY_AGAIN)
+			no_data = EAI_AGAIN;
+		      else
+			no_data = h_errno == NO_DATA;
+		      break;
+		    }
 
-		  addrmem = calloc (air->naddrs, sizeof (*addrmem));
-		  if (addrmem == NULL)
+		  if (!scratch_buffer_grow (tmpbuf))
 		    {
+		      __resolv_context_put (res_ctx);
 		      result = -EAI_MEMORY;
 		      goto free_and_return;
 		    }
+		}
 
-		  struct gaih_addrtuple *addrfree = addrmem;
-		  struct gaih_addrtuple **pat = &at;
+	      if (status == NSS_STATUS_SUCCESS)
+		{
+		  assert (!no_data);
+		  no_data = 1;
 
-		  for (int i = 0; i < air->naddrs; ++i)
+		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
 		    {
-		      socklen_t size = (air->family[i] == AF_INET
-					? INADDRSZ : IN6ADDRSZ);
-
-		      if (!((air->family[i] == AF_INET
-			     && req->ai_family == AF_INET6
-			     && (req->ai_flags & AI_V4MAPPED) != 0)
-			    || req->ai_family == AF_UNSPEC
-			    || air->family[i] == req->ai_family))
+		      char *canonbuf = __strdup (res.at->name);
+		      if (canonbuf == NULL)
 			{
-			  /* Skip over non-matching result.  */
-			  addrs += size;
-			  continue;
+			  __resolv_context_put (res_ctx);
+			  result = -EAI_MEMORY;
+			  goto free_and_return;
 			}
+		      res.canon = canonbuf;
+		    }
 
-		      if (*pat == NULL)
-			{
-			  *pat = addrfree++;
-			  (*pat)->scopeid = 0;
-			}
-		      uint32_t *pataddr = (*pat)->addr;
-		      (*pat)->next = NULL;
-		      if (added_canon || air->canon == NULL)
-			(*pat)->name = NULL;
-		      else if (canon == NULL)
-			{
-			  char *canonbuf = __strdup (air->canon);
-			  if (canonbuf == NULL)
-			    {
-			      result = -EAI_MEMORY;
-			      goto free_and_return;
-			    }
-			  canon = (*pat)->name = canonbuf;
-			}
+		  struct gaih_addrtuple **pat = &res.at;
 
-		      if (air->family[i] == AF_INET
+		  while (*pat != NULL)
+		    {
+		      if ((*pat)->family == AF_INET
 			  && req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED))
+			  && (req->ai_flags & AI_V4MAPPED) != 0)
 			{
+			  uint32_t *pataddr = (*pat)->addr;
 			  (*pat)->family = AF_INET6;
-			  pataddr[3] = *(uint32_t *) addrs;
+			  pataddr[3] = pataddr[0];
 			  pataddr[2] = htonl (0xffff);
 			  pataddr[1] = 0;
 			  pataddr[0] = 0;
 			  pat = &((*pat)->next);
-			  added_canon = true;
+			  no_data = 0;
 			}
 		      else if (req->ai_family == AF_UNSPEC
-			       || air->family[i] == req->ai_family)
+			       || (*pat)->family == req->ai_family)
 			{
-			  (*pat)->family = air->family[i];
-			  memcpy (pataddr, addrs, size);
 			  pat = &((*pat)->next);
-			  added_canon = true;
-			  if (air->family[i] == AF_INET6)
+
+			  no_data = 0;
+			  if (req->ai_family == AF_INET6)
 			    got_ipv6 = true;
 			}
-		      addrs += size;
+		      else
+			*pat = ((*pat)->next);
 		    }
-
-		  free (air);
-
-		  goto process_list;
 		}
-	      else if (err == 0)
-		/* The database contains a negative entry.  */
-		goto free_and_return;
-	      else if (__nss_not_use_nscd_hosts == 0)
-		{
-		  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-		    result = -EAI_MEMORY;
-		  else if (h_errno == TRY_AGAIN)
-		    result = -EAI_AGAIN;
-		  else
-		    result = -EAI_SYSTEM;
 
-		  goto free_and_return;
-		}
+	      no_inet6_data = no_data;
 	    }
-#endif
-
-	  no_more = !__nss_database_get (nss_database_hosts, &nip);
-
-	  /* If we are looking for both IPv4 and IPv6 address we don't
-	     want the lookup functions to automatically promote IPv4
-	     addresses to IPv6 addresses, so we use the no_inet6
-	     function variant.  */
-	  res_ctx = __resolv_context_get ();
-	  if (res_ctx == NULL)
-	    no_more = 1;
-
-	  while (!no_more)
+	  else
 	    {
-	      /* Always start afresh; continue should discard previous results
-		 and the hosts database does not support merge.  */
-	      at = NULL;
-	      free (canon);
-	      free (addrmem);
-	      canon = NULL;
-	      addrmem = NULL;
-
-	      if (do_merge)
+	      nss_gethostbyname3_r *fct = NULL;
+	      if (req->ai_flags & AI_CANONNAME)
+		/* No need to use this function if we do not look for
+		   the canonical name.  The function does not exist in
+		   all NSS modules and therefore the lookup would
+		   often fail.  */
+		fct = __nss_lookup_function (nip, "gethostbyname3_r");
+	      if (fct == NULL)
+		/* We are cheating here.  The gethostbyname2_r
+		   function does not have the same interface as
+		   gethostbyname3_r but the extra arguments the
+		   latter takes are added at the end.  So the
+		   gethostbyname2_r code will just ignore them.  */
+		fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	      if (fct != NULL)
 		{
-		  __set_h_errno (NETDB_INTERNAL);
-		  __set_errno (EBUSY);
-		  break;
-		}
-
-	      no_data = 0;
-	      nss_gethostbyname4_r *fct4 = NULL;
-
-	      /* gethostbyname4_r sends out parallel A and AAAA queries and
-		 is thus only suitable for PF_UNSPEC.  */
-	      if (req->ai_family == PF_UNSPEC)
-		fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+		  struct gaih_addrtuple **pat = &res.at;
 
-	      if (fct4 != NULL)
-		{
-		  while (1)
+		  if (req->ai_family == AF_INET6
+		      || req->ai_family == AF_UNSPEC)
 		    {
-		      status = DL_CALL_FCT (fct4, (name, &at,
-						   tmpbuf->data, tmpbuf->length,
-						   &errno, &h_errno,
-						   NULL));
-		      if (status == NSS_STATUS_SUCCESS)
-			break;
-		      /* gethostbyname4_r may write into AT, so reset it.  */
-		      at = NULL;
-		      if (status != NSS_STATUS_TRYAGAIN
-			  || errno != ERANGE || h_errno != NETDB_INTERNAL)
-			{
-			  if (h_errno == TRY_AGAIN)
-			    no_data = EAI_AGAIN;
-			  else
-			    no_data = h_errno == NO_DATA;
-			  break;
-			}
+		      gethosts (AF_INET6);
+		      no_inet6_data = no_data;
+		      inet6_status = status;
+		    }
+		  if (req->ai_family == AF_INET
+		      || req->ai_family == AF_UNSPEC
+		      || (req->ai_family == AF_INET6
+			  && (req->ai_flags & AI_V4MAPPED)
+			  /* Avoid generating the mapped addresses if we
+			     know we are not going to need them.  */
+			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+		    {
+		      gethosts (AF_INET);
 
-		      if (!scratch_buffer_grow (tmpbuf))
+		      if (req->ai_family == AF_INET)
 			{
-			  __resolv_context_put (res_ctx);
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
+			  no_inet6_data = no_data;
+			  inet6_status = status;
 			}
 		    }
 
-		  if (status == NSS_STATUS_SUCCESS)
+		  /* If we found one address for AF_INET or AF_INET6,
+		     don't continue the search.  */
+		  if (inet6_status == NSS_STATUS_SUCCESS
+		      || status == NSS_STATUS_SUCCESS)
 		    {
-		      assert (!no_data);
-		      no_data = 1;
-
-		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
+		      if ((req->ai_flags & AI_CANONNAME) != 0
+			  && res.canon == NULL)
 			{
-			  char *canonbuf = __strdup (at->name);
+			  char *canonbuf = getcanonname (nip, res.at, name);
 			  if (canonbuf == NULL)
 			    {
 			      __resolv_context_put (res_ctx);
 			      result = -EAI_MEMORY;
 			      goto free_and_return;
 			    }
-			  canon = canonbuf;
-			}
-
-		      struct gaih_addrtuple **pat = &at;
-
-		      while (*pat != NULL)
-			{
-			  if ((*pat)->family == AF_INET
-			      && req->ai_family == AF_INET6
-			      && (req->ai_flags & AI_V4MAPPED) != 0)
-			    {
-			      uint32_t *pataddr = (*pat)->addr;
-			      (*pat)->family = AF_INET6;
-			      pataddr[3] = pataddr[0];
-			      pataddr[2] = htonl (0xffff);
-			      pataddr[1] = 0;
-			      pataddr[0] = 0;
-			      pat = &((*pat)->next);
-			      no_data = 0;
-			    }
-			  else if (req->ai_family == AF_UNSPEC
-				   || (*pat)->family == req->ai_family)
-			    {
-			      pat = &((*pat)->next);
-
-			      no_data = 0;
-			      if (req->ai_family == AF_INET6)
-				got_ipv6 = true;
-			    }
-			  else
-			    *pat = ((*pat)->next);
-			}
-		    }
-
-		  no_inet6_data = no_data;
-		}
-	      else
-		{
-		  nss_gethostbyname3_r *fct = NULL;
-		  if (req->ai_flags & AI_CANONNAME)
-		    /* No need to use this function if we do not look for
-		       the canonical name.  The function does not exist in
-		       all NSS modules and therefore the lookup would
-		       often fail.  */
-		    fct = __nss_lookup_function (nip, "gethostbyname3_r");
-		  if (fct == NULL)
-		    /* We are cheating here.  The gethostbyname2_r
-		       function does not have the same interface as
-		       gethostbyname3_r but the extra arguments the
-		       latter takes are added at the end.  So the
-		       gethostbyname2_r code will just ignore them.  */
-		    fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-		  if (fct != NULL)
-		    {
-		      struct gaih_addrtuple **pat = &at;
-
-		      if (req->ai_family == AF_INET6
-			  || req->ai_family == AF_UNSPEC)
-			{
-			  gethosts (AF_INET6);
-			  no_inet6_data = no_data;
-			  inet6_status = status;
-			}
-		      if (req->ai_family == AF_INET
-			  || req->ai_family == AF_UNSPEC
-			  || (req->ai_family == AF_INET6
-			      && (req->ai_flags & AI_V4MAPPED)
-			      /* Avoid generating the mapped addresses if we
-				 know we are not going to need them.  */
-			      && ((req->ai_flags & AI_ALL) || !got_ipv6)))
-			{
-			  gethosts (AF_INET);
-
-			  if (req->ai_family == AF_INET)
-			    {
-			      no_inet6_data = no_data;
-			      inet6_status = status;
-			    }
-			}
-
-		      /* If we found one address for AF_INET or AF_INET6,
-			 don't continue the search.  */
-		      if (inet6_status == NSS_STATUS_SUCCESS
-			  || status == NSS_STATUS_SUCCESS)
-			{
-			  if ((req->ai_flags & AI_CANONNAME) != 0
-			      && canon == NULL)
-			    {
-			      char *canonbuf = getcanonname (nip, at, name);
-			      if (canonbuf == NULL)
-				{
-				  __resolv_context_put (res_ctx);
-				  result = -EAI_MEMORY;
-				  goto free_and_return;
-				}
-			      canon = canonbuf;
-			    }
-			  status = NSS_STATUS_SUCCESS;
-			}
-		      else
-			{
-			  /* We can have different states for AF_INET and
-			     AF_INET6.  Try to find a useful one for both.  */
-			  if (inet6_status == NSS_STATUS_TRYAGAIN)
-			    status = NSS_STATUS_TRYAGAIN;
-			  else if (status == NSS_STATUS_UNAVAIL
-				   && inet6_status != NSS_STATUS_UNAVAIL)
-			    status = inet6_status;
+			  res.canon = canonbuf;
 			}
+		      status = NSS_STATUS_SUCCESS;
 		    }
 		  else
 		    {
-		      /* Could not locate any of the lookup functions.
-			 The NSS lookup code does not consistently set
-			 errno, so we need to supply our own error
-			 code here.  The root cause could either be a
-			 resource allocation failure, or a missing
-			 service function in the DSO (so it should not
-			 be listed in /etc/nsswitch.conf).  Assume the
-			 former, and return EBUSY.  */
-		      status = NSS_STATUS_UNAVAIL;
-		     __set_h_errno (NETDB_INTERNAL);
-		     __set_errno (EBUSY);
+		      /* We can have different states for AF_INET and
+			 AF_INET6.  Try to find a useful one for both.  */
+		      if (inet6_status == NSS_STATUS_TRYAGAIN)
+			status = NSS_STATUS_TRYAGAIN;
+		      else if (status == NSS_STATUS_UNAVAIL
+			       && inet6_status != NSS_STATUS_UNAVAIL)
+			status = inet6_status;
 		    }
 		}
+	      else
+		{
+		  /* Could not locate any of the lookup functions.
+		     The NSS lookup code does not consistently set
+		     errno, so we need to supply our own error
+		     code here.  The root cause could either be a
+		     resource allocation failure, or a missing
+		     service function in the DSO (so it should not
+		     be listed in /etc/nsswitch.conf).  Assume the
+		     former, and return EBUSY.  */
+		  status = NSS_STATUS_UNAVAIL;
+		  __set_h_errno (NETDB_INTERNAL);
+		  __set_errno (EBUSY);
+		}
+	    }
 
-	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-		break;
+	  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	    break;
 
-	      /* The hosts database does not support MERGE.  */
-	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
-		do_merge = true;
+	  /* The hosts database does not support MERGE.  */
+	  if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+	    do_merge = true;
 
-	      nip++;
-	      if (nip->module == NULL)
-		no_more = -1;
-	    }
+	  nip++;
+	  if (nip->module == NULL)
+	    no_more = -1;
+	}
 
-	  __resolv_context_put (res_ctx);
+      __resolv_context_put (res_ctx);
 
-	  /* If we have a failure which sets errno, report it using
-	     EAI_SYSTEM.  */
-	  if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
-	      && h_errno == NETDB_INTERNAL)
-	    {
-	      result = -EAI_SYSTEM;
-	      goto free_and_return;
-	    }
+      /* If we have a failure which sets errno, report it using
+	 EAI_SYSTEM.  */
+      if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+	  && h_errno == NETDB_INTERNAL)
+	{
+	  result = -EAI_SYSTEM;
+	  goto free_and_return;
+	}
 
-	  if (no_data != 0 && no_inet6_data != 0)
-	    {
-	      /* If both requests timed out report this.  */
-	      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		/* We made requests but they turned out no data.  The name
-		   is known, though.  */
-		result = -EAI_NODATA;
+      if (no_data != 0 && no_inet6_data != 0)
+	{
+	  /* If both requests timed out report this.  */
+	  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+	    result = -EAI_AGAIN;
+	  else
+	    /* We made requests but they turned out no data.  The name
+	       is known, though.  */
+	    result = -EAI_NODATA;
 
-	      goto free_and_return;
-	    }
+	  goto free_and_return;
 	}
 
     process_list:
-      if (at == NULL)
+      if (res.at == NULL)
 	{
 	  result = -EAI_NONAME;
 	  goto free_and_return;
@@ -1031,21 +1043,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
   else
     {
       struct gaih_addrtuple *atr;
-      atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      memset (at, '\0', sizeof (struct gaih_addrtuple));
+      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
+				     alloca_used);
+      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
 
       if (req->ai_family == AF_UNSPEC)
 	{
-	  at->next = __alloca (sizeof (struct gaih_addrtuple));
-	  memset (at->next, '\0', sizeof (struct gaih_addrtuple));
+	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
+	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
 	}
 
       if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
 	{
-	  at->family = AF_INET6;
+	  res.at->family = AF_INET6;
 	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
-	  atr = at->next;
+	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
+	  atr = res.at->next;
 	}
 
       if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
@@ -1058,10 +1071,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   {
     /* Set up the canonical name if we need it.  */
-    if ((result = process_canonname (req, orig_name, &canon)) != 0)
+    if ((result = process_canonname (req, orig_name, &res)) != 0)
       goto free_and_return;
 
-    struct gaih_addrtuple *at2 = at;
+    struct gaih_addrtuple *at2 = res.at;
     size_t socklen;
     sa_family_t family;
 
@@ -1104,8 +1117,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    ai->ai_addr = (void *) (ai + 1);
 
 	    /* We only add the canonical name once.  */
-	    ai->ai_canonname = (char *) canon;
-	    canon = NULL;
+	    ai->ai_canonname = res.canon;
+	    res.canon = NULL;
 
 #ifdef _HAVE_SA_LEN
 	    ai->ai_addr->sa_len = socklen;
@@ -1151,7 +1164,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-  free (canon);
+  free (res.canon);
 
   return result;
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 06/12] gaih_inet: Split simple gethostbyname into its own function
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (4 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Add a free_at flag in gaih_result to indicate if res.at needs to be
freed by the caller.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 125 ++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 63 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 868c7d222f..2024464f52 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -120,6 +120,7 @@ struct gaih_result
 {
   struct gaih_addrtuple *at;
   char *canon;
+  bool free_at;
 };
 
 /* Values for `protoflag'.  */
@@ -565,6 +566,60 @@ out:
   return result;
 }
 
+/* If possible, call the simple, old functions, which do not support IPv6 scope
+   ids, nor retrieving the canonical name.  */
+
+static int
+try_simple_gethostbyname (const char *name, const struct addrinfo *req,
+			  struct scratch_buffer *tmpbuf,
+			  struct gaih_result *res)
+{
+  res->at = NULL;
+
+  if (req->ai_family != AF_INET || (req->ai_flags & AI_CANONNAME) != 0)
+    return 0;
+
+  int rc;
+  struct hostent th;
+  struct hostent *h;
+
+  while (1)
+    {
+      rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf->data,
+			       tmpbuf->length, &h, &h_errno);
+      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
+	break;
+      if (!scratch_buffer_grow (tmpbuf))
+	return -EAI_MEMORY;
+    }
+
+  if (rc == 0)
+    {
+      if (h != NULL)
+	{
+	  /* We found data, convert it.  */
+	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
+	    return -EAI_MEMORY;
+
+	  res->free_at = true;
+	  return 0;
+	}
+      if (h_errno == NO_DATA)
+	return -EAI_NODATA;
+
+      return -EAI_NONAME;
+    }
+
+  if (h_errno == NETDB_INTERNAL)
+    return -EAI_SYSTEM;
+  if (h_errno == TRY_AGAIN)
+    return -EAI_AGAIN;
+
+  /* We made requests but they turned out no data.
+     The name is known, though.  */
+  return -EAI_NODATA;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -610,6 +665,11 @@ gaih_inet (const char *name, const struct gaih_service *service,
       else if (res.at != NULL)
 	goto process_list;
 
+      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+
       int no_data = 0;
       int no_inet6_data = 0;
       nss_action_list nip;
@@ -619,69 +679,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
       struct resolv_context *res_ctx = NULL;
       bool do_merge = false;
 
-      /* If we do not have to look for IPv6 addresses or the canonical
-	 name, use the simple, old functions, which do not support
-	 IPv6 scope ids, nor retrieving the canonical name.  */
-      if (req->ai_family == AF_INET
-	  && (req->ai_flags & AI_CANONNAME) == 0)
-	{
-	  int rc;
-	  struct hostent th;
-	  struct hostent *h;
-
-	  while (1)
-	    {
-	      rc = __gethostbyname2_r (name, AF_INET, &th,
-				       tmpbuf->data, tmpbuf->length,
-				       &h, &h_errno);
-	      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
-		break;
-	      if (!scratch_buffer_grow (tmpbuf))
-		{
-		  result = -EAI_MEMORY;
-		  goto free_and_return;
-		}
-	    }
-
-	  if (rc == 0)
-	    {
-	      if (h != NULL)
-		{
-		  /* We found data, convert it.  */
-		  if (!convert_hostent_to_gaih_addrtuple
-		      (req, AF_INET, h, &addrmem))
-		    {
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
-		    }
-		  res.at = addrmem;
-		}
-	      else
-		{
-		  if (h_errno == NO_DATA)
-		    result = -EAI_NODATA;
-		  else
-		    result = -EAI_NONAME;
-		  goto free_and_return;
-		}
-	    }
-	  else
-	    {
-	      if (h_errno == NETDB_INTERNAL)
-		result = -EAI_SYSTEM;
-	      else if (h_errno == TRY_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		/* We made requests but they turned out no data.
-		   The name is known, though.  */
-		result = -EAI_NODATA;
-
-	      goto free_and_return;
-	    }
-
-	  goto process_list;
-	}
-
 #ifdef USE_NSCD
       if (__nss_not_use_nscd_hosts > 0
 	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
@@ -1164,6 +1161,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
+  if (res.free_at)
+    free (res.at);
   free (res.canon);
 
   return result;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 07/12] gaih_inet: Split nscd lookup code into its own function.
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (5 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Add a new member got_ipv6 to indicate if the results have an IPv6
result and use it instead of the local got_ipv6.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 248 +++++++++++++++++++-----------------
 1 file changed, 134 insertions(+), 114 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 2024464f52..2ec5a7d76f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -121,6 +121,7 @@ struct gaih_result
   struct gaih_addrtuple *at;
   char *canon;
   bool free_at;
+  bool got_ipv6;
 };
 
 /* Values for `protoflag'.  */
@@ -316,7 +317,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	  res.canon = canonbuf;						      \
 	}								      \
       if (_family == AF_INET6 && *pat != NULL)				      \
-	got_ipv6 = true;						      \
+	res.got_ipv6 = true;						      \
     }									      \
  }
 
@@ -467,6 +468,128 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
   return 0;
 }
 
+#ifdef USE_NSCD
+/* Query addresses from nscd cache, returning a non-zero value on error.
+   RES members have the lookup result; RES->AT is NULL if there were no errors
+   but also no results.  */
+
+static int
+get_nscd_addresses (const char *name, const struct addrinfo *req,
+		    struct gaih_result *res)
+{
+  if (__nss_not_use_nscd_hosts > 0
+      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+    __nss_not_use_nscd_hosts = 0;
+
+  res->at = NULL;
+
+  if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts])
+    return 0;
+
+  /* Try to use nscd.  */
+  struct nscd_ai_result *air = NULL;
+  int err = __nscd_getai (name, &air, &h_errno);
+
+  if (__glibc_unlikely (air == NULL))
+    {
+      /* The database contains a negative entry.  */
+      if (err == 0)
+	return -EAI_NONAME;
+      if (__nss_not_use_nscd_hosts == 0)
+	{
+	  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+	    return -EAI_MEMORY;
+	  if (h_errno == TRY_AGAIN)
+	    return -EAI_AGAIN;
+	  return -EAI_SYSTEM;
+	}
+      return 0;
+    }
+
+  /* Transform into gaih_addrtuple list.  */
+  int result = 0;
+  char *addrs = air->addrs;
+
+  struct gaih_addrtuple *addrfree = calloc (air->naddrs, sizeof (*addrfree));
+  struct gaih_addrtuple *at = calloc (air->naddrs, sizeof (*at));
+  if (at == NULL)
+    {
+      result = -EAI_MEMORY;
+      goto out;
+    }
+
+  res->free_at = true;
+
+  int count = 0;
+  for (int i = 0; i < air->naddrs; ++i)
+    {
+      socklen_t size = (air->family[i] == AF_INET
+			? INADDRSZ : IN6ADDRSZ);
+
+      if (!((air->family[i] == AF_INET
+	     && req->ai_family == AF_INET6
+	     && (req->ai_flags & AI_V4MAPPED) != 0)
+	    || req->ai_family == AF_UNSPEC
+	    || air->family[i] == req->ai_family))
+	{
+	  /* Skip over non-matching result.  */
+	  addrs += size;
+	  continue;
+	}
+
+      if (air->family[i] == AF_INET && req->ai_family == AF_INET6
+	  && (req->ai_flags & AI_V4MAPPED))
+	{
+	  at[count].family = AF_INET6;
+	  at[count].addr[3] = *(uint32_t *) addrs;
+	  at[count].addr[2] = htonl (0xffff);
+	}
+      else if (req->ai_family == AF_UNSPEC
+	       || air->family[count] == req->ai_family)
+	{
+	  at[count].family = air->family[count];
+	  memcpy (at[count].addr, addrs, size);
+	  if (air->family[count] == AF_INET6)
+	    res->got_ipv6 = true;
+	}
+      at[count].next = at + count + 1;
+      count++;
+      addrs += size;
+    }
+
+  if ((req->ai_flags & AI_CANONNAME) && air->canon != NULL)
+    {
+      char *canonbuf = __strdup (air->canon);
+      if (canonbuf == NULL)
+	{
+	  result = -EAI_MEMORY;
+	  goto out;
+	}
+      res->canon = canonbuf;
+    }
+
+  if (count == 0)
+    {
+      result = -EAI_NONAME;
+      goto out;
+    }
+
+  at[count - 1].next = NULL;
+
+  res->at = at;
+
+out:
+  free (air);
+  if (result != 0)
+    {
+      free (at);
+      res->free_at = false;
+    }
+
+  return result;
+}
+#endif
+
 /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
    NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
    the function cannot determine a result, RES->AT is set to NULL and 0
@@ -628,7 +751,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
 			   / sizeof (struct gaih_typeproto)] = {0};
 
-  bool got_ipv6 = false;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -670,6 +792,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
       else if (res.at != NULL)
 	goto process_list;
 
+#ifdef USE_NSCD
+      if ((result = get_nscd_addresses (name, req, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+#endif
+
       int no_data = 0;
       int no_inet6_data = 0;
       nss_action_list nip;
@@ -679,115 +808,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
       struct resolv_context *res_ctx = NULL;
       bool do_merge = false;
 
-#ifdef USE_NSCD
-      if (__nss_not_use_nscd_hosts > 0
-	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-	__nss_not_use_nscd_hosts = 0;
-
-      if (!__nss_not_use_nscd_hosts
-	  && !__nss_database_custom[NSS_DBSIDX_hosts])
-	{
-	  /* Try to use nscd.  */
-	  struct nscd_ai_result *air = NULL;
-	  int err = __nscd_getai (name, &air, &h_errno);
-	  if (air != NULL)
-	    {
-	      /* Transform into gaih_addrtuple list.  */
-	      bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
-	      char *addrs = air->addrs;
-
-	      addrmem = calloc (air->naddrs, sizeof (*addrmem));
-	      if (addrmem == NULL)
-		{
-		  result = -EAI_MEMORY;
-		  goto free_and_return;
-		}
-
-	      struct gaih_addrtuple *addrfree = addrmem;
-	      struct gaih_addrtuple **pat = &res.at;
-
-	      for (int i = 0; i < air->naddrs; ++i)
-		{
-		  socklen_t size = (air->family[i] == AF_INET
-				    ? INADDRSZ : IN6ADDRSZ);
-
-		  if (!((air->family[i] == AF_INET
-			 && req->ai_family == AF_INET6
-			 && (req->ai_flags & AI_V4MAPPED) != 0)
-			|| req->ai_family == AF_UNSPEC
-			|| air->family[i] == req->ai_family))
-		    {
-		      /* Skip over non-matching result.  */
-		      addrs += size;
-		      continue;
-		    }
-
-		  if (*pat == NULL)
-		    {
-		      *pat = addrfree++;
-		      (*pat)->scopeid = 0;
-		    }
-		  uint32_t *pataddr = (*pat)->addr;
-		  (*pat)->next = NULL;
-		  if (added_canon || air->canon == NULL)
-		    (*pat)->name = NULL;
-		  else if (res.canon == NULL)
-		    {
-		      char *canonbuf = __strdup (air->canon);
-		      if (canonbuf == NULL)
-			{
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
-			}
-		      res.canon = (*pat)->name = canonbuf;
-		    }
-
-		  if (air->family[i] == AF_INET
-		      && req->ai_family == AF_INET6
-		      && (req->ai_flags & AI_V4MAPPED))
-		    {
-		      (*pat)->family = AF_INET6;
-		      pataddr[3] = *(uint32_t *) addrs;
-		      pataddr[2] = htonl (0xffff);
-		      pataddr[1] = 0;
-		      pataddr[0] = 0;
-		      pat = &((*pat)->next);
-		      added_canon = true;
-		    }
-		  else if (req->ai_family == AF_UNSPEC
-			   || air->family[i] == req->ai_family)
-		    {
-		      (*pat)->family = air->family[i];
-		      memcpy (pataddr, addrs, size);
-		      pat = &((*pat)->next);
-		      added_canon = true;
-		      if (air->family[i] == AF_INET6)
-			got_ipv6 = true;
-		    }
-		  addrs += size;
-		}
-
-	      free (air);
-
-	      goto process_list;
-	    }
-	  else if (err == 0)
-	    /* The database contains a negative entry.  */
-	    goto free_and_return;
-	  else if (__nss_not_use_nscd_hosts == 0)
-	    {
-	      if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-		result = -EAI_MEMORY;
-	      else if (h_errno == TRY_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		result = -EAI_SYSTEM;
-
-	      goto free_and_return;
-	    }
-	}
-#endif
-
       no_more = !__nss_database_get (nss_database_hosts, &nip);
 
       /* If we are looking for both IPv4 and IPv6 address we don't
@@ -894,7 +914,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 			  no_data = 0;
 			  if (req->ai_family == AF_INET6)
-			    got_ipv6 = true;
+			    res.got_ipv6 = true;
 			}
 		      else
 			*pat = ((*pat)->next);
@@ -937,7 +957,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  && (req->ai_flags & AI_V4MAPPED)
 			  /* Avoid generating the mapped addresses if we
 			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))
 		    {
 		      gethosts (AF_INET);
 
@@ -1088,7 +1108,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    /* If we looked up IPv4 mapped address discard them here if
 	       the caller isn't interested in all address and we have
 	       found at least one IPv6 address.  */
-	    if (got_ipv6
+	    if (res.got_ipv6
 		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
 		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
 	      goto ignore;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 08/12] gaih_inet: separate nss lookup loop into its own function
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (6 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 566 ++++++++++++++++++------------------
 1 file changed, 288 insertions(+), 278 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 2ec5a7d76f..b30af6bb7b 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -159,6 +159,14 @@ static const struct addrinfo default_hints =
     .ai_next = NULL
   };
 
+static void
+gaih_result_reset (struct gaih_result *res)
+{
+  if (res->free_at)
+    free (res->at);
+  free (res->canon);
+  memset (res, 0, sizeof (*res));
+}
 
 static int
 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
@@ -195,15 +203,13 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
   return 0;
 }
 
-/* Convert struct hostent to a list of struct gaih_addrtuple objects.
-   h_name is not copied, and the struct hostent object must not be
-   deallocated prematurely.  *RESULT must be NULL or a pointer to a
-   linked-list.  The new addresses are appended at the end.  */
+/* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
+   is not copied, and the struct hostent object must not be deallocated
+   prematurely.  *RESULT must be NULL or a pointer to a linked-list.  The new
+   addresses are appended at the end.  */
 static bool
-convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
-				   int family,
-				   struct hostent *h,
-				   struct gaih_addrtuple **result)
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+				   struct hostent *h, struct gaih_result *res)
 {
   /* Count the number of addresses in h->h_addr_list.  */
   size_t count = 0;
@@ -215,7 +221,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
     return true;
 
-  struct gaih_addrtuple *array = *result;
+  struct gaih_addrtuple *array = res->at;
   size_t old = 0;
 
   while (array)
@@ -224,12 +230,13 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
       array = array->next;
     }
 
-  array = realloc (*result, (old + count) * sizeof (*array));
+  array = res->at = realloc (res->at, (old + count) * sizeof (*array));
 
   if (array == NULL)
     return false;
 
-  *result = array;
+  res->got_ipv6 = family == AF_INET6;
+  res->free_at = true;
 
   /* Update the next pointers on reallocation.  */
   for (size_t i = 0; i < old; i++)
@@ -278,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_MEMORY;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
     }									      \
   if (status == NSS_STATUS_NOTFOUND					      \
@@ -288,7 +295,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_SYSTEM;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
       if (h_errno == TRY_AGAIN)						      \
 	no_data = EAI_AGAIN;						      \
@@ -297,27 +304,24 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
     }									      \
   else if (status == NSS_STATUS_SUCCESS)				      \
     {									      \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem))   \
+      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_SYSTEM;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
-      *pat = addrmem;							      \
 									      \
-      if (localcanon != NULL && res.canon == NULL)			      \
+      if (localcanon != NULL && res->canon == NULL)			      \
 	{								      \
 	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
 	    {								      \
 	      __resolv_context_put (res_ctx);				      \
 	      result = -EAI_SYSTEM;					      \
-	      goto free_and_return;					      \
+	      goto out;							      \
 	    }								      \
-	  res.canon = canonbuf;						      \
+	  res->canon = canonbuf;					      \
 	}								      \
-      if (_family == AF_INET6 && *pat != NULL)				      \
-	res.got_ipv6 = true;						      \
     }									      \
  }
 
@@ -590,6 +594,260 @@ out:
 }
 #endif
 
+static int
+get_nss_addresses (const char *name, const struct addrinfo *req,
+		   struct scratch_buffer *tmpbuf, struct gaih_result *res)
+{
+  int no_data = 0;
+  int no_inet6_data = 0;
+  nss_action_list nip;
+  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+  enum nss_status status = NSS_STATUS_UNAVAIL;
+  int no_more;
+  struct resolv_context *res_ctx = NULL;
+  bool do_merge = false;
+  int result = 0;
+
+  no_more = !__nss_database_get (nss_database_hosts, &nip);
+
+  /* If we are looking for both IPv4 and IPv6 address we don't
+     want the lookup functions to automatically promote IPv4
+     addresses to IPv6 addresses, so we use the no_inet6
+     function variant.  */
+  res_ctx = __resolv_context_get ();
+  if (res_ctx == NULL)
+    no_more = 1;
+
+  while (!no_more)
+    {
+      /* Always start afresh; continue should discard previous results
+	 and the hosts database does not support merge.  */
+      gaih_result_reset (res);
+
+      if (do_merge)
+	{
+	  __set_h_errno (NETDB_INTERNAL);
+	  __set_errno (EBUSY);
+	  break;
+	}
+
+      no_data = 0;
+      nss_gethostbyname4_r *fct4 = NULL;
+
+      /* gethostbyname4_r sends out parallel A and AAAA queries and
+	 is thus only suitable for PF_UNSPEC.  */
+      if (req->ai_family == PF_UNSPEC)
+	fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+
+      if (fct4 != NULL)
+	{
+	  while (1)
+	    {
+	      status = DL_CALL_FCT (fct4, (name, &res->at,
+					   tmpbuf->data, tmpbuf->length,
+					   &errno, &h_errno,
+					   NULL));
+	      if (status == NSS_STATUS_SUCCESS)
+		break;
+	      /* gethostbyname4_r may write into AT, so reset it.  */
+	      res->at = NULL;
+	      if (status != NSS_STATUS_TRYAGAIN
+		  || errno != ERANGE || h_errno != NETDB_INTERNAL)
+		{
+		  if (h_errno == TRY_AGAIN)
+		    no_data = EAI_AGAIN;
+		  else
+		    no_data = h_errno == NO_DATA;
+		  break;
+		}
+
+	      if (!scratch_buffer_grow (tmpbuf))
+		{
+		  __resolv_context_put (res_ctx);
+		  result = -EAI_MEMORY;
+		  goto out;
+		}
+	    }
+
+	  if (status == NSS_STATUS_SUCCESS)
+	    {
+	      assert (!no_data);
+	      no_data = 1;
+
+	      if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL)
+		{
+		  char *canonbuf = __strdup (res->at->name);
+		  if (canonbuf == NULL)
+		    {
+		      __resolv_context_put (res_ctx);
+		      result = -EAI_MEMORY;
+		      goto out;
+		    }
+		  res->canon = canonbuf;
+		}
+
+	      struct gaih_addrtuple **pat = &res->at;
+
+	      while (*pat != NULL)
+		{
+		  if ((*pat)->family == AF_INET
+		      && req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED) != 0)
+		    {
+		      uint32_t *pataddr = (*pat)->addr;
+		      (*pat)->family = AF_INET6;
+		      pataddr[3] = pataddr[0];
+		      pataddr[2] = htonl (0xffff);
+		      pataddr[1] = 0;
+		      pataddr[0] = 0;
+		      pat = &((*pat)->next);
+		      no_data = 0;
+		    }
+		  else if (req->ai_family == AF_UNSPEC
+			   || (*pat)->family == req->ai_family)
+		    {
+		      pat = &((*pat)->next);
+
+		      no_data = 0;
+		      if (req->ai_family == AF_INET6)
+			res->got_ipv6 = true;
+		    }
+		  else
+		    *pat = ((*pat)->next);
+		}
+	    }
+
+	  no_inet6_data = no_data;
+	}
+      else
+	{
+	  nss_gethostbyname3_r *fct = NULL;
+	  if (req->ai_flags & AI_CANONNAME)
+	    /* No need to use this function if we do not look for
+	       the canonical name.  The function does not exist in
+	       all NSS modules and therefore the lookup would
+	       often fail.  */
+	    fct = __nss_lookup_function (nip, "gethostbyname3_r");
+	  if (fct == NULL)
+	    /* We are cheating here.  The gethostbyname2_r
+	       function does not have the same interface as
+	       gethostbyname3_r but the extra arguments the
+	       latter takes are added at the end.  So the
+	       gethostbyname2_r code will just ignore them.  */
+	    fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	  if (fct != NULL)
+	    {
+	      if (req->ai_family == AF_INET6
+		  || req->ai_family == AF_UNSPEC)
+		{
+		  gethosts (AF_INET6);
+		  no_inet6_data = no_data;
+		  inet6_status = status;
+		}
+	      if (req->ai_family == AF_INET
+		  || req->ai_family == AF_UNSPEC
+		  || (req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED)
+		      /* Avoid generating the mapped addresses if we
+			 know we are not going to need them.  */
+		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
+		{
+		  gethosts (AF_INET);
+
+		  if (req->ai_family == AF_INET)
+		    {
+		      no_inet6_data = no_data;
+		      inet6_status = status;
+		    }
+		}
+
+	      /* If we found one address for AF_INET or AF_INET6,
+		 don't continue the search.  */
+	      if (inet6_status == NSS_STATUS_SUCCESS
+		  || status == NSS_STATUS_SUCCESS)
+		{
+		  if ((req->ai_flags & AI_CANONNAME) != 0
+		      && res->canon == NULL)
+		    {
+		      char *canonbuf = getcanonname (nip, res->at, name);
+		      if (canonbuf == NULL)
+			{
+			  __resolv_context_put (res_ctx);
+			  result = -EAI_MEMORY;
+			  goto out;
+			}
+		      res->canon = canonbuf;
+		    }
+		  status = NSS_STATUS_SUCCESS;
+		}
+	      else
+		{
+		  /* We can have different states for AF_INET and
+		     AF_INET6.  Try to find a useful one for both.  */
+		  if (inet6_status == NSS_STATUS_TRYAGAIN)
+		    status = NSS_STATUS_TRYAGAIN;
+		  else if (status == NSS_STATUS_UNAVAIL
+			   && inet6_status != NSS_STATUS_UNAVAIL)
+		    status = inet6_status;
+		}
+	    }
+	  else
+	    {
+	      /* Could not locate any of the lookup functions.
+		 The NSS lookup code does not consistently set
+		 errno, so we need to supply our own error
+		 code here.  The root cause could either be a
+		 resource allocation failure, or a missing
+		 service function in the DSO (so it should not
+		 be listed in /etc/nsswitch.conf).  Assume the
+		 former, and return EBUSY.  */
+	      status = NSS_STATUS_UNAVAIL;
+	      __set_h_errno (NETDB_INTERNAL);
+	      __set_errno (EBUSY);
+	    }
+	}
+
+      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	break;
+
+      /* The hosts database does not support MERGE.  */
+      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+	do_merge = true;
+
+      nip++;
+      if (nip->module == NULL)
+	no_more = -1;
+    }
+
+  __resolv_context_put (res_ctx);
+
+  /* If we have a failure which sets errno, report it using
+     EAI_SYSTEM.  */
+  if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+      && h_errno == NETDB_INTERNAL)
+    {
+      result = -EAI_SYSTEM;
+      goto out;
+    }
+
+  if (no_data != 0 && no_inet6_data != 0)
+    {
+      /* If both requests timed out report this.  */
+      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+	result = -EAI_AGAIN;
+      else
+	/* We made requests but they turned out no data.  The name
+	   is known, though.  */
+	result = -EAI_NODATA;
+    }
+
+out:
+  if (result != 0)
+    gaih_result_reset (res);
+  return result;
+}
+
 /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
    NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
    the function cannot determine a result, RES->AT is set to NULL and 0
@@ -721,7 +979,7 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
       if (h != NULL)
 	{
 	  /* We found data, convert it.  */
-	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
+	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res))
 	    return -EAI_MEMORY;
 
 	  res->free_at = true;
@@ -799,263 +1057,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	goto process_list;
 #endif
 
-      int no_data = 0;
-      int no_inet6_data = 0;
-      nss_action_list nip;
-      enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
-      enum nss_status status = NSS_STATUS_UNAVAIL;
-      int no_more;
-      struct resolv_context *res_ctx = NULL;
-      bool do_merge = false;
-
-      no_more = !__nss_database_get (nss_database_hosts, &nip);
-
-      /* If we are looking for both IPv4 and IPv6 address we don't
-	 want the lookup functions to automatically promote IPv4
-	 addresses to IPv6 addresses, so we use the no_inet6
-	 function variant.  */
-      res_ctx = __resolv_context_get ();
-      if (res_ctx == NULL)
-	no_more = 1;
-
-      while (!no_more)
-	{
-	  /* Always start afresh; continue should discard previous results
-	     and the hosts database does not support merge.  */
-	  res.at = NULL;
-	  free (res.canon);
-	  free (addrmem);
-	  res.canon = NULL;
-	  addrmem = NULL;
-
-	  if (do_merge)
-	    {
-	      __set_h_errno (NETDB_INTERNAL);
-	      __set_errno (EBUSY);
-	      break;
-	    }
-
-	  no_data = 0;
-	  nss_gethostbyname4_r *fct4 = NULL;
-
-	  /* gethostbyname4_r sends out parallel A and AAAA queries and
-	     is thus only suitable for PF_UNSPEC.  */
-	  if (req->ai_family == PF_UNSPEC)
-	    fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
-
-	  if (fct4 != NULL)
-	    {
-	      while (1)
-		{
-		  status = DL_CALL_FCT (fct4, (name, &res.at,
-					       tmpbuf->data, tmpbuf->length,
-					       &errno, &h_errno,
-					       NULL));
-		  if (status == NSS_STATUS_SUCCESS)
-		    break;
-		  /* gethostbyname4_r may write into AT, so reset it.  */
-		  res.at = NULL;
-		  if (status != NSS_STATUS_TRYAGAIN
-		      || errno != ERANGE || h_errno != NETDB_INTERNAL)
-		    {
-		      if (h_errno == TRY_AGAIN)
-			no_data = EAI_AGAIN;
-		      else
-			no_data = h_errno == NO_DATA;
-		      break;
-		    }
-
-		  if (!scratch_buffer_grow (tmpbuf))
-		    {
-		      __resolv_context_put (res_ctx);
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
-		    }
-		}
-
-	      if (status == NSS_STATUS_SUCCESS)
-		{
-		  assert (!no_data);
-		  no_data = 1;
-
-		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
-		    {
-		      char *canonbuf = __strdup (res.at->name);
-		      if (canonbuf == NULL)
-			{
-			  __resolv_context_put (res_ctx);
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
-			}
-		      res.canon = canonbuf;
-		    }
-
-		  struct gaih_addrtuple **pat = &res.at;
-
-		  while (*pat != NULL)
-		    {
-		      if ((*pat)->family == AF_INET
-			  && req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED) != 0)
-			{
-			  uint32_t *pataddr = (*pat)->addr;
-			  (*pat)->family = AF_INET6;
-			  pataddr[3] = pataddr[0];
-			  pataddr[2] = htonl (0xffff);
-			  pataddr[1] = 0;
-			  pataddr[0] = 0;
-			  pat = &((*pat)->next);
-			  no_data = 0;
-			}
-		      else if (req->ai_family == AF_UNSPEC
-			       || (*pat)->family == req->ai_family)
-			{
-			  pat = &((*pat)->next);
-
-			  no_data = 0;
-			  if (req->ai_family == AF_INET6)
-			    res.got_ipv6 = true;
-			}
-		      else
-			*pat = ((*pat)->next);
-		    }
-		}
-
-	      no_inet6_data = no_data;
-	    }
-	  else
-	    {
-	      nss_gethostbyname3_r *fct = NULL;
-	      if (req->ai_flags & AI_CANONNAME)
-		/* No need to use this function if we do not look for
-		   the canonical name.  The function does not exist in
-		   all NSS modules and therefore the lookup would
-		   often fail.  */
-		fct = __nss_lookup_function (nip, "gethostbyname3_r");
-	      if (fct == NULL)
-		/* We are cheating here.  The gethostbyname2_r
-		   function does not have the same interface as
-		   gethostbyname3_r but the extra arguments the
-		   latter takes are added at the end.  So the
-		   gethostbyname2_r code will just ignore them.  */
-		fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-	      if (fct != NULL)
-		{
-		  struct gaih_addrtuple **pat = &res.at;
-
-		  if (req->ai_family == AF_INET6
-		      || req->ai_family == AF_UNSPEC)
-		    {
-		      gethosts (AF_INET6);
-		      no_inet6_data = no_data;
-		      inet6_status = status;
-		    }
-		  if (req->ai_family == AF_INET
-		      || req->ai_family == AF_UNSPEC
-		      || (req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED)
-			  /* Avoid generating the mapped addresses if we
-			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))
-		    {
-		      gethosts (AF_INET);
-
-		      if (req->ai_family == AF_INET)
-			{
-			  no_inet6_data = no_data;
-			  inet6_status = status;
-			}
-		    }
-
-		  /* If we found one address for AF_INET or AF_INET6,
-		     don't continue the search.  */
-		  if (inet6_status == NSS_STATUS_SUCCESS
-		      || status == NSS_STATUS_SUCCESS)
-		    {
-		      if ((req->ai_flags & AI_CANONNAME) != 0
-			  && res.canon == NULL)
-			{
-			  char *canonbuf = getcanonname (nip, res.at, name);
-			  if (canonbuf == NULL)
-			    {
-			      __resolv_context_put (res_ctx);
-			      result = -EAI_MEMORY;
-			      goto free_and_return;
-			    }
-			  res.canon = canonbuf;
-			}
-		      status = NSS_STATUS_SUCCESS;
-		    }
-		  else
-		    {
-		      /* We can have different states for AF_INET and
-			 AF_INET6.  Try to find a useful one for both.  */
-		      if (inet6_status == NSS_STATUS_TRYAGAIN)
-			status = NSS_STATUS_TRYAGAIN;
-		      else if (status == NSS_STATUS_UNAVAIL
-			       && inet6_status != NSS_STATUS_UNAVAIL)
-			status = inet6_status;
-		    }
-		}
-	      else
-		{
-		  /* Could not locate any of the lookup functions.
-		     The NSS lookup code does not consistently set
-		     errno, so we need to supply our own error
-		     code here.  The root cause could either be a
-		     resource allocation failure, or a missing
-		     service function in the DSO (so it should not
-		     be listed in /etc/nsswitch.conf).  Assume the
-		     former, and return EBUSY.  */
-		  status = NSS_STATUS_UNAVAIL;
-		  __set_h_errno (NETDB_INTERNAL);
-		  __set_errno (EBUSY);
-		}
-	    }
-
-	  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-	    break;
-
-	  /* The hosts database does not support MERGE.  */
-	  if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
-	    do_merge = true;
-
-	  nip++;
-	  if (nip->module == NULL)
-	    no_more = -1;
-	}
-
-      __resolv_context_put (res_ctx);
-
-      /* If we have a failure which sets errno, report it using
-	 EAI_SYSTEM.  */
-      if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
-	  && h_errno == NETDB_INTERNAL)
-	{
-	  result = -EAI_SYSTEM;
-	  goto free_and_return;
-	}
-
-      if (no_data != 0 && no_inet6_data != 0)
-	{
-	  /* If both requests timed out report this.  */
-	  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-	    result = -EAI_AGAIN;
-	  else
-	    /* We made requests but they turned out no data.  The name
-	       is known, though.  */
-	    result = -EAI_NODATA;
-
-	  goto free_and_return;
-	}
+      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
 
-    process_list:
-      if (res.at == NULL)
-	{
-	  result = -EAI_NONAME;
-	  goto free_and_return;
-	}
+      /* None of the lookups worked, so name not found.  */
+      result = -EAI_NONAME;
+      goto free_and_return;
     }
   else
     {
@@ -1086,6 +1095,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
     }
 
+process_list:
   {
     /* Set up the canonical name if we need it.  */
     if ((result = process_canonname (req, orig_name, &res)) != 0)
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 09/12] gaih_inet: make gethosts into a function
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (7 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

The macro is quite a pain to debug, so make gethosts into a function to
make it easier to maintain.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 117 ++++++++++++++++++------------------
 1 file changed, 59 insertions(+), 58 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index b30af6bb7b..81710632cb 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -268,63 +268,54 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
   return true;
 }
 
-#define gethosts(_family) \
- {									      \
-  struct hostent th;							      \
-  char *localcanon = NULL;						      \
-  no_data = 0;								      \
-  while (1)								      \
-    {									      \
-      status = DL_CALL_FCT (fct, (name, _family, &th,			      \
-				  tmpbuf->data, tmpbuf->length,		      \
-				  &errno, &h_errno, NULL, &localcanon));      \
-      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL	      \
-	  || errno != ERANGE)						      \
-	break;								      \
-      if (!scratch_buffer_grow (tmpbuf))				      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_MEMORY;						      \
-	  goto out;							      \
-	}								      \
-    }									      \
-  if (status == NSS_STATUS_NOTFOUND					      \
-      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)	      \
-    {									      \
-      if (h_errno == NETDB_INTERNAL)					      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_SYSTEM;						      \
-	  goto out;							      \
-	}								      \
-      if (h_errno == TRY_AGAIN)						      \
-	no_data = EAI_AGAIN;						      \
-      else								      \
-	no_data = h_errno == NO_DATA;					      \
-    }									      \
-  else if (status == NSS_STATUS_SUCCESS)				      \
-    {									      \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_SYSTEM;						      \
-	  goto out;							      \
-	}								      \
-									      \
-      if (localcanon != NULL && res->canon == NULL)			      \
-	{								      \
-	  char *canonbuf = __strdup (localcanon);			      \
-	  if (canonbuf == NULL)						      \
-	    {								      \
-	      __resolv_context_put (res_ctx);				      \
-	      result = -EAI_SYSTEM;					      \
-	      goto out;							      \
-	    }								      \
-	  res->canon = canonbuf;					      \
-	}								      \
-    }									      \
- }
+static int
+gethosts (nss_gethostbyname3_r fct, int family, const char *name,
+	  const struct addrinfo *req, struct scratch_buffer *tmpbuf,
+	  struct gaih_result *res, enum nss_status *statusp, int *no_datap)
+{
+  struct hostent th;
+  char *localcanon = NULL;
+  enum nss_status status;
+
+  *no_datap = 0;
+  while (1)
+    {
+      *statusp = status = DL_CALL_FCT (fct, (name, family, &th,
+					     tmpbuf->data, tmpbuf->length,
+					     &errno, &h_errno, NULL,
+					     &localcanon));
+      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL
+	  || errno != ERANGE)
+	break;
+      if (!scratch_buffer_grow (tmpbuf))
+	return -EAI_MEMORY;
+    }
+  if (status == NSS_STATUS_NOTFOUND
+      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+    {
+      if (h_errno == NETDB_INTERNAL)
+	return -EAI_SYSTEM;
+      if (h_errno == TRY_AGAIN)
+	*no_datap = EAI_AGAIN;
+      else
+	*no_datap = h_errno == NO_DATA;
+    }
+  else if (status == NSS_STATUS_SUCCESS)
+    {
+      if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
+	return -EAI_SYSTEM;
+
+      if (localcanon != NULL && res->canon == NULL)
+	{
+	  char *canonbuf = __strdup (localcanon);
+	  if (canonbuf == NULL)
+	    return  -EAI_SYSTEM;
+	  res->canon = canonbuf;
+	}
+    }
 
+  return 0;
+}
 
 /* This function is called if a canonical name is requested, but if
    the service function did not provide it.  It tries to obtain the
@@ -741,7 +732,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
 	      if (req->ai_family == AF_INET6
 		  || req->ai_family == AF_UNSPEC)
 		{
-		  gethosts (AF_INET6);
+		  if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf,
+					  res, &status, &no_data)) != 0)
+		    {
+		      __resolv_context_put (res_ctx);
+		      goto out;
+		    }
 		  no_inet6_data = no_data;
 		  inet6_status = status;
 		}
@@ -753,7 +749,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
 			 know we are not going to need them.  */
 		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
 		{
-		  gethosts (AF_INET);
+		  if ((result = gethosts (fct, AF_INET, name, req, tmpbuf,
+					  res, &status, &no_data)) != 0)
+		    {
+		      __resolv_context_put (res_ctx);
+		      goto out;
+		    }
 
 		  if (req->ai_family == AF_INET)
 		    {
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 10/12] gaih_inet: split loopback lookup into its own function
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (8 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Flatten the condition nesting and replace the alloca for RET.AT/ATR with
a single array LOCAL_AT[2].  This gets rid of alloca and alloca
accounting.

`git diff -b` is probably the best way to view this change since much of
the diff is whitespace changes.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 127 ++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 65 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 81710632cb..7258fa6f7e 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1002,6 +1002,32 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
   return -EAI_NODATA;
 }
 
+/* Add local address information into RES.  RES->AT is assumed to have enough
+   space for two tuples and is zeroed out.  */
+
+static void
+get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
+{
+  struct gaih_addrtuple *atr = res->at;
+  if (req->ai_family == AF_UNSPEC)
+    res->at->next = res->at + 1;
+
+  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+    {
+      res->at->family = AF_INET6;
+      if ((req->ai_flags & AI_PASSIVE) == 0)
+	memcpy (res->at->addr, &in6addr_loopback, sizeof (struct in6_addr));
+      atr = res->at->next;
+    }
+
+  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
+    {
+      atr->family = AF_INET;
+      if ((req->ai_flags & AI_PASSIVE) == 0)
+	atr->addr[0] = htonl (INADDR_LOOPBACK);
+    }
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -1012,10 +1038,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   const char *orig_name = name;
 
-  /* Reserve stack memory for the scratch buffer in the getaddrinfo
-     function.  */
-  size_t alloca_used = sizeof (struct scratch_buffer);
-
   int rc;
   if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
     return rc;
@@ -1025,76 +1047,51 @@ gaih_inet (const char *name, const struct gaih_service *service,
   int result = 0;
 
   struct gaih_result res = {0};
-  if (name != NULL)
+  struct gaih_addrtuple local_at[2] = {0};
+
+  res.at = local_at;
+
+  if (__glibc_unlikely (name == NULL))
     {
-      if (req->ai_flags & AI_IDN)
-	{
-	  char *out;
-	  result = __idna_to_dns_encoding (name, &out);
-	  if (result != 0)
-	    return -result;
-	  name = out;
-	  malloc_name = true;
-	}
+      get_local_addresses (req, &res);
+      goto process_list;
+    }
 
-      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      res.at->scopeid = 0;
-      res.at->next = NULL;
+  if (req->ai_flags & AI_IDN)
+    {
+      char *out;
+      result = __idna_to_dns_encoding (name, &out);
+      if (result != 0)
+	return -result;
+      name = out;
+      malloc_name = true;
+    }
 
-      if ((result = text_to_binary_address (name, req, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = text_to_binary_address (name, req, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
-      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
 #ifdef USE_NSCD
-      if ((result = get_nscd_addresses (name, req, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = get_nscd_addresses (name, req, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 #endif
 
-      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
-
-      /* None of the lookups worked, so name not found.  */
-      result = -EAI_NONAME;
-      goto free_and_return;
-    }
-  else
-    {
-      struct gaih_addrtuple *atr;
-      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
-				     alloca_used);
-      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
-
-      if (req->ai_family == AF_UNSPEC)
-	{
-	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
-	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
-	}
-
-      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	{
-	  res.at->family = AF_INET6;
-	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
-	  atr = res.at->next;
-	}
+  if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
-      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	{
-	  atr->family = AF_INET;
-	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    atr->addr[0] = htonl (INADDR_LOOPBACK);
-	}
-    }
+  /* None of the lookups worked, so name not found.  */
+  result = -EAI_NONAME;
+  goto free_and_return;
 
 process_list:
   {
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 11/12] gaih_inet: Split result generation into its own function
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (9 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-08 10:07 ` [PATCH 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

Simplify the loop a wee bit and clean up variable names too.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 176 ++++++++++++++++++------------------
 1 file changed, 86 insertions(+), 90 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 7258fa6f7e..6cbbd5dac1 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1028,6 +1028,87 @@ get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
     }
 }
 
+/* Generate results in PAI and its count in NADDRS.  Return 0 on success or an
+   error code on failure.  */
+
+static int
+generate_addrinfo (const struct addrinfo *req, struct gaih_result *res,
+		   const struct gaih_servtuple *st, struct addrinfo **pai,
+		   unsigned int *naddrs)
+{
+  size_t socklen;
+  sa_family_t family;
+
+  /* Buffer is the size of an unformatted IPv6 address in printable format.  */
+  for (struct gaih_addrtuple *at = res->at; at != NULL; at = at->next)
+    {
+      family = at->family;
+      if (family == AF_INET6)
+	{
+	  socklen = sizeof (struct sockaddr_in6);
+
+	  /* If we looked up IPv4 mapped address discard them here if
+	     the caller isn't interested in all address and we have
+	     found at least one IPv6 address.  */
+	  if (res->got_ipv6
+	      && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
+	      && IN6_IS_ADDR_V4MAPPED (at->addr))
+	    continue;
+	}
+      else
+	socklen = sizeof (struct sockaddr_in);
+
+      for (int i = 0; st[i].set; i++)
+	{
+	  struct addrinfo *ai;
+	  ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
+	  if (ai == NULL)
+	    return -EAI_MEMORY;
+
+	  ai->ai_flags = req->ai_flags;
+	  ai->ai_family = family;
+	  ai->ai_socktype = st[i].socktype;
+	  ai->ai_protocol = st[i].protocol;
+	  ai->ai_addrlen = socklen;
+	  ai->ai_addr = (void *) (ai + 1);
+
+	  /* We only add the canonical name once.  */
+	  ai->ai_canonname = res->canon;
+	  res->canon = NULL;
+
+#ifdef _HAVE_SA_LEN
+	  ai->ai_addr->sa_len = socklen;
+#endif /* _HAVE_SA_LEN */
+	  ai->ai_addr->sa_family = family;
+
+	  /* In case of an allocation error the list must be NULL
+	     terminated.  */
+	  ai->ai_next = NULL;
+
+	  if (family == AF_INET6)
+	    {
+	      struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) ai->ai_addr;
+	      sin6p->sin6_port = st[i].port;
+	      sin6p->sin6_flowinfo = 0;
+	      memcpy (&sin6p->sin6_addr, at->addr, sizeof (struct in6_addr));
+	      sin6p->sin6_scope_id = at->scopeid;
+	    }
+	  else
+	    {
+	      struct sockaddr_in *sinp = (struct sockaddr_in *) ai->ai_addr;
+	      sinp->sin_port = st[i].port;
+	      memcpy (&sinp->sin_addr, at->addr, sizeof (struct in_addr));
+	      memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
+	    }
+
+	  pai = &(ai->ai_next);
+	}
+
+      ++*naddrs;
+    }
+  return 0;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -1094,98 +1175,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
   goto free_and_return;
 
 process_list:
-  {
-    /* Set up the canonical name if we need it.  */
-    if ((result = process_canonname (req, orig_name, &res)) != 0)
-      goto free_and_return;
-
-    struct gaih_addrtuple *at2 = res.at;
-    size_t socklen;
-    sa_family_t family;
-
-    /*
-      buffer is the size of an unformatted IPv6 address in printable format.
-     */
-    while (at2 != NULL)
-      {
-	family = at2->family;
-	if (family == AF_INET6)
-	  {
-	    socklen = sizeof (struct sockaddr_in6);
-
-	    /* If we looked up IPv4 mapped address discard them here if
-	       the caller isn't interested in all address and we have
-	       found at least one IPv6 address.  */
-	    if (res.got_ipv6
-		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
-		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
-	      goto ignore;
-	  }
-	else
-	  socklen = sizeof (struct sockaddr_in);
-
-	for (int i = 0; st[i].set; i++)
-	  {
-	    struct addrinfo *ai;
-	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
-	    if (ai == NULL)
-	      {
-		result = -EAI_MEMORY;
-		goto free_and_return;
-	      }
-
-	    ai->ai_flags = req->ai_flags;
-	    ai->ai_family = family;
-	    ai->ai_socktype = st[i].socktype;
-	    ai->ai_protocol = st[i].protocol;
-	    ai->ai_addrlen = socklen;
-	    ai->ai_addr = (void *) (ai + 1);
-
-	    /* We only add the canonical name once.  */
-	    ai->ai_canonname = res.canon;
-	    res.canon = NULL;
-
-#ifdef _HAVE_SA_LEN
-	    ai->ai_addr->sa_len = socklen;
-#endif /* _HAVE_SA_LEN */
-	    ai->ai_addr->sa_family = family;
-
-	    /* In case of an allocation error the list must be NULL
-	       terminated.  */
-	    ai->ai_next = NULL;
-
-	    if (family == AF_INET6)
-	      {
-		struct sockaddr_in6 *sin6p =
-		  (struct sockaddr_in6 *) ai->ai_addr;
-
-		sin6p->sin6_port = st[i].port;
-		sin6p->sin6_flowinfo = 0;
-		memcpy (&sin6p->sin6_addr,
-			at2->addr, sizeof (struct in6_addr));
-		sin6p->sin6_scope_id = at2->scopeid;
-	      }
-	    else
-	      {
-		struct sockaddr_in *sinp =
-		  (struct sockaddr_in *) ai->ai_addr;
-		sinp->sin_port = st[i].port;
-		memcpy (&sinp->sin_addr,
-			at2->addr, sizeof (struct in_addr));
-		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
-	      }
-
-	    pai = &(ai->ai_next);
-	  }
-
-	++*naddrs;
+  /* Set up the canonical name if we need it.  */
+  if ((result = process_canonname (req, orig_name, &res)) != 0)
+    goto free_and_return;
 
-      ignore:
-	at2 = at2->next;
-      }
-  }
+  result = generate_addrinfo (req, &res, st, pai, naddrs);
 
- free_and_return:
+free_and_return:
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 12/12] gethosts: Return EAI_MEMORY on allocation failure
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (10 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
@ 2022-03-08 10:07 ` Siddhesh Poyarekar
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 10:07 UTC (permalink / raw)
  To: libc-alpha; +Cc: carlos, fweimer

All other cases of failures due to lack of memory return EAI_MEMORY, so
it seems wrong to return EAI_SYSTEM here.  The only reason
convert_hostent_to_gaih_addrtuple could fail is on calloc failure.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 6cbbd5dac1..7c7565ac6d 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -303,13 +303,13 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name,
   else if (status == NSS_STATUS_SUCCESS)
     {
       if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
-	return -EAI_SYSTEM;
+	return -EAI_MEMORY;
 
       if (localcanon != NULL && res->canon == NULL)
 	{
 	  char *canonbuf = __strdup (localcanon);
 	  if (canonbuf == NULL)
-	    return  -EAI_SYSTEM;
+	    return  -EAI_MEMORY;
 	  res->canon = canonbuf;
 	}
     }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-08 10:07 ` [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
@ 2022-03-08 11:00   ` Andreas Schwab
  2022-03-08 13:45     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 68+ messages in thread
From: Andreas Schwab @ 2022-03-08 11:00 UTC (permalink / raw)
  To: Siddhesh Poyarekar via Libc-alpha; +Cc: Siddhesh Poyarekar, fweimer

On Mär 08 2022, Siddhesh Poyarekar via Libc-alpha wrote:

> @@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
>    if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
>      return true;
>  
> -  struct gaih_addrtuple *array = calloc (count, sizeof (*array));
> +  struct gaih_addrtuple *array = *result;
> +  size_t old = 0;
> +
> +  while (array)

            array != NULL

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-08 11:00   ` Andreas Schwab
@ 2022-03-08 13:45     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 13:45 UTC (permalink / raw)
  To: Andreas Schwab, Siddhesh Poyarekar via Libc-alpha; +Cc: fweimer

On 08/03/2022 16:30, Andreas Schwab wrote:
> On Mär 08 2022, Siddhesh Poyarekar via Libc-alpha wrote:
> 
>> @@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
>>     if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
>>       return true;
>>   
>> -  struct gaih_addrtuple *array = calloc (count, sizeof (*array));
>> +  struct gaih_addrtuple *array = *result;
>> +  size_t old = 0;
>> +
>> +  while (array)
> 
>              array != NULL
> 

Thanks, fixed locally.

Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-08 10:07 ` [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
@ 2022-03-08 13:52   ` Siddhesh Poyarekar
  2022-03-08 21:12     ` Carlos O'Donell
  0 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-08 13:52 UTC (permalink / raw)
  To: Siddhesh Poyarekar, libc-alpha; +Cc: fweimer

On 08/03/2022 15:37, Siddhesh Poyarekar via Libc-alpha wrote:
> Allocations for address tuples is currently a bit confusing because of
> the pointer chasing through PAT, making it hard to observe the sequence
> in which allocations have been made.  Narrow scope of the pointer
> chasing through PAT so that it is only used where necessary.
> 
> This also tightens actions behaviour with the hosts database in
> getaddrinfo to comply with the manual text.  The "continue" action
> discards previous results and the "merge" action results in an immedate
> lookup failure.  Consequently, chaining of allocations across modules is
> no longer necessary, thus opening up cleanup opportunities.
> 
> A test has been added that checks some combinations to ensure that they
> work correctly.  The "dns [SUCCESS=continue] dns" for example results in
> a segfault without this fix.
> 
> Resolves: BZ #28931
> 
> Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
> ---
>   nss/Makefile                |   1 +
>   nss/tst-nss-gai-actions.c   | 242 ++++++++++++++++++++++++++++++++++++
>   sysdeps/posix/getaddrinfo.c | 142 +++++++++++++--------
>   3 files changed, 333 insertions(+), 52 deletions(-)
>   create mode 100644 nss/tst-nss-gai-actions.c
> 
> diff --git a/nss/Makefile b/nss/Makefile
> index 552e5d03e1..43a0b9defe 100644
> --- a/nss/Makefile
> +++ b/nss/Makefile
> @@ -78,6 +78,7 @@ tests += tst-nss-files-hosts-multi
>   tests += tst-nss-files-hosts-getent
>   tests += tst-nss-files-alias-leak
>   tests += tst-nss-files-alias-truncated
> +tests += tst-nss-gai-actions
>   endif
>   
>   # If we have a thread library then we can test cancellation against
> diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
> new file mode 100644
> index 0000000000..b5ba5f0138
> --- /dev/null
> +++ b/nss/tst-nss-gai-actions.c
> @@ -0,0 +1,242 @@
> +/* Test continue and merge NSS actions for getaddrinfo.

Looks like DNS tests don't work under the patchwork trybot container. 
I'll mark the "dns [SUCCESS=continue] dns" tests as FAIL_UNSUPPORTED 
when it encounters a temporary failure in name resolution.

Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-08 13:52   ` Siddhesh Poyarekar
@ 2022-03-08 21:12     ` Carlos O'Donell
  2022-03-09 17:13       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 68+ messages in thread
From: Carlos O'Donell @ 2022-03-08 21:12 UTC (permalink / raw)
  To: Siddhesh Poyarekar, Siddhesh Poyarekar, libc-alpha, DJ Delorie; +Cc: fweimer


On 3/8/22 08:52, Siddhesh Poyarekar wrote:
> On 08/03/2022 15:37, Siddhesh Poyarekar via Libc-alpha wrote:
>> Allocations for address tuples is currently a bit confusing because of
>> the pointer chasing through PAT, making it hard to observe the sequence
>> in which allocations have been made.  Narrow scope of the pointer
>> chasing through PAT so that it is only used where necessary.
>>
>> This also tightens actions behaviour with the hosts database in
>> getaddrinfo to comply with the manual text.  The "continue" action
>> discards previous results and the "merge" action results in an immedate
>> lookup failure.  Consequently, chaining of allocations across modules is
>> no longer necessary, thus opening up cleanup opportunities.
>>
>> A test has been added that checks some combinations to ensure that they
>> work correctly.  The "dns [SUCCESS=continue] dns" for example results in
>> a segfault without this fix.
>>
>> Resolves: BZ #28931
>>
>> Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
>> ---
>>   nss/Makefile                |   1 +
>>   nss/tst-nss-gai-actions.c   | 242 ++++++++++++++++++++++++++++++++++++
>>   sysdeps/posix/getaddrinfo.c | 142 +++++++++++++--------
>>   3 files changed, 333 insertions(+), 52 deletions(-)
>>   create mode 100644 nss/tst-nss-gai-actions.c
>>
>> diff --git a/nss/Makefile b/nss/Makefile
>> index 552e5d03e1..43a0b9defe 100644
>> --- a/nss/Makefile
>> +++ b/nss/Makefile
>> @@ -78,6 +78,7 @@ tests += tst-nss-files-hosts-multi
>>   tests += tst-nss-files-hosts-getent
>>   tests += tst-nss-files-alias-leak
>>   tests += tst-nss-files-alias-truncated
>> +tests += tst-nss-gai-actions
>>   endif
>>     # If we have a thread library then we can test cancellation against
>> diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
>> new file mode 100644
>> index 0000000000..b5ba5f0138
>> --- /dev/null
>> +++ b/nss/tst-nss-gai-actions.c
>> @@ -0,0 +1,242 @@
>> +/* Test continue and merge NSS actions for getaddrinfo.
> 
> Looks like DNS tests don't work under the patchwork trybot container.
> I'll mark the "dns [SUCCESS=continue] dns" tests as FAIL_UNSUPPORTED
> when it encounters a temporary failure in name resolution.

No new tests should rely on external DNS being available during testing.

The trybots must be able to run disconnected and fully isolated from the network
from an operational security perspective.

You can and should provide either a stub NSS resolver that returns results for DNS
that just feeds that data back to you.

This is an indication of an incomplete test, in that we need to be able to control
all inputs to the test, otherwise this test is unreliable at best and will spurious
fail when DNS goes down.

-- 
Cheers,
Carlos.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-08 21:12     ` Carlos O'Donell
@ 2022-03-09 17:13       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-09 17:13 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, DJ Delorie; +Cc: fweimer

[Sorry, I thought I responded to this, but I clearly didn't]

On 09/03/2022 02:42, Carlos O'Donell wrote:
> No new tests should rely on external DNS being available during testing.
> 
> The trybots must be able to run disconnected and fully isolated from the network
> from an operational security perspective.
> 
> You can and should provide either a stub NSS resolver that returns results for DNS
> that just feeds that data back to you.
> 
> This is an indication of an incomplete test, in that we need to be able to control
> all inputs to the test, otherwise this test is unreliable at best and will spurious
> fail when DNS goes down.

I'll just drop the dns tests.  I had put it in there because "dns 
[SUCCESS=continue] dns" would segfault in nss_dns, but that's only a 
symptom of the problem in gaih_inet.  The remaining nss_files tests in 
tst-nss-gai-actions.c test that core problem, so we have the coverage we 
need.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 00/12] getaddrinfo facelift and fixes
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (11 preceding siblings ...)
  2022-03-08 10:07 ` [PATCH 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
@ 2022-03-14  9:48 ` Siddhesh Poyarekar
  2022-03-14  9:48   ` [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
                     ` (11 more replies)
  2022-03-14 13:21 ` [PATCH 00/12] getaddrinfo facelift and fixes Cristian Rodríguez
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
  14 siblings, 12 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

The gaih_inet implementation, which forms the core of getaddrinfo, is
quite complex in its implementation, making it hard to follow or debug.
Particularly, allocations for gaih_addrtuples to store intermediate
results are particularly hard to track because of the way in which it is
written.

This patchset is an attempt at cleaning up the implementation to make it
much easier to follow.  In the process, it also fixes a couple of bugs,
one that was the trigger for this cleanup and another that was
discovered during the cleanup.

Testing:

The leak reproducer in BZ #28852 could not be converted into a testsuite
test since mtrace isn't able to track the leak, so I separately verified
that it is fixed.  There is a new test for #28931 which tests a variety
of combinations with SUCCESS=merge and SUCCESS=continue.  Further, I
have built and done some smoke testing on Fedora with and without nscd
to ensure that there are no regressions resulting from this patchset.

Finally, I did a scratch build for Fedora and verified that there are no
new regressions on i686, x86_64, s390x, ppc64le and aarch64.

Changes from v1:
- Fixed nit: boolean coercion in convert_hostent_to_gaih_addrtuple
- Dropped DNS tests in tst-nss-gai-actions test since they are
  unnecessary and the files tests are sufficient.

Siddhesh Poyarekar (12):
  Simplify allocations and fix merge and continue actions [BZ #28931]
  gaih_inet: Simplify canon name resolution
  getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  gaih_inet: Simplify service resolution
  gaih_inet: make numeric lookup a separate routine
  gaih_inet: Split simple gethostbyname into its own function
  gaih_inet: Split nscd lookup code into its own function.
  gaih_inet: separate nss lookup loop into its own function
  gaih_inet: make gethosts into a function
  gaih_inet: split loopback lookup into its own function
  gaih_inet: Split result generation into its own function
  gethosts: Return EAI_MEMORY on allocation failure

 nss/Makefile                |    1 +
 nss/tst-nss-gai-actions.c   |  208 +++++
 sysdeps/posix/getaddrinfo.c | 1518 ++++++++++++++++++-----------------
 3 files changed, 1012 insertions(+), 715 deletions(-)
 create mode 100644 nss/tst-nss-gai-actions.c

-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-14 10:30     ` Andreas Schwab
  2022-03-16 20:47     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
                     ` (10 subsequent siblings)
  11 siblings, 2 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Allocations for address tuples is currently a bit confusing because of
the pointer chasing through PAT, making it hard to observe the sequence
in which allocations have been made.  Narrow scope of the pointer
chasing through PAT so that it is only used where necessary.

This also tightens actions behaviour with the hosts database in
getaddrinfo to comply with the manual text.  The "continue" action
discards previous results and the "merge" action results in an immedate
lookup failure.  Consequently, chaining of allocations across modules is
no longer necessary, thus opening up cleanup opportunities.

A test has been added that checks some combinations to ensure that they
work correctly.

Resolves: BZ #28931

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 nss/Makefile                |   1 +
 nss/tst-nss-gai-actions.c   | 208 ++++++++++++++++++++++++++++++++++++
 sysdeps/posix/getaddrinfo.c | 142 +++++++++++++++---------
 3 files changed, 299 insertions(+), 52 deletions(-)
 create mode 100644 nss/tst-nss-gai-actions.c

diff --git a/nss/Makefile b/nss/Makefile
index de439d4911..8ca69ed25b 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -79,6 +79,7 @@ tests += tst-nss-files-hosts-multi
 tests += tst-nss-files-hosts-getent
 tests += tst-nss-files-alias-leak
 tests += tst-nss-files-alias-truncated
+tests += tst-nss-gai-actions
 endif
 
 # If we have a thread library then we can test cancellation against
diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
new file mode 100644
index 0000000000..f7f38f0032
--- /dev/null
+++ b/nss/tst-nss-gai-actions.c
@@ -0,0 +1,208 @@
+/* Test continue and merge NSS actions for getaddrinfo.
+   Copyright The GNU Toolchain Authors.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <gnu/lib-names.h>
+#include <nss.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+enum
+{
+  ACTION_MERGE = 0,
+  ACTION_CONTINUE,
+};
+
+struct test_params
+{
+  int action;
+  int family;
+  bool canon;
+};
+
+struct support_chroot *chroot_env;
+
+static void
+prepare (int argc, char **argv)
+{
+  chroot_env = support_chroot_create
+    ((struct support_chroot_configuration)
+     {
+       .resolv_conf = "",
+       .hosts = "",
+       .host_conf = "multi on\n",
+     });
+}
+
+/* Create the /etc/hosts file from outside the chroot.  */
+static void
+write_hosts (void)
+{
+  const int count = 512;
+
+  FILE *fp = xfopen (chroot_env->path_hosts, "w");
+  fputs ("127.0.0.1   localhost localhost.localdomain\n"
+         "::1         localhost localhost.localdomain\n",
+         fp);
+  for (int i = 1; i < count; ++i)
+    fprintf (fp, "192.0.%d.%d gnu.org\n", (i / 256) & 0xff, i & 0xff);
+  xfclose (fp);
+}
+
+static const char *
+family_str (int family)
+{
+  switch (family)
+    {
+    case AF_UNSPEC:
+      return "AF_UNSPEC";
+    case AF_INET:
+      return "AF_INET";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static const char *
+action_str (int action)
+{
+  switch (action)
+    {
+    case ACTION_MERGE:
+      return "merge";
+    case ACTION_CONTINUE:
+      return "continue";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+/* getaddrinfo test.  To be run from a subprocess.  */
+static void
+test_gai (void *closure)
+{
+  struct test_params *params = closure;
+
+  struct addrinfo hints =
+    {
+      .ai_family = params->family,
+    };
+
+  struct addrinfo *ai;
+
+  if (params->canon)
+    hints.ai_flags = AI_CANONNAME;
+
+  /* Use /etc/hosts in the chroot.  */
+  xchroot (chroot_env->path_chroot);
+
+  printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n",
+	  action_str (params->action), family_str (params->family),
+	  params->canon ? "AI_CANONNAME" : "");
+
+  int ret = getaddrinfo ("gnu.org", "80", &hints, &ai);
+
+  switch (params->action)
+    {
+    case ACTION_MERGE:
+      if (ret == 0)
+	{
+	  char *formatted = support_format_addrinfo (ai, ret);
+
+	  printf ("merge unexpectedly succeeded:\n %s\n", formatted);
+	  support_record_failure ();
+	  free (formatted);
+	}
+      else
+	return;
+    case ACTION_CONTINUE:
+	{
+	  char *formatted = support_format_addrinfo (ai, ret);
+
+	  /* Verify that the result appears exactly once.  */
+	  const char *expected = "address: STREAM/TCP 192.0.0.1 80\n"
+	    "address: DGRAM/UDP 192.0.0.1 80\n"
+	    "address: RAW/IP 192.0.0.1 80\n";
+
+	  const char *contains = strstr (formatted, expected);
+	  const char *contains2 = NULL;
+
+	  if (contains != NULL)
+	    contains2 = strstr (contains + strlen (expected), expected);
+
+	  if (contains == NULL || contains2 != NULL)
+	    {
+	      printf ("continue failed:\n%s\n", formatted);
+	      support_record_failure ();
+	    }
+
+	  free (formatted);
+	  break;
+	}
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static void
+test_in_subprocess (int action)
+{
+  char buf[32];
+
+  snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files",
+	    action_str (action));
+  __nss_configure_lookup ("hosts", buf);
+
+  struct test_params params =
+    {
+      .action = action,
+      .family = AF_UNSPEC,
+      .canon = false,
+    };
+  support_isolate_in_subprocess (test_gai, &params);
+  params.family = AF_INET;
+  support_isolate_in_subprocess (test_gai, &params);
+  params.canon = true;
+  support_isolate_in_subprocess (test_gai, &params);
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  if (!support_can_chroot ())
+    FAIL_UNSUPPORTED ("Cannot chroot\n");
+
+  write_hosts ();
+  test_in_subprocess (ACTION_CONTINUE);
+  test_in_subprocess (ACTION_MERGE);
+
+  support_chroot_free (chroot_env);
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 18dccd5924..454a27eb2f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -458,11 +458,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   if (name != NULL)
     {
-      at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      at->family = AF_UNSPEC;
-      at->scopeid = 0;
-      at->next = NULL;
-
       if (req->ai_flags & AI_IDN)
 	{
 	  char *out;
@@ -473,13 +468,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+      uint32_t addr[4];
+      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
 	{
+	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+	  at->scopeid = 0;
+	  at->next = NULL;
+
 	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    at->family = AF_INET;
+	    {
+	      memcpy (at->addr, addr, sizeof (at->addr));
+	      at->family = AF_INET;
+	    }
 	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
 	    {
-	      at->addr[3] = at->addr[0];
+	      at->addr[3] = addr[0];
 	      at->addr[2] = htonl (0xffff);
 	      at->addr[1] = 0;
 	      at->addr[0] = 0;
@@ -493,49 +496,62 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  if (req->ai_flags & AI_CANONNAME)
 	    canon = name;
+
+	  goto process_list;
 	}
-      else if (at->family == AF_UNSPEC)
+
+      char *scope_delim = strchr (name, SCOPE_DELIMITER);
+      int e;
+
+      if (scope_delim == NULL)
+	e = inet_pton (AF_INET6, name, addr);
+      else
+	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
+
+      if (e > 0)
 	{
-	  char *scope_delim = strchr (name, SCOPE_DELIMITER);
-	  int e;
-	  if (scope_delim == NULL)
-	    e = inet_pton (AF_INET6, name, at->addr);
+	  at = alloca_account (sizeof (struct gaih_addrtuple),
+			       alloca_used);
+	  at->scopeid = 0;
+	  at->next = NULL;
+
+	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+	    {
+	      memcpy (at->addr, addr, sizeof (at->addr));
+	      at->family = AF_INET6;
+	    }
+	  else if (req->ai_family == AF_INET
+		   && IN6_IS_ADDR_V4MAPPED (addr))
+	    {
+	      at->addr[0] = addr[3];
+	      at->addr[1] = addr[1];
+	      at->addr[2] = addr[2];
+	      at->addr[3] = addr[3];
+	      at->family = AF_INET;
+	    }
 	  else
-	    e = __inet_pton_length (AF_INET6, name, scope_delim - name,
-				    at->addr);
-	  if (e > 0)
 	    {
-	      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-		at->family = AF_INET6;
-	      else if (req->ai_family == AF_INET
-		       && IN6_IS_ADDR_V4MAPPED (at->addr))
-		{
-		  at->addr[0] = at->addr[3];
-		  at->family = AF_INET;
-		}
-	      else
-		{
-		  result = -EAI_ADDRFAMILY;
-		  goto free_and_return;
-		}
-
-	      if (scope_delim != NULL
-		  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
-					   scope_delim + 1,
-					   &at->scopeid) != 0)
-		{
-		  result = -EAI_NONAME;
-		  goto free_and_return;
-		}
+	      result = -EAI_ADDRFAMILY;
+	      goto free_and_return;
+	    }
 
-	      if (req->ai_flags & AI_CANONNAME)
-		canon = name;
+	  if (scope_delim != NULL
+	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+				       scope_delim + 1,
+				       &at->scopeid) != 0)
+	    {
+	      result = -EAI_NONAME;
+	      goto free_and_return;
 	    }
+
+	  if (req->ai_flags & AI_CANONNAME)
+	    canon = name;
+
+	  goto process_list;
 	}
 
-      if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
+      if ((req->ai_flags & AI_NUMERICHOST) == 0)
 	{
-	  struct gaih_addrtuple **pat = &at;
 	  int no_data = 0;
 	  int no_inet6_data = 0;
 	  nss_action_list nip;
@@ -543,6 +559,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  enum nss_status status = NSS_STATUS_UNAVAIL;
 	  int no_more;
 	  struct resolv_context *res_ctx = NULL;
+	  bool do_merge = false;
 
 	  /* If we do not have to look for IPv6 addresses or the canonical
 	     name, use the simple, old functions, which do not support
@@ -579,7 +596,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  result = -EAI_MEMORY;
 			  goto free_and_return;
 			}
-		      *pat = addrmem;
+		      at = addrmem;
 		    }
 		  else
 		    {
@@ -632,6 +649,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		    }
 
 		  struct gaih_addrtuple *addrfree = addrmem;
+		  struct gaih_addrtuple **pat = &at;
+
 		  for (int i = 0; i < air->naddrs; ++i)
 		    {
 		      socklen_t size = (air->family[i] == AF_INET
@@ -695,12 +714,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 		  free (air);
 
-		  if (at->family == AF_UNSPEC)
-		    {
-		      result = -EAI_NONAME;
-		      goto free_and_return;
-		    }
-
 		  goto process_list;
 		}
 	      else if (err == 0)
@@ -732,6 +745,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  while (!no_more)
 	    {
+	      /* Always start afresh; continue should discard previous results
+		 and the hosts database does not support merge.  */
+	      at = NULL;
+	      free (canonbuf);
+	      free (addrmem);
+	      canon = canonbuf = NULL;
+	      addrmem = NULL;
+
+	      if (do_merge)
+		{
+		  __set_h_errno (NETDB_INTERNAL);
+		  __set_errno (EBUSY);
+		  break;
+		}
+
 	      no_data = 0;
 	      nss_gethostbyname4_r *fct4 = NULL;
 
@@ -744,12 +772,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		{
 		  while (1)
 		    {
-		      status = DL_CALL_FCT (fct4, (name, pat,
+		      status = DL_CALL_FCT (fct4, (name, &at,
 						   tmpbuf->data, tmpbuf->length,
 						   &errno, &h_errno,
 						   NULL));
 		      if (status == NSS_STATUS_SUCCESS)
 			break;
+		      /* gethostbyname4_r may write into AT, so reset it.  */
+		      at = NULL;
 		      if (status != NSS_STATUS_TRYAGAIN
 			  || errno != ERANGE || h_errno != NETDB_INTERNAL)
 			{
@@ -774,7 +804,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      no_data = 1;
 
 		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
-			canon = (*pat)->name;
+			canon = at->name;
+
+		      struct gaih_addrtuple **pat = &at;
 
 		      while (*pat != NULL)
 			{
@@ -826,6 +858,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 		  if (fct != NULL)
 		    {
+		      struct gaih_addrtuple **pat = &at;
+
 		      if (req->ai_family == AF_INET6
 			  || req->ai_family == AF_UNSPEC)
 			{
@@ -899,6 +933,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
 		break;
 
+	      /* The hosts database does not support MERGE.  */
+	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+		do_merge = true;
+
 	      nip++;
 	      if (nip->module == NULL)
 		no_more = -1;
@@ -930,7 +968,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
 
     process_list:
-      if (at->family == AF_UNSPEC)
+      if (at == NULL)
 	{
 	  result = -EAI_NONAME;
 	  goto free_and_return;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 02/12] gaih_inet: Simplify canon name resolution
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
  2022-03-14  9:48   ` [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-16 21:12     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
                     ` (9 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Simplify logic for allocation of canon to remove the canonbuf variable;
canon now always points to an allocated block.  Also pull the canon name
set into a separate function.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 130 +++++++++++++++++++++---------------
 1 file changed, 75 insertions(+), 55 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 454a27eb2f..df164a3e96 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -285,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 									      \
       if (localcanon != NULL && canon == NULL)				      \
 	{								      \
-	  canonbuf = __strdup (localcanon);				      \
+	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
 	    {								      \
 	      __resolv_context_put (res_ctx);				      \
@@ -323,6 +323,41 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
   return __strdup (name);
 }
 
+/* Process looked up canonical name and if necessary, decode to IDNA.  Result
+   is a new string written to CANONP and the earlier string is freed.  */
+
+static int
+process_canonname (const struct addrinfo *req, const char *orig_name,
+		   char **canonp)
+{
+  char *canon = *canonp;
+
+  if ((req->ai_flags & AI_CANONNAME) != 0)
+    {
+      bool do_idn = req->ai_flags & AI_CANONIDN;
+      if (do_idn)
+	{
+	  char *out;
+	  int rc = __idna_from_dns_encoding (canon ?: orig_name, &out);
+	  if (rc == 0)
+	    {
+	      free (canon);
+	      canon = out;
+	    }
+	  else if (rc == EAI_IDN_ENCODE)
+	    /* Use the punycode name as a fallback.  */
+	    do_idn = false;
+	  else
+	    return -rc;
+	}
+      if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL)
+	return -EAI_MEMORY;
+    }
+
+  *canonp = canon;
+  return 0;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -332,7 +367,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
   struct gaih_addrtuple *at = NULL;
   bool got_ipv6 = false;
-  const char *canon = NULL;
+  char *canon = NULL;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -453,7 +488,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   bool malloc_name = false;
   struct gaih_addrtuple *addrmem = NULL;
-  char *canonbuf = NULL;
   int result = 0;
 
   if (name != NULL)
@@ -495,7 +529,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    }
 
 	  if (req->ai_flags & AI_CANONNAME)
-	    canon = name;
+	    {
+	      char *canonbuf = __strdup (name);
+	      if (canonbuf == NULL)
+		{
+		  result = -EAI_MEMORY;
+		  goto free_and_return;
+		}
+	      canon = canonbuf;
+	    }
 
 	  goto process_list;
 	}
@@ -545,7 +587,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    }
 
 	  if (req->ai_flags & AI_CANONNAME)
-	    canon = name;
+	    {
+	      char *canonbuf = __strdup (name);
+	      if (canonbuf == NULL)
+		{
+		  result = -EAI_MEMORY;
+		  goto free_and_return;
+		}
+	      canon = canonbuf;
+	    }
 
 	  goto process_list;
 	}
@@ -676,9 +726,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      (*pat)->next = NULL;
 		      if (added_canon || air->canon == NULL)
 			(*pat)->name = NULL;
-		      else if (canonbuf == NULL)
+		      else if (canon == NULL)
 			{
-			  canonbuf = __strdup (air->canon);
+			  char *canonbuf = __strdup (air->canon);
 			  if (canonbuf == NULL)
 			    {
 			      result = -EAI_MEMORY;
@@ -748,9 +798,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      /* Always start afresh; continue should discard previous results
 		 and the hosts database does not support merge.  */
 	      at = NULL;
-	      free (canonbuf);
+	      free (canon);
 	      free (addrmem);
-	      canon = canonbuf = NULL;
+	      canon = NULL;
 	      addrmem = NULL;
 
 	      if (do_merge)
@@ -804,7 +854,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      no_data = 1;
 
 		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
-			canon = at->name;
+			{
+			  char *canonbuf = __strdup (at->name);
+			  if (canonbuf == NULL)
+			    {
+			      __resolv_context_put (res_ctx);
+			      result = -EAI_MEMORY;
+			      goto free_and_return;
+			    }
+			  canon = canonbuf;
+			}
 
 		      struct gaih_addrtuple **pat = &at;
 
@@ -892,7 +951,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  if ((req->ai_flags & AI_CANONNAME) != 0
 			      && canon == NULL)
 			    {
-			      canonbuf = getcanonname (nip, at, name);
+			      char *canonbuf = getcanonname (nip, at, name);
 			      if (canonbuf == NULL)
 				{
 				  __resolv_context_put (res_ctx);
@@ -1003,6 +1062,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
     }
 
   {
+    /* Set up the canonical name if we need it.  */
+    if ((result = process_canonname (req, orig_name, &canon)) != 0)
+      goto free_and_return;
+
     struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
     size_t socklen;
@@ -1013,48 +1076,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
      */
     while (at2 != NULL)
       {
-	/* Only the first entry gets the canonical name.  */
-	if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
-	  {
-	    if (canon == NULL)
-	      /* If the canonical name cannot be determined, use
-		 the passed in string.  */
-	      canon = orig_name;
-
-	    bool do_idn = req->ai_flags & AI_CANONIDN;
-	    if (do_idn)
-	      {
-		char *out;
-		int rc = __idna_from_dns_encoding (canon, &out);
-		if (rc == 0)
-		  canon = out;
-		else if (rc == EAI_IDN_ENCODE)
-		  /* Use the punycode name as a fallback.  */
-		  do_idn = false;
-		else
-		  {
-		    result = -rc;
-		    goto free_and_return;
-		  }
-	      }
-	    if (!do_idn)
-	      {
-		if (canonbuf != NULL)
-		  /* We already allocated the string using malloc, but
-		     the buffer is now owned by canon.  */
-		  canonbuf = NULL;
-		else
-		  {
-		    canon = __strdup (canon);
-		    if (canon == NULL)
-		      {
-			result = -EAI_MEMORY;
-			goto free_and_return;
-		      }
-		  }
-	      }
-	  }
-
 	family = at2->family;
 	if (family == AF_INET6)
 	  {
@@ -1077,7 +1098,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
 	    if (ai == NULL)
 	      {
-		free ((char *) canon);
 		result = -EAI_MEMORY;
 		goto free_and_return;
 	      }
@@ -1137,7 +1157,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-  free (canonbuf);
+  free (canon);
 
   return result;
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
  2022-03-14  9:48   ` [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
  2022-03-14  9:48   ` [PATCH v2 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-16 23:42     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
                     ` (8 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Use realloc in convert_hostent_to_gaih_addrtuple and fix up pointers in
the result list so that a single block is maintained for
hostbyname3_r/hostbyname2_r and freed in gaih_inet.  This result is
never merged with any other results, since the hosts database does not
permit merging.

Resolves BZ #28852.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index df164a3e96..b0a64ead0e 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -199,9 +199,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 				   struct hostent *h,
 				   struct gaih_addrtuple **result)
 {
-  while (*result)
-    result = &(*result)->next;
-
   /* Count the number of addresses in h->h_addr_list.  */
   size_t count = 0;
   for (char **p = h->h_addr_list; *p != NULL; ++p)
@@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
     return true;
 
-  struct gaih_addrtuple *array = calloc (count, sizeof (*array));
+  struct gaih_addrtuple *array = *result;
+  size_t old = 0;
+
+  while (array != NULL)
+    {
+      old++;
+      array = array->next;
+    }
+
+  array = realloc (*result, (old + count) * sizeof (*array));
+
   if (array == NULL)
     return false;
 
+  *result = array;
+
+  /* Update the next pointers on reallocation.  */
+  for (size_t i = 0; i < old; i++)
+    array[i].next = array + i + 1;
+
+  array += old;
+
+  memset (array, 0, count * sizeof (*array));
+
   for (size_t i = 0; i < count; ++i)
     {
       if (family == AF_INET && req->ai_family == AF_INET6)
@@ -235,7 +252,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   array[0].name = h->h_name;
   array[count - 1].next = NULL;
 
-  *result = array;
   return true;
 }
 
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 04/12] gaih_inet: Simplify service resolution
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (2 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  0:48     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
                     ` (7 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Refactor the code to split out the service resolution code into a
separate function.  Allocate the service tuples array just once to the
size of the typeproto array, thus avoiding the unnecessary pointer
chasing and stack allocations.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 178 ++++++++++++++++--------------------
 1 file changed, 78 insertions(+), 100 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index b0a64ead0e..936fa64d17 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -100,14 +100,12 @@ struct gaih_service
 
 struct gaih_servtuple
   {
-    struct gaih_servtuple *next;
     int socktype;
     int protocol;
     int port;
+    bool set;
   };
 
-static const struct gaih_servtuple nullserv;
-
 
 struct gaih_typeproto
   {
@@ -180,11 +178,11 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
     }
   while (r);
 
-  st->next = NULL;
   st->socktype = tp->socktype;
   st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
 		  ? req->ai_protocol : tp->protocol);
   st->port = s->s_port;
+  st->set = true;
 
   return 0;
 }
@@ -375,20 +373,11 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
 }
 
 static int
-gaih_inet (const char *name, const struct gaih_service *service,
-	   const struct addrinfo *req, struct addrinfo **pai,
-	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
+get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
+		struct gaih_servtuple *st, struct scratch_buffer *tmpbuf)
 {
+  int i;
   const struct gaih_typeproto *tp = gaih_inet_typeproto;
-  struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
-  struct gaih_addrtuple *at = NULL;
-  bool got_ipv6 = false;
-  char *canon = NULL;
-  const char *orig_name = name;
-
-  /* Reserve stack memory for the scratch buffer in the getaddrinfo
-     function.  */
-  size_t alloca_used = sizeof (struct scratch_buffer);
 
   if (req->ai_protocol || req->ai_socktype)
     {
@@ -410,98 +399,88 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
     }
 
-  int port = 0;
-  if (service != NULL)
+  if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+    return -EAI_SERVICE;
+
+  if (service == NULL || service->num >= 0)
     {
-      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-	return -EAI_SERVICE;
+      int port = service != NULL ? htons (service->num) : 0;
 
-      if (service->num < 0)
+      if (req->ai_socktype || req->ai_protocol)
 	{
-	  if (tp->name[0])
-	    {
-	      st = (struct gaih_servtuple *)
-		alloca_account (sizeof (struct gaih_servtuple), alloca_used);
-
-	      int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf);
-	      if (__glibc_unlikely (rc != 0))
-		return rc;
-	    }
-	  else
-	    {
-	      struct gaih_servtuple **pst = &st;
-	      for (tp++; tp->name[0]; tp++)
-		{
-		  struct gaih_servtuple *newp;
+	  st[0].socktype = tp->socktype;
+	  st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+			  ? req->ai_protocol : tp->protocol);
+	  st[0].port = port;
+	  st[0].set = true;
 
-		  if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-		    continue;
+	  return 0;
+	}
 
-		  if (req->ai_socktype != 0
-		      && req->ai_socktype != tp->socktype)
-		    continue;
-		  if (req->ai_protocol != 0
-		      && !(tp->protoflag & GAI_PROTO_PROTOANY)
-		      && req->ai_protocol != tp->protocol)
-		    continue;
+      /* Neither socket type nor protocol is set.  Return all socket types
+	 we know about.  */
+      for (i = 0, ++tp; tp->name[0]; ++tp)
+	if (tp->defaultflag)
+	  {
+	    st[i].socktype = tp->socktype;
+	    st[i].protocol = tp->protocol;
+	    st[i].port = port;
+	    st[i++].set = true;
+	  }
 
-		  newp = (struct gaih_servtuple *)
-		    alloca_account (sizeof (struct gaih_servtuple),
-				    alloca_used);
+      return 0;
+    }
 
-		  if (gaih_inet_serv (service->name,
-				      tp, req, newp, tmpbuf) != 0)
-		    continue;
+  if (tp->name[0])
+    return gaih_inet_serv (service->name, tp, req, st, tmpbuf);
 
-		  *pst = newp;
-		  pst = &(newp->next);
-		}
-	      if (st == (struct gaih_servtuple *) &nullserv)
-		return -EAI_SERVICE;
-	    }
-	}
-      else
-	{
-	  port = htons (service->num);
-	  goto got_port;
-	}
-    }
-  else
+  for (i = 0, tp++; tp->name[0]; tp++)
     {
-    got_port:
+      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+	continue;
 
-      if (req->ai_socktype || req->ai_protocol)
-	{
-	  st = alloca_account (sizeof (struct gaih_servtuple), alloca_used);
-	  st->next = NULL;
-	  st->socktype = tp->socktype;
-	  st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
-			  ? req->ai_protocol : tp->protocol);
-	  st->port = port;
-	}
-      else
-	{
-	  /* Neither socket type nor protocol is set.  Return all socket types
-	     we know about.  */
-	  struct gaih_servtuple **lastp = &st;
-	  for (++tp; tp->name[0]; ++tp)
-	    if (tp->defaultflag)
-	      {
-		struct gaih_servtuple *newp;
+      if (req->ai_socktype != 0
+	  && req->ai_socktype != tp->socktype)
+	continue;
+      if (req->ai_protocol != 0
+	  && !(tp->protoflag & GAI_PROTO_PROTOANY)
+	  && req->ai_protocol != tp->protocol)
+	continue;
 
-		newp = alloca_account (sizeof (struct gaih_servtuple),
-				       alloca_used);
-		newp->next = NULL;
-		newp->socktype = tp->socktype;
-		newp->protocol = tp->protocol;
-		newp->port = port;
+      if (gaih_inet_serv (service->name,
+			  tp, req, &st[i], tmpbuf) != 0)
+	continue;
 
-		*lastp = newp;
-		lastp = &newp->next;
-	      }
-	}
+      i++;
     }
 
+  if (!st[0].set)
+    return -EAI_SERVICE;
+
+  return 0;
+}
+
+static int
+gaih_inet (const char *name, const struct gaih_service *service,
+	   const struct addrinfo *req, struct addrinfo **pai,
+	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
+{
+  struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
+			   / sizeof (struct gaih_typeproto)] = {0};
+
+  struct gaih_addrtuple *at = NULL;
+  bool got_ipv6 = false;
+  char *canon = NULL;
+  const char *orig_name = name;
+
+  /* Reserve stack memory for the scratch buffer in the getaddrinfo
+     function.  */
+  size_t alloca_used = sizeof (struct scratch_buffer);
+
+  int rc;
+  if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
+    return rc;
+
   bool malloc_name = false;
   struct gaih_addrtuple *addrmem = NULL;
   int result = 0;
@@ -1082,7 +1061,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
     if ((result = process_canonname (req, orig_name, &canon)) != 0)
       goto free_and_return;
 
-    struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
     size_t socklen;
     sa_family_t family;
@@ -1108,7 +1086,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	else
 	  socklen = sizeof (struct sockaddr_in);
 
-	for (st2 = st; st2 != NULL; st2 = st2->next)
+	for (int i = 0; st[i].set; i++)
 	  {
 	    struct addrinfo *ai;
 	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
@@ -1120,8 +1098,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	    ai->ai_flags = req->ai_flags;
 	    ai->ai_family = family;
-	    ai->ai_socktype = st2->socktype;
-	    ai->ai_protocol = st2->protocol;
+	    ai->ai_socktype = st[i].socktype;
+	    ai->ai_protocol = st[i].protocol;
 	    ai->ai_addrlen = socklen;
 	    ai->ai_addr = (void *) (ai + 1);
 
@@ -1143,7 +1121,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		struct sockaddr_in6 *sin6p =
 		  (struct sockaddr_in6 *) ai->ai_addr;
 
-		sin6p->sin6_port = st2->port;
+		sin6p->sin6_port = st[i].port;
 		sin6p->sin6_flowinfo = 0;
 		memcpy (&sin6p->sin6_addr,
 			at2->addr, sizeof (struct in6_addr));
@@ -1153,7 +1131,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      {
 		struct sockaddr_in *sinp =
 		  (struct sockaddr_in *) ai->ai_addr;
-		sinp->sin_port = st2->port;
+		sinp->sin_port = st[i].port;
 		memcpy (&sinp->sin_addr,
 			at2->addr, sizeof (struct in_addr));
 		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 05/12] gaih_inet: make numeric lookup a separate routine
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (3 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  4:10     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
                     ` (6 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Introduce the gaih_result structure and general paradigm for cleanups
that follow to process the lookup request and return a result.  A lookup
function (like text_to_binary_address), should return an integer error
code and set members of gaih_result based on what it finds.  If the
function does not have a result and no errors have occurred during the
lookup, it should return 0 and res.at should be set to NULL, allowing a
subsequent function to do the lookup until we run out of options.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 889 ++++++++++++++++++------------------
 1 file changed, 451 insertions(+), 438 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 936fa64d17..d7b6eae9fc 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -116,6 +116,12 @@ struct gaih_typeproto
     char name[8];
   };
 
+struct gaih_result
+{
+  struct gaih_addrtuple *at;
+  char *canon;
+};
+
 /* Values for `protoflag'.  */
 #define GAI_PROTO_NOSERVICE	1
 #define GAI_PROTO_PROTOANY	2
@@ -297,7 +303,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	}								      \
       *pat = addrmem;							      \
 									      \
-      if (localcanon != NULL && canon == NULL)				      \
+      if (localcanon != NULL && res.canon == NULL)			      \
 	{								      \
 	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
@@ -306,7 +312,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	      result = -EAI_SYSTEM;					      \
 	      goto free_and_return;					      \
 	    }								      \
-	  canon = canonbuf;						      \
+	  res.canon = canonbuf;						      \
 	}								      \
       if (_family == AF_INET6 && *pat != NULL)				      \
 	got_ipv6 = true;						      \
@@ -342,9 +348,9 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
 
 static int
 process_canonname (const struct addrinfo *req, const char *orig_name,
-		   char **canonp)
+		   struct gaih_result *res)
 {
-  char *canon = *canonp;
+  char *canon = res->canon;
 
   if ((req->ai_flags & AI_CANONNAME) != 0)
     {
@@ -368,7 +374,7 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
 	return -EAI_MEMORY;
     }
 
-  *canonp = canon;
+  res->canon = canon;
   return 0;
 }
 
@@ -460,6 +466,105 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
   return 0;
 }
 
+/* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
+   NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
+   the function cannot determine a result, RES->AT is set to NULL and 0
+   returned.  */
+
+static int
+text_to_binary_address (const char *name, const struct addrinfo *req,
+			struct gaih_result *res)
+{
+  struct gaih_addrtuple *at = res->at;
+  int result = 0;
+
+  assert (at != NULL);
+
+  memset (at->addr, 0, sizeof (at->addr));
+  if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+    {
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
+	at->family = AF_INET;
+      else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
+	{
+	  at->addr[3] = at->addr[0];
+	  at->addr[2] = htonl (0xffff);
+	  at->addr[1] = 0;
+	  at->addr[0] = 0;
+	  at->family = AF_INET6;
+	}
+      else
+	{
+	  result = -EAI_ADDRFAMILY;
+	  goto out;
+	}
+
+      if (req->ai_flags & AI_CANONNAME)
+	{
+	  char *canonbuf = __strdup (name);
+	  if (canonbuf == NULL)
+	    {
+	      result = -EAI_MEMORY;
+	      goto out;
+	    }
+	  res->canon = canonbuf;
+	}
+      return 0;
+    }
+
+  char *scope_delim = strchr (name, SCOPE_DELIMITER);
+  int e;
+
+  if (scope_delim == NULL)
+    e = inet_pton (AF_INET6, name, at->addr);
+  else
+    e = __inet_pton_length (AF_INET6, name, scope_delim - name, at->addr);
+
+  if (e > 0)
+    {
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+	at->family = AF_INET6;
+      else if (req->ai_family == AF_INET
+	       && IN6_IS_ADDR_V4MAPPED (at->addr))
+	{
+	  at->addr[0] = at->addr[3];
+	  at->family = AF_INET;
+	}
+      else
+	{
+	  result = -EAI_ADDRFAMILY;
+	  goto out;
+	}
+
+      if (scope_delim != NULL
+	  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+				   scope_delim + 1, &at->scopeid) != 0)
+	{
+	  result = -EAI_NONAME;
+	  goto out;
+	}
+
+      if (req->ai_flags & AI_CANONNAME)
+	{
+	  char *canonbuf = __strdup (name);
+	  if (canonbuf == NULL)
+	    {
+	      result = -EAI_MEMORY;
+	      goto out;
+	    }
+	  res->canon = canonbuf;
+	}
+      return 0;
+    }
+
+  if ((req->ai_flags & AI_NUMERICHOST))
+    result = -EAI_NONAME;
+
+out:
+  res->at = NULL;
+  return result;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -468,9 +573,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
 			   / sizeof (struct gaih_typeproto)] = {0};
 
-  struct gaih_addrtuple *at = NULL;
   bool got_ipv6 = false;
-  char *canon = NULL;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -485,6 +588,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_addrtuple *addrmem = NULL;
   int result = 0;
 
+  struct gaih_result res = {0};
   if (name != NULL)
     {
       if (req->ai_flags & AI_IDN)
@@ -497,532 +601,440 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      uint32_t addr[4];
-      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
+      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+      res.at->scopeid = 0;
+      res.at->next = NULL;
+
+      if ((result = text_to_binary_address (name, req, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+
+      int no_data = 0;
+      int no_inet6_data = 0;
+      nss_action_list nip;
+      enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+      enum nss_status status = NSS_STATUS_UNAVAIL;
+      int no_more;
+      struct resolv_context *res_ctx = NULL;
+      bool do_merge = false;
+
+      /* If we do not have to look for IPv6 addresses or the canonical
+	 name, use the simple, old functions, which do not support
+	 IPv6 scope ids, nor retrieving the canonical name.  */
+      if (req->ai_family == AF_INET
+	  && (req->ai_flags & AI_CANONNAME) == 0)
 	{
-	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-	  at->scopeid = 0;
-	  at->next = NULL;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    {
-	      memcpy (at->addr, addr, sizeof (at->addr));
-	      at->family = AF_INET;
-	    }
-	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
-	    {
-	      at->addr[3] = addr[0];
-	      at->addr[2] = htonl (0xffff);
-	      at->addr[1] = 0;
-	      at->addr[0] = 0;
-	      at->family = AF_INET6;
-	    }
-	  else
-	    {
-	      result = -EAI_ADDRFAMILY;
-	      goto free_and_return;
-	    }
+	  int rc;
+	  struct hostent th;
+	  struct hostent *h;
 
-	  if (req->ai_flags & AI_CANONNAME)
+	  while (1)
 	    {
-	      char *canonbuf = __strdup (name);
-	      if (canonbuf == NULL)
+	      rc = __gethostbyname2_r (name, AF_INET, &th,
+				       tmpbuf->data, tmpbuf->length,
+				       &h, &h_errno);
+	      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
+		break;
+	      if (!scratch_buffer_grow (tmpbuf))
 		{
 		  result = -EAI_MEMORY;
 		  goto free_and_return;
 		}
-	      canon = canonbuf;
 	    }
 
-	  goto process_list;
-	}
-
-      char *scope_delim = strchr (name, SCOPE_DELIMITER);
-      int e;
-
-      if (scope_delim == NULL)
-	e = inet_pton (AF_INET6, name, addr);
-      else
-	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
-
-      if (e > 0)
-	{
-	  at = alloca_account (sizeof (struct gaih_addrtuple),
-			       alloca_used);
-	  at->scopeid = 0;
-	  at->next = NULL;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	    {
-	      memcpy (at->addr, addr, sizeof (at->addr));
-	      at->family = AF_INET6;
-	    }
-	  else if (req->ai_family == AF_INET
-		   && IN6_IS_ADDR_V4MAPPED (addr))
+	  if (rc == 0)
 	    {
-	      at->addr[0] = addr[3];
-	      at->addr[1] = addr[1];
-	      at->addr[2] = addr[2];
-	      at->addr[3] = addr[3];
-	      at->family = AF_INET;
+	      if (h != NULL)
+		{
+		  /* We found data, convert it.  */
+		  if (!convert_hostent_to_gaih_addrtuple
+		      (req, AF_INET, h, &addrmem))
+		    {
+		      result = -EAI_MEMORY;
+		      goto free_and_return;
+		    }
+		  res.at = addrmem;
+		}
+	      else
+		{
+		  if (h_errno == NO_DATA)
+		    result = -EAI_NODATA;
+		  else
+		    result = -EAI_NONAME;
+		  goto free_and_return;
+		}
 	    }
 	  else
 	    {
-	      result = -EAI_ADDRFAMILY;
-	      goto free_and_return;
-	    }
+	      if (h_errno == NETDB_INTERNAL)
+		result = -EAI_SYSTEM;
+	      else if (h_errno == TRY_AGAIN)
+		result = -EAI_AGAIN;
+	      else
+		/* We made requests but they turned out no data.
+		   The name is known, though.  */
+		result = -EAI_NODATA;
 
-	  if (scope_delim != NULL
-	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
-				       scope_delim + 1,
-				       &at->scopeid) != 0)
-	    {
-	      result = -EAI_NONAME;
 	      goto free_and_return;
 	    }
 
-	  if (req->ai_flags & AI_CANONNAME)
+	  goto process_list;
+	}
+
+#ifdef USE_NSCD
+      if (__nss_not_use_nscd_hosts > 0
+	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+	__nss_not_use_nscd_hosts = 0;
+
+      if (!__nss_not_use_nscd_hosts
+	  && !__nss_database_custom[NSS_DBSIDX_hosts])
+	{
+	  /* Try to use nscd.  */
+	  struct nscd_ai_result *air = NULL;
+	  int err = __nscd_getai (name, &air, &h_errno);
+	  if (air != NULL)
 	    {
-	      char *canonbuf = __strdup (name);
-	      if (canonbuf == NULL)
+	      /* Transform into gaih_addrtuple list.  */
+	      bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
+	      char *addrs = air->addrs;
+
+	      addrmem = calloc (air->naddrs, sizeof (*addrmem));
+	      if (addrmem == NULL)
 		{
 		  result = -EAI_MEMORY;
 		  goto free_and_return;
 		}
-	      canon = canonbuf;
-	    }
 
-	  goto process_list;
-	}
-
-      if ((req->ai_flags & AI_NUMERICHOST) == 0)
-	{
-	  int no_data = 0;
-	  int no_inet6_data = 0;
-	  nss_action_list nip;
-	  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
-	  enum nss_status status = NSS_STATUS_UNAVAIL;
-	  int no_more;
-	  struct resolv_context *res_ctx = NULL;
-	  bool do_merge = false;
-
-	  /* If we do not have to look for IPv6 addresses or the canonical
-	     name, use the simple, old functions, which do not support
-	     IPv6 scope ids, nor retrieving the canonical name.  */
-	  if (req->ai_family == AF_INET
-	      && (req->ai_flags & AI_CANONNAME) == 0)
-	    {
-	      int rc;
-	      struct hostent th;
-	      struct hostent *h;
+	      struct gaih_addrtuple *addrfree = addrmem;
+	      struct gaih_addrtuple **pat = &res.at;
 
-	      while (1)
+	      for (int i = 0; i < air->naddrs; ++i)
 		{
-		  rc = __gethostbyname2_r (name, AF_INET, &th,
-					   tmpbuf->data, tmpbuf->length,
-					   &h, &h_errno);
-		  if (rc != ERANGE || h_errno != NETDB_INTERNAL)
-		    break;
-		  if (!scratch_buffer_grow (tmpbuf))
+		  socklen_t size = (air->family[i] == AF_INET
+				    ? INADDRSZ : IN6ADDRSZ);
+
+		  if (!((air->family[i] == AF_INET
+			 && req->ai_family == AF_INET6
+			 && (req->ai_flags & AI_V4MAPPED) != 0)
+			|| req->ai_family == AF_UNSPEC
+			|| air->family[i] == req->ai_family))
 		    {
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
+		      /* Skip over non-matching result.  */
+		      addrs += size;
+		      continue;
 		    }
-		}
 
-	      if (rc == 0)
-		{
-		  if (h != NULL)
+		  if (*pat == NULL)
+		    {
+		      *pat = addrfree++;
+		      (*pat)->scopeid = 0;
+		    }
+		  uint32_t *pataddr = (*pat)->addr;
+		  (*pat)->next = NULL;
+		  if (added_canon || air->canon == NULL)
+		    (*pat)->name = NULL;
+		  else if (res.canon == NULL)
 		    {
-		      /* We found data, convert it.  */
-		      if (!convert_hostent_to_gaih_addrtuple
-			  (req, AF_INET, h, &addrmem))
+		      char *canonbuf = __strdup (air->canon);
+		      if (canonbuf == NULL)
 			{
 			  result = -EAI_MEMORY;
 			  goto free_and_return;
 			}
-		      at = addrmem;
+		      res.canon = (*pat)->name = canonbuf;
 		    }
-		  else
+
+		  if (air->family[i] == AF_INET
+		      && req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED))
 		    {
-		      if (h_errno == NO_DATA)
-			result = -EAI_NODATA;
-		      else
-			result = -EAI_NONAME;
-		      goto free_and_return;
+		      (*pat)->family = AF_INET6;
+		      pataddr[3] = *(uint32_t *) addrs;
+		      pataddr[2] = htonl (0xffff);
+		      pataddr[1] = 0;
+		      pataddr[0] = 0;
+		      pat = &((*pat)->next);
+		      added_canon = true;
+		    }
+		  else if (req->ai_family == AF_UNSPEC
+			   || air->family[i] == req->ai_family)
+		    {
+		      (*pat)->family = air->family[i];
+		      memcpy (pataddr, addrs, size);
+		      pat = &((*pat)->next);
+		      added_canon = true;
+		      if (air->family[i] == AF_INET6)
+			got_ipv6 = true;
 		    }
+		  addrs += size;
 		}
-	      else
-		{
-		  if (h_errno == NETDB_INTERNAL)
-		    result = -EAI_SYSTEM;
-		  else if (h_errno == TRY_AGAIN)
-		    result = -EAI_AGAIN;
-		  else
-		    /* We made requests but they turned out no data.
-		       The name is known, though.  */
-		    result = -EAI_NODATA;
 
-		  goto free_and_return;
-		}
+	      free (air);
 
 	      goto process_list;
 	    }
+	  else if (err == 0)
+	    /* The database contains a negative entry.  */
+	    goto free_and_return;
+	  else if (__nss_not_use_nscd_hosts == 0)
+	    {
+	      if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+		result = -EAI_MEMORY;
+	      else if (h_errno == TRY_AGAIN)
+		result = -EAI_AGAIN;
+	      else
+		result = -EAI_SYSTEM;
 
-#ifdef USE_NSCD
-	  if (__nss_not_use_nscd_hosts > 0
-	      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-	    __nss_not_use_nscd_hosts = 0;
+	      goto free_and_return;
+	    }
+	}
+#endif
+
+      no_more = !__nss_database_get (nss_database_hosts, &nip);
 
-	  if (!__nss_not_use_nscd_hosts
-	      && !__nss_database_custom[NSS_DBSIDX_hosts])
+      /* If we are looking for both IPv4 and IPv6 address we don't
+	 want the lookup functions to automatically promote IPv4
+	 addresses to IPv6 addresses, so we use the no_inet6
+	 function variant.  */
+      res_ctx = __resolv_context_get ();
+      if (res_ctx == NULL)
+	no_more = 1;
+
+      while (!no_more)
+	{
+	  /* Always start afresh; continue should discard previous results
+	     and the hosts database does not support merge.  */
+	  res.at = NULL;
+	  free (res.canon);
+	  free (addrmem);
+	  res.canon = NULL;
+	  addrmem = NULL;
+
+	  if (do_merge)
 	    {
-	      /* Try to use nscd.  */
-	      struct nscd_ai_result *air = NULL;
-	      int err = __nscd_getai (name, &air, &h_errno);
-	      if (air != NULL)
+	      __set_h_errno (NETDB_INTERNAL);
+	      __set_errno (EBUSY);
+	      break;
+	    }
+
+	  no_data = 0;
+	  nss_gethostbyname4_r *fct4 = NULL;
+
+	  /* gethostbyname4_r sends out parallel A and AAAA queries and
+	     is thus only suitable for PF_UNSPEC.  */
+	  if (req->ai_family == PF_UNSPEC)
+	    fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+
+	  if (fct4 != NULL)
+	    {
+	      while (1)
 		{
-		  /* Transform into gaih_addrtuple list.  */
-		  bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
-		  char *addrs = air->addrs;
+		  status = DL_CALL_FCT (fct4, (name, &res.at,
+					       tmpbuf->data, tmpbuf->length,
+					       &errno, &h_errno,
+					       NULL));
+		  if (status == NSS_STATUS_SUCCESS)
+		    break;
+		  /* gethostbyname4_r may write into AT, so reset it.  */
+		  res.at = NULL;
+		  if (status != NSS_STATUS_TRYAGAIN
+		      || errno != ERANGE || h_errno != NETDB_INTERNAL)
+		    {
+		      if (h_errno == TRY_AGAIN)
+			no_data = EAI_AGAIN;
+		      else
+			no_data = h_errno == NO_DATA;
+		      break;
+		    }
 
-		  addrmem = calloc (air->naddrs, sizeof (*addrmem));
-		  if (addrmem == NULL)
+		  if (!scratch_buffer_grow (tmpbuf))
 		    {
+		      __resolv_context_put (res_ctx);
 		      result = -EAI_MEMORY;
 		      goto free_and_return;
 		    }
+		}
 
-		  struct gaih_addrtuple *addrfree = addrmem;
-		  struct gaih_addrtuple **pat = &at;
+	      if (status == NSS_STATUS_SUCCESS)
+		{
+		  assert (!no_data);
+		  no_data = 1;
 
-		  for (int i = 0; i < air->naddrs; ++i)
+		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
 		    {
-		      socklen_t size = (air->family[i] == AF_INET
-					? INADDRSZ : IN6ADDRSZ);
-
-		      if (!((air->family[i] == AF_INET
-			     && req->ai_family == AF_INET6
-			     && (req->ai_flags & AI_V4MAPPED) != 0)
-			    || req->ai_family == AF_UNSPEC
-			    || air->family[i] == req->ai_family))
+		      char *canonbuf = __strdup (res.at->name);
+		      if (canonbuf == NULL)
 			{
-			  /* Skip over non-matching result.  */
-			  addrs += size;
-			  continue;
+			  __resolv_context_put (res_ctx);
+			  result = -EAI_MEMORY;
+			  goto free_and_return;
 			}
+		      res.canon = canonbuf;
+		    }
 
-		      if (*pat == NULL)
-			{
-			  *pat = addrfree++;
-			  (*pat)->scopeid = 0;
-			}
-		      uint32_t *pataddr = (*pat)->addr;
-		      (*pat)->next = NULL;
-		      if (added_canon || air->canon == NULL)
-			(*pat)->name = NULL;
-		      else if (canon == NULL)
-			{
-			  char *canonbuf = __strdup (air->canon);
-			  if (canonbuf == NULL)
-			    {
-			      result = -EAI_MEMORY;
-			      goto free_and_return;
-			    }
-			  canon = (*pat)->name = canonbuf;
-			}
+		  struct gaih_addrtuple **pat = &res.at;
 
-		      if (air->family[i] == AF_INET
+		  while (*pat != NULL)
+		    {
+		      if ((*pat)->family == AF_INET
 			  && req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED))
+			  && (req->ai_flags & AI_V4MAPPED) != 0)
 			{
+			  uint32_t *pataddr = (*pat)->addr;
 			  (*pat)->family = AF_INET6;
-			  pataddr[3] = *(uint32_t *) addrs;
+			  pataddr[3] = pataddr[0];
 			  pataddr[2] = htonl (0xffff);
 			  pataddr[1] = 0;
 			  pataddr[0] = 0;
 			  pat = &((*pat)->next);
-			  added_canon = true;
+			  no_data = 0;
 			}
 		      else if (req->ai_family == AF_UNSPEC
-			       || air->family[i] == req->ai_family)
+			       || (*pat)->family == req->ai_family)
 			{
-			  (*pat)->family = air->family[i];
-			  memcpy (pataddr, addrs, size);
 			  pat = &((*pat)->next);
-			  added_canon = true;
-			  if (air->family[i] == AF_INET6)
+
+			  no_data = 0;
+			  if (req->ai_family == AF_INET6)
 			    got_ipv6 = true;
 			}
-		      addrs += size;
+		      else
+			*pat = ((*pat)->next);
 		    }
-
-		  free (air);
-
-		  goto process_list;
 		}
-	      else if (err == 0)
-		/* The database contains a negative entry.  */
-		goto free_and_return;
-	      else if (__nss_not_use_nscd_hosts == 0)
-		{
-		  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-		    result = -EAI_MEMORY;
-		  else if (h_errno == TRY_AGAIN)
-		    result = -EAI_AGAIN;
-		  else
-		    result = -EAI_SYSTEM;
 
-		  goto free_and_return;
-		}
+	      no_inet6_data = no_data;
 	    }
-#endif
-
-	  no_more = !__nss_database_get (nss_database_hosts, &nip);
-
-	  /* If we are looking for both IPv4 and IPv6 address we don't
-	     want the lookup functions to automatically promote IPv4
-	     addresses to IPv6 addresses, so we use the no_inet6
-	     function variant.  */
-	  res_ctx = __resolv_context_get ();
-	  if (res_ctx == NULL)
-	    no_more = 1;
-
-	  while (!no_more)
+	  else
 	    {
-	      /* Always start afresh; continue should discard previous results
-		 and the hosts database does not support merge.  */
-	      at = NULL;
-	      free (canon);
-	      free (addrmem);
-	      canon = NULL;
-	      addrmem = NULL;
-
-	      if (do_merge)
+	      nss_gethostbyname3_r *fct = NULL;
+	      if (req->ai_flags & AI_CANONNAME)
+		/* No need to use this function if we do not look for
+		   the canonical name.  The function does not exist in
+		   all NSS modules and therefore the lookup would
+		   often fail.  */
+		fct = __nss_lookup_function (nip, "gethostbyname3_r");
+	      if (fct == NULL)
+		/* We are cheating here.  The gethostbyname2_r
+		   function does not have the same interface as
+		   gethostbyname3_r but the extra arguments the
+		   latter takes are added at the end.  So the
+		   gethostbyname2_r code will just ignore them.  */
+		fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	      if (fct != NULL)
 		{
-		  __set_h_errno (NETDB_INTERNAL);
-		  __set_errno (EBUSY);
-		  break;
-		}
-
-	      no_data = 0;
-	      nss_gethostbyname4_r *fct4 = NULL;
-
-	      /* gethostbyname4_r sends out parallel A and AAAA queries and
-		 is thus only suitable for PF_UNSPEC.  */
-	      if (req->ai_family == PF_UNSPEC)
-		fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+		  struct gaih_addrtuple **pat = &res.at;
 
-	      if (fct4 != NULL)
-		{
-		  while (1)
+		  if (req->ai_family == AF_INET6
+		      || req->ai_family == AF_UNSPEC)
 		    {
-		      status = DL_CALL_FCT (fct4, (name, &at,
-						   tmpbuf->data, tmpbuf->length,
-						   &errno, &h_errno,
-						   NULL));
-		      if (status == NSS_STATUS_SUCCESS)
-			break;
-		      /* gethostbyname4_r may write into AT, so reset it.  */
-		      at = NULL;
-		      if (status != NSS_STATUS_TRYAGAIN
-			  || errno != ERANGE || h_errno != NETDB_INTERNAL)
-			{
-			  if (h_errno == TRY_AGAIN)
-			    no_data = EAI_AGAIN;
-			  else
-			    no_data = h_errno == NO_DATA;
-			  break;
-			}
+		      gethosts (AF_INET6);
+		      no_inet6_data = no_data;
+		      inet6_status = status;
+		    }
+		  if (req->ai_family == AF_INET
+		      || req->ai_family == AF_UNSPEC
+		      || (req->ai_family == AF_INET6
+			  && (req->ai_flags & AI_V4MAPPED)
+			  /* Avoid generating the mapped addresses if we
+			     know we are not going to need them.  */
+			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+		    {
+		      gethosts (AF_INET);
 
-		      if (!scratch_buffer_grow (tmpbuf))
+		      if (req->ai_family == AF_INET)
 			{
-			  __resolv_context_put (res_ctx);
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
+			  no_inet6_data = no_data;
+			  inet6_status = status;
 			}
 		    }
 
-		  if (status == NSS_STATUS_SUCCESS)
+		  /* If we found one address for AF_INET or AF_INET6,
+		     don't continue the search.  */
+		  if (inet6_status == NSS_STATUS_SUCCESS
+		      || status == NSS_STATUS_SUCCESS)
 		    {
-		      assert (!no_data);
-		      no_data = 1;
-
-		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
+		      if ((req->ai_flags & AI_CANONNAME) != 0
+			  && res.canon == NULL)
 			{
-			  char *canonbuf = __strdup (at->name);
+			  char *canonbuf = getcanonname (nip, res.at, name);
 			  if (canonbuf == NULL)
 			    {
 			      __resolv_context_put (res_ctx);
 			      result = -EAI_MEMORY;
 			      goto free_and_return;
 			    }
-			  canon = canonbuf;
-			}
-
-		      struct gaih_addrtuple **pat = &at;
-
-		      while (*pat != NULL)
-			{
-			  if ((*pat)->family == AF_INET
-			      && req->ai_family == AF_INET6
-			      && (req->ai_flags & AI_V4MAPPED) != 0)
-			    {
-			      uint32_t *pataddr = (*pat)->addr;
-			      (*pat)->family = AF_INET6;
-			      pataddr[3] = pataddr[0];
-			      pataddr[2] = htonl (0xffff);
-			      pataddr[1] = 0;
-			      pataddr[0] = 0;
-			      pat = &((*pat)->next);
-			      no_data = 0;
-			    }
-			  else if (req->ai_family == AF_UNSPEC
-				   || (*pat)->family == req->ai_family)
-			    {
-			      pat = &((*pat)->next);
-
-			      no_data = 0;
-			      if (req->ai_family == AF_INET6)
-				got_ipv6 = true;
-			    }
-			  else
-			    *pat = ((*pat)->next);
-			}
-		    }
-
-		  no_inet6_data = no_data;
-		}
-	      else
-		{
-		  nss_gethostbyname3_r *fct = NULL;
-		  if (req->ai_flags & AI_CANONNAME)
-		    /* No need to use this function if we do not look for
-		       the canonical name.  The function does not exist in
-		       all NSS modules and therefore the lookup would
-		       often fail.  */
-		    fct = __nss_lookup_function (nip, "gethostbyname3_r");
-		  if (fct == NULL)
-		    /* We are cheating here.  The gethostbyname2_r
-		       function does not have the same interface as
-		       gethostbyname3_r but the extra arguments the
-		       latter takes are added at the end.  So the
-		       gethostbyname2_r code will just ignore them.  */
-		    fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-		  if (fct != NULL)
-		    {
-		      struct gaih_addrtuple **pat = &at;
-
-		      if (req->ai_family == AF_INET6
-			  || req->ai_family == AF_UNSPEC)
-			{
-			  gethosts (AF_INET6);
-			  no_inet6_data = no_data;
-			  inet6_status = status;
-			}
-		      if (req->ai_family == AF_INET
-			  || req->ai_family == AF_UNSPEC
-			  || (req->ai_family == AF_INET6
-			      && (req->ai_flags & AI_V4MAPPED)
-			      /* Avoid generating the mapped addresses if we
-				 know we are not going to need them.  */
-			      && ((req->ai_flags & AI_ALL) || !got_ipv6)))
-			{
-			  gethosts (AF_INET);
-
-			  if (req->ai_family == AF_INET)
-			    {
-			      no_inet6_data = no_data;
-			      inet6_status = status;
-			    }
-			}
-
-		      /* If we found one address for AF_INET or AF_INET6,
-			 don't continue the search.  */
-		      if (inet6_status == NSS_STATUS_SUCCESS
-			  || status == NSS_STATUS_SUCCESS)
-			{
-			  if ((req->ai_flags & AI_CANONNAME) != 0
-			      && canon == NULL)
-			    {
-			      char *canonbuf = getcanonname (nip, at, name);
-			      if (canonbuf == NULL)
-				{
-				  __resolv_context_put (res_ctx);
-				  result = -EAI_MEMORY;
-				  goto free_and_return;
-				}
-			      canon = canonbuf;
-			    }
-			  status = NSS_STATUS_SUCCESS;
-			}
-		      else
-			{
-			  /* We can have different states for AF_INET and
-			     AF_INET6.  Try to find a useful one for both.  */
-			  if (inet6_status == NSS_STATUS_TRYAGAIN)
-			    status = NSS_STATUS_TRYAGAIN;
-			  else if (status == NSS_STATUS_UNAVAIL
-				   && inet6_status != NSS_STATUS_UNAVAIL)
-			    status = inet6_status;
+			  res.canon = canonbuf;
 			}
+		      status = NSS_STATUS_SUCCESS;
 		    }
 		  else
 		    {
-		      /* Could not locate any of the lookup functions.
-			 The NSS lookup code does not consistently set
-			 errno, so we need to supply our own error
-			 code here.  The root cause could either be a
-			 resource allocation failure, or a missing
-			 service function in the DSO (so it should not
-			 be listed in /etc/nsswitch.conf).  Assume the
-			 former, and return EBUSY.  */
-		      status = NSS_STATUS_UNAVAIL;
-		     __set_h_errno (NETDB_INTERNAL);
-		     __set_errno (EBUSY);
+		      /* We can have different states for AF_INET and
+			 AF_INET6.  Try to find a useful one for both.  */
+		      if (inet6_status == NSS_STATUS_TRYAGAIN)
+			status = NSS_STATUS_TRYAGAIN;
+		      else if (status == NSS_STATUS_UNAVAIL
+			       && inet6_status != NSS_STATUS_UNAVAIL)
+			status = inet6_status;
 		    }
 		}
+	      else
+		{
+		  /* Could not locate any of the lookup functions.
+		     The NSS lookup code does not consistently set
+		     errno, so we need to supply our own error
+		     code here.  The root cause could either be a
+		     resource allocation failure, or a missing
+		     service function in the DSO (so it should not
+		     be listed in /etc/nsswitch.conf).  Assume the
+		     former, and return EBUSY.  */
+		  status = NSS_STATUS_UNAVAIL;
+		  __set_h_errno (NETDB_INTERNAL);
+		  __set_errno (EBUSY);
+		}
+	    }
 
-	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-		break;
+	  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	    break;
 
-	      /* The hosts database does not support MERGE.  */
-	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
-		do_merge = true;
+	  /* The hosts database does not support MERGE.  */
+	  if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+	    do_merge = true;
 
-	      nip++;
-	      if (nip->module == NULL)
-		no_more = -1;
-	    }
+	  nip++;
+	  if (nip->module == NULL)
+	    no_more = -1;
+	}
 
-	  __resolv_context_put (res_ctx);
+      __resolv_context_put (res_ctx);
 
-	  /* If we have a failure which sets errno, report it using
-	     EAI_SYSTEM.  */
-	  if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
-	      && h_errno == NETDB_INTERNAL)
-	    {
-	      result = -EAI_SYSTEM;
-	      goto free_and_return;
-	    }
+      /* If we have a failure which sets errno, report it using
+	 EAI_SYSTEM.  */
+      if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+	  && h_errno == NETDB_INTERNAL)
+	{
+	  result = -EAI_SYSTEM;
+	  goto free_and_return;
+	}
 
-	  if (no_data != 0 && no_inet6_data != 0)
-	    {
-	      /* If both requests timed out report this.  */
-	      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		/* We made requests but they turned out no data.  The name
-		   is known, though.  */
-		result = -EAI_NODATA;
+      if (no_data != 0 && no_inet6_data != 0)
+	{
+	  /* If both requests timed out report this.  */
+	  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+	    result = -EAI_AGAIN;
+	  else
+	    /* We made requests but they turned out no data.  The name
+	       is known, though.  */
+	    result = -EAI_NODATA;
 
-	      goto free_and_return;
-	    }
+	  goto free_and_return;
 	}
 
     process_list:
-      if (at == NULL)
+      if (res.at == NULL)
 	{
 	  result = -EAI_NONAME;
 	  goto free_and_return;
@@ -1031,21 +1043,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
   else
     {
       struct gaih_addrtuple *atr;
-      atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      memset (at, '\0', sizeof (struct gaih_addrtuple));
+      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
+				     alloca_used);
+      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
 
       if (req->ai_family == AF_UNSPEC)
 	{
-	  at->next = __alloca (sizeof (struct gaih_addrtuple));
-	  memset (at->next, '\0', sizeof (struct gaih_addrtuple));
+	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
+	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
 	}
 
       if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
 	{
-	  at->family = AF_INET6;
+	  res.at->family = AF_INET6;
 	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
-	  atr = at->next;
+	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
+	  atr = res.at->next;
 	}
 
       if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
@@ -1058,10 +1071,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   {
     /* Set up the canonical name if we need it.  */
-    if ((result = process_canonname (req, orig_name, &canon)) != 0)
+    if ((result = process_canonname (req, orig_name, &res)) != 0)
       goto free_and_return;
 
-    struct gaih_addrtuple *at2 = at;
+    struct gaih_addrtuple *at2 = res.at;
     size_t socklen;
     sa_family_t family;
 
@@ -1104,8 +1117,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    ai->ai_addr = (void *) (ai + 1);
 
 	    /* We only add the canonical name once.  */
-	    ai->ai_canonname = (char *) canon;
-	    canon = NULL;
+	    ai->ai_canonname = res.canon;
+	    res.canon = NULL;
 
 #ifdef _HAVE_SA_LEN
 	    ai->ai_addr->sa_len = socklen;
@@ -1151,7 +1164,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-  free (canon);
+  free (res.canon);
 
   return result;
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 06/12] gaih_inet: Split simple gethostbyname into its own function
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (4 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  4:20     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
                     ` (5 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Add a free_at flag in gaih_result to indicate if res.at needs to be
freed by the caller.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 125 ++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 63 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index d7b6eae9fc..bcceab7d07 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -120,6 +120,7 @@ struct gaih_result
 {
   struct gaih_addrtuple *at;
   char *canon;
+  bool free_at;
 };
 
 /* Values for `protoflag'.  */
@@ -565,6 +566,60 @@ out:
   return result;
 }
 
+/* If possible, call the simple, old functions, which do not support IPv6 scope
+   ids, nor retrieving the canonical name.  */
+
+static int
+try_simple_gethostbyname (const char *name, const struct addrinfo *req,
+			  struct scratch_buffer *tmpbuf,
+			  struct gaih_result *res)
+{
+  res->at = NULL;
+
+  if (req->ai_family != AF_INET || (req->ai_flags & AI_CANONNAME) != 0)
+    return 0;
+
+  int rc;
+  struct hostent th;
+  struct hostent *h;
+
+  while (1)
+    {
+      rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf->data,
+			       tmpbuf->length, &h, &h_errno);
+      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
+	break;
+      if (!scratch_buffer_grow (tmpbuf))
+	return -EAI_MEMORY;
+    }
+
+  if (rc == 0)
+    {
+      if (h != NULL)
+	{
+	  /* We found data, convert it.  */
+	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
+	    return -EAI_MEMORY;
+
+	  res->free_at = true;
+	  return 0;
+	}
+      if (h_errno == NO_DATA)
+	return -EAI_NODATA;
+
+      return -EAI_NONAME;
+    }
+
+  if (h_errno == NETDB_INTERNAL)
+    return -EAI_SYSTEM;
+  if (h_errno == TRY_AGAIN)
+    return -EAI_AGAIN;
+
+  /* We made requests but they turned out no data.
+     The name is known, though.  */
+  return -EAI_NODATA;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -610,6 +665,11 @@ gaih_inet (const char *name, const struct gaih_service *service,
       else if (res.at != NULL)
 	goto process_list;
 
+      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+
       int no_data = 0;
       int no_inet6_data = 0;
       nss_action_list nip;
@@ -619,69 +679,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
       struct resolv_context *res_ctx = NULL;
       bool do_merge = false;
 
-      /* If we do not have to look for IPv6 addresses or the canonical
-	 name, use the simple, old functions, which do not support
-	 IPv6 scope ids, nor retrieving the canonical name.  */
-      if (req->ai_family == AF_INET
-	  && (req->ai_flags & AI_CANONNAME) == 0)
-	{
-	  int rc;
-	  struct hostent th;
-	  struct hostent *h;
-
-	  while (1)
-	    {
-	      rc = __gethostbyname2_r (name, AF_INET, &th,
-				       tmpbuf->data, tmpbuf->length,
-				       &h, &h_errno);
-	      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
-		break;
-	      if (!scratch_buffer_grow (tmpbuf))
-		{
-		  result = -EAI_MEMORY;
-		  goto free_and_return;
-		}
-	    }
-
-	  if (rc == 0)
-	    {
-	      if (h != NULL)
-		{
-		  /* We found data, convert it.  */
-		  if (!convert_hostent_to_gaih_addrtuple
-		      (req, AF_INET, h, &addrmem))
-		    {
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
-		    }
-		  res.at = addrmem;
-		}
-	      else
-		{
-		  if (h_errno == NO_DATA)
-		    result = -EAI_NODATA;
-		  else
-		    result = -EAI_NONAME;
-		  goto free_and_return;
-		}
-	    }
-	  else
-	    {
-	      if (h_errno == NETDB_INTERNAL)
-		result = -EAI_SYSTEM;
-	      else if (h_errno == TRY_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		/* We made requests but they turned out no data.
-		   The name is known, though.  */
-		result = -EAI_NODATA;
-
-	      goto free_and_return;
-	    }
-
-	  goto process_list;
-	}
-
 #ifdef USE_NSCD
       if (__nss_not_use_nscd_hosts > 0
 	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
@@ -1164,6 +1161,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
+  if (res.free_at)
+    free (res.at);
   free (res.canon);
 
   return result;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 07/12] gaih_inet: Split nscd lookup code into its own function.
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (5 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  4:31     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
                     ` (4 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Add a new member got_ipv6 to indicate if the results have an IPv6
result and use it instead of the local got_ipv6.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 248 +++++++++++++++++++-----------------
 1 file changed, 134 insertions(+), 114 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index bcceab7d07..ba5028ad7b 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -121,6 +121,7 @@ struct gaih_result
   struct gaih_addrtuple *at;
   char *canon;
   bool free_at;
+  bool got_ipv6;
 };
 
 /* Values for `protoflag'.  */
@@ -316,7 +317,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	  res.canon = canonbuf;						      \
 	}								      \
       if (_family == AF_INET6 && *pat != NULL)				      \
-	got_ipv6 = true;						      \
+	res.got_ipv6 = true;						      \
     }									      \
  }
 
@@ -467,6 +468,128 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
   return 0;
 }
 
+#ifdef USE_NSCD
+/* Query addresses from nscd cache, returning a non-zero value on error.
+   RES members have the lookup result; RES->AT is NULL if there were no errors
+   but also no results.  */
+
+static int
+get_nscd_addresses (const char *name, const struct addrinfo *req,
+		    struct gaih_result *res)
+{
+  if (__nss_not_use_nscd_hosts > 0
+      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+    __nss_not_use_nscd_hosts = 0;
+
+  res->at = NULL;
+
+  if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts])
+    return 0;
+
+  /* Try to use nscd.  */
+  struct nscd_ai_result *air = NULL;
+  int err = __nscd_getai (name, &air, &h_errno);
+
+  if (__glibc_unlikely (air == NULL))
+    {
+      /* The database contains a negative entry.  */
+      if (err == 0)
+	return -EAI_NONAME;
+      if (__nss_not_use_nscd_hosts == 0)
+	{
+	  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+	    return -EAI_MEMORY;
+	  if (h_errno == TRY_AGAIN)
+	    return -EAI_AGAIN;
+	  return -EAI_SYSTEM;
+	}
+      return 0;
+    }
+
+  /* Transform into gaih_addrtuple list.  */
+  int result = 0;
+  char *addrs = air->addrs;
+
+  struct gaih_addrtuple *addrfree = calloc (air->naddrs, sizeof (*addrfree));
+  struct gaih_addrtuple *at = calloc (air->naddrs, sizeof (*at));
+  if (at == NULL)
+    {
+      result = -EAI_MEMORY;
+      goto out;
+    }
+
+  res->free_at = true;
+
+  int count = 0;
+  for (int i = 0; i < air->naddrs; ++i)
+    {
+      socklen_t size = (air->family[i] == AF_INET
+			? INADDRSZ : IN6ADDRSZ);
+
+      if (!((air->family[i] == AF_INET
+	     && req->ai_family == AF_INET6
+	     && (req->ai_flags & AI_V4MAPPED) != 0)
+	    || req->ai_family == AF_UNSPEC
+	    || air->family[i] == req->ai_family))
+	{
+	  /* Skip over non-matching result.  */
+	  addrs += size;
+	  continue;
+	}
+
+      if (air->family[i] == AF_INET && req->ai_family == AF_INET6
+	  && (req->ai_flags & AI_V4MAPPED))
+	{
+	  at[count].family = AF_INET6;
+	  at[count].addr[3] = *(uint32_t *) addrs;
+	  at[count].addr[2] = htonl (0xffff);
+	}
+      else if (req->ai_family == AF_UNSPEC
+	       || air->family[count] == req->ai_family)
+	{
+	  at[count].family = air->family[count];
+	  memcpy (at[count].addr, addrs, size);
+	  if (air->family[count] == AF_INET6)
+	    res->got_ipv6 = true;
+	}
+      at[count].next = at + count + 1;
+      count++;
+      addrs += size;
+    }
+
+  if ((req->ai_flags & AI_CANONNAME) && air->canon != NULL)
+    {
+      char *canonbuf = __strdup (air->canon);
+      if (canonbuf == NULL)
+	{
+	  result = -EAI_MEMORY;
+	  goto out;
+	}
+      res->canon = canonbuf;
+    }
+
+  if (count == 0)
+    {
+      result = -EAI_NONAME;
+      goto out;
+    }
+
+  at[count - 1].next = NULL;
+
+  res->at = at;
+
+out:
+  free (air);
+  if (result != 0)
+    {
+      free (at);
+      res->free_at = false;
+    }
+
+  return result;
+}
+#endif
+
 /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
    NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
    the function cannot determine a result, RES->AT is set to NULL and 0
@@ -628,7 +751,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
 			   / sizeof (struct gaih_typeproto)] = {0};
 
-  bool got_ipv6 = false;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -670,6 +792,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
       else if (res.at != NULL)
 	goto process_list;
 
+#ifdef USE_NSCD
+      if ((result = get_nscd_addresses (name, req, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+#endif
+
       int no_data = 0;
       int no_inet6_data = 0;
       nss_action_list nip;
@@ -679,115 +808,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
       struct resolv_context *res_ctx = NULL;
       bool do_merge = false;
 
-#ifdef USE_NSCD
-      if (__nss_not_use_nscd_hosts > 0
-	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-	__nss_not_use_nscd_hosts = 0;
-
-      if (!__nss_not_use_nscd_hosts
-	  && !__nss_database_custom[NSS_DBSIDX_hosts])
-	{
-	  /* Try to use nscd.  */
-	  struct nscd_ai_result *air = NULL;
-	  int err = __nscd_getai (name, &air, &h_errno);
-	  if (air != NULL)
-	    {
-	      /* Transform into gaih_addrtuple list.  */
-	      bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
-	      char *addrs = air->addrs;
-
-	      addrmem = calloc (air->naddrs, sizeof (*addrmem));
-	      if (addrmem == NULL)
-		{
-		  result = -EAI_MEMORY;
-		  goto free_and_return;
-		}
-
-	      struct gaih_addrtuple *addrfree = addrmem;
-	      struct gaih_addrtuple **pat = &res.at;
-
-	      for (int i = 0; i < air->naddrs; ++i)
-		{
-		  socklen_t size = (air->family[i] == AF_INET
-				    ? INADDRSZ : IN6ADDRSZ);
-
-		  if (!((air->family[i] == AF_INET
-			 && req->ai_family == AF_INET6
-			 && (req->ai_flags & AI_V4MAPPED) != 0)
-			|| req->ai_family == AF_UNSPEC
-			|| air->family[i] == req->ai_family))
-		    {
-		      /* Skip over non-matching result.  */
-		      addrs += size;
-		      continue;
-		    }
-
-		  if (*pat == NULL)
-		    {
-		      *pat = addrfree++;
-		      (*pat)->scopeid = 0;
-		    }
-		  uint32_t *pataddr = (*pat)->addr;
-		  (*pat)->next = NULL;
-		  if (added_canon || air->canon == NULL)
-		    (*pat)->name = NULL;
-		  else if (res.canon == NULL)
-		    {
-		      char *canonbuf = __strdup (air->canon);
-		      if (canonbuf == NULL)
-			{
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
-			}
-		      res.canon = (*pat)->name = canonbuf;
-		    }
-
-		  if (air->family[i] == AF_INET
-		      && req->ai_family == AF_INET6
-		      && (req->ai_flags & AI_V4MAPPED))
-		    {
-		      (*pat)->family = AF_INET6;
-		      pataddr[3] = *(uint32_t *) addrs;
-		      pataddr[2] = htonl (0xffff);
-		      pataddr[1] = 0;
-		      pataddr[0] = 0;
-		      pat = &((*pat)->next);
-		      added_canon = true;
-		    }
-		  else if (req->ai_family == AF_UNSPEC
-			   || air->family[i] == req->ai_family)
-		    {
-		      (*pat)->family = air->family[i];
-		      memcpy (pataddr, addrs, size);
-		      pat = &((*pat)->next);
-		      added_canon = true;
-		      if (air->family[i] == AF_INET6)
-			got_ipv6 = true;
-		    }
-		  addrs += size;
-		}
-
-	      free (air);
-
-	      goto process_list;
-	    }
-	  else if (err == 0)
-	    /* The database contains a negative entry.  */
-	    goto free_and_return;
-	  else if (__nss_not_use_nscd_hosts == 0)
-	    {
-	      if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-		result = -EAI_MEMORY;
-	      else if (h_errno == TRY_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		result = -EAI_SYSTEM;
-
-	      goto free_and_return;
-	    }
-	}
-#endif
-
       no_more = !__nss_database_get (nss_database_hosts, &nip);
 
       /* If we are looking for both IPv4 and IPv6 address we don't
@@ -894,7 +914,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 			  no_data = 0;
 			  if (req->ai_family == AF_INET6)
-			    got_ipv6 = true;
+			    res.got_ipv6 = true;
 			}
 		      else
 			*pat = ((*pat)->next);
@@ -937,7 +957,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  && (req->ai_flags & AI_V4MAPPED)
 			  /* Avoid generating the mapped addresses if we
 			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))
 		    {
 		      gethosts (AF_INET);
 
@@ -1088,7 +1108,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    /* If we looked up IPv4 mapped address discard them here if
 	       the caller isn't interested in all address and we have
 	       found at least one IPv6 address.  */
-	    if (got_ipv6
+	    if (res.got_ipv6
 		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
 		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
 	      goto ignore;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 08/12] gaih_inet: separate nss lookup loop into its own function
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (6 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  4:42     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
                     ` (3 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 566 ++++++++++++++++++------------------
 1 file changed, 288 insertions(+), 278 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index ba5028ad7b..c33cc2507b 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -159,6 +159,14 @@ static const struct addrinfo default_hints =
     .ai_next = NULL
   };
 
+static void
+gaih_result_reset (struct gaih_result *res)
+{
+  if (res->free_at)
+    free (res->at);
+  free (res->canon);
+  memset (res, 0, sizeof (*res));
+}
 
 static int
 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
@@ -195,15 +203,13 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
   return 0;
 }
 
-/* Convert struct hostent to a list of struct gaih_addrtuple objects.
-   h_name is not copied, and the struct hostent object must not be
-   deallocated prematurely.  *RESULT must be NULL or a pointer to a
-   linked-list.  The new addresses are appended at the end.  */
+/* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
+   is not copied, and the struct hostent object must not be deallocated
+   prematurely.  *RESULT must be NULL or a pointer to a linked-list.  The new
+   addresses are appended at the end.  */
 static bool
-convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
-				   int family,
-				   struct hostent *h,
-				   struct gaih_addrtuple **result)
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+				   struct hostent *h, struct gaih_result *res)
 {
   /* Count the number of addresses in h->h_addr_list.  */
   size_t count = 0;
@@ -215,7 +221,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
     return true;
 
-  struct gaih_addrtuple *array = *result;
+  struct gaih_addrtuple *array = res->at;
   size_t old = 0;
 
   while (array != NULL)
@@ -224,12 +230,13 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
       array = array->next;
     }
 
-  array = realloc (*result, (old + count) * sizeof (*array));
+  array = res->at = realloc (res->at, (old + count) * sizeof (*array));
 
   if (array == NULL)
     return false;
 
-  *result = array;
+  res->got_ipv6 = family == AF_INET6;
+  res->free_at = true;
 
   /* Update the next pointers on reallocation.  */
   for (size_t i = 0; i < old; i++)
@@ -278,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_MEMORY;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
     }									      \
   if (status == NSS_STATUS_NOTFOUND					      \
@@ -288,7 +295,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_SYSTEM;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
       if (h_errno == TRY_AGAIN)						      \
 	no_data = EAI_AGAIN;						      \
@@ -297,27 +304,24 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
     }									      \
   else if (status == NSS_STATUS_SUCCESS)				      \
     {									      \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem))   \
+      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_SYSTEM;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
-      *pat = addrmem;							      \
 									      \
-      if (localcanon != NULL && res.canon == NULL)			      \
+      if (localcanon != NULL && res->canon == NULL)			      \
 	{								      \
 	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
 	    {								      \
 	      __resolv_context_put (res_ctx);				      \
 	      result = -EAI_SYSTEM;					      \
-	      goto free_and_return;					      \
+	      goto out;							      \
 	    }								      \
-	  res.canon = canonbuf;						      \
+	  res->canon = canonbuf;					      \
 	}								      \
-      if (_family == AF_INET6 && *pat != NULL)				      \
-	res.got_ipv6 = true;						      \
     }									      \
  }
 
@@ -590,6 +594,260 @@ out:
 }
 #endif
 
+static int
+get_nss_addresses (const char *name, const struct addrinfo *req,
+		   struct scratch_buffer *tmpbuf, struct gaih_result *res)
+{
+  int no_data = 0;
+  int no_inet6_data = 0;
+  nss_action_list nip;
+  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+  enum nss_status status = NSS_STATUS_UNAVAIL;
+  int no_more;
+  struct resolv_context *res_ctx = NULL;
+  bool do_merge = false;
+  int result = 0;
+
+  no_more = !__nss_database_get (nss_database_hosts, &nip);
+
+  /* If we are looking for both IPv4 and IPv6 address we don't
+     want the lookup functions to automatically promote IPv4
+     addresses to IPv6 addresses, so we use the no_inet6
+     function variant.  */
+  res_ctx = __resolv_context_get ();
+  if (res_ctx == NULL)
+    no_more = 1;
+
+  while (!no_more)
+    {
+      /* Always start afresh; continue should discard previous results
+	 and the hosts database does not support merge.  */
+      gaih_result_reset (res);
+
+      if (do_merge)
+	{
+	  __set_h_errno (NETDB_INTERNAL);
+	  __set_errno (EBUSY);
+	  break;
+	}
+
+      no_data = 0;
+      nss_gethostbyname4_r *fct4 = NULL;
+
+      /* gethostbyname4_r sends out parallel A and AAAA queries and
+	 is thus only suitable for PF_UNSPEC.  */
+      if (req->ai_family == PF_UNSPEC)
+	fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+
+      if (fct4 != NULL)
+	{
+	  while (1)
+	    {
+	      status = DL_CALL_FCT (fct4, (name, &res->at,
+					   tmpbuf->data, tmpbuf->length,
+					   &errno, &h_errno,
+					   NULL));
+	      if (status == NSS_STATUS_SUCCESS)
+		break;
+	      /* gethostbyname4_r may write into AT, so reset it.  */
+	      res->at = NULL;
+	      if (status != NSS_STATUS_TRYAGAIN
+		  || errno != ERANGE || h_errno != NETDB_INTERNAL)
+		{
+		  if (h_errno == TRY_AGAIN)
+		    no_data = EAI_AGAIN;
+		  else
+		    no_data = h_errno == NO_DATA;
+		  break;
+		}
+
+	      if (!scratch_buffer_grow (tmpbuf))
+		{
+		  __resolv_context_put (res_ctx);
+		  result = -EAI_MEMORY;
+		  goto out;
+		}
+	    }
+
+	  if (status == NSS_STATUS_SUCCESS)
+	    {
+	      assert (!no_data);
+	      no_data = 1;
+
+	      if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL)
+		{
+		  char *canonbuf = __strdup (res->at->name);
+		  if (canonbuf == NULL)
+		    {
+		      __resolv_context_put (res_ctx);
+		      result = -EAI_MEMORY;
+		      goto out;
+		    }
+		  res->canon = canonbuf;
+		}
+
+	      struct gaih_addrtuple **pat = &res->at;
+
+	      while (*pat != NULL)
+		{
+		  if ((*pat)->family == AF_INET
+		      && req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED) != 0)
+		    {
+		      uint32_t *pataddr = (*pat)->addr;
+		      (*pat)->family = AF_INET6;
+		      pataddr[3] = pataddr[0];
+		      pataddr[2] = htonl (0xffff);
+		      pataddr[1] = 0;
+		      pataddr[0] = 0;
+		      pat = &((*pat)->next);
+		      no_data = 0;
+		    }
+		  else if (req->ai_family == AF_UNSPEC
+			   || (*pat)->family == req->ai_family)
+		    {
+		      pat = &((*pat)->next);
+
+		      no_data = 0;
+		      if (req->ai_family == AF_INET6)
+			res->got_ipv6 = true;
+		    }
+		  else
+		    *pat = ((*pat)->next);
+		}
+	    }
+
+	  no_inet6_data = no_data;
+	}
+      else
+	{
+	  nss_gethostbyname3_r *fct = NULL;
+	  if (req->ai_flags & AI_CANONNAME)
+	    /* No need to use this function if we do not look for
+	       the canonical name.  The function does not exist in
+	       all NSS modules and therefore the lookup would
+	       often fail.  */
+	    fct = __nss_lookup_function (nip, "gethostbyname3_r");
+	  if (fct == NULL)
+	    /* We are cheating here.  The gethostbyname2_r
+	       function does not have the same interface as
+	       gethostbyname3_r but the extra arguments the
+	       latter takes are added at the end.  So the
+	       gethostbyname2_r code will just ignore them.  */
+	    fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	  if (fct != NULL)
+	    {
+	      if (req->ai_family == AF_INET6
+		  || req->ai_family == AF_UNSPEC)
+		{
+		  gethosts (AF_INET6);
+		  no_inet6_data = no_data;
+		  inet6_status = status;
+		}
+	      if (req->ai_family == AF_INET
+		  || req->ai_family == AF_UNSPEC
+		  || (req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED)
+		      /* Avoid generating the mapped addresses if we
+			 know we are not going to need them.  */
+		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
+		{
+		  gethosts (AF_INET);
+
+		  if (req->ai_family == AF_INET)
+		    {
+		      no_inet6_data = no_data;
+		      inet6_status = status;
+		    }
+		}
+
+	      /* If we found one address for AF_INET or AF_INET6,
+		 don't continue the search.  */
+	      if (inet6_status == NSS_STATUS_SUCCESS
+		  || status == NSS_STATUS_SUCCESS)
+		{
+		  if ((req->ai_flags & AI_CANONNAME) != 0
+		      && res->canon == NULL)
+		    {
+		      char *canonbuf = getcanonname (nip, res->at, name);
+		      if (canonbuf == NULL)
+			{
+			  __resolv_context_put (res_ctx);
+			  result = -EAI_MEMORY;
+			  goto out;
+			}
+		      res->canon = canonbuf;
+		    }
+		  status = NSS_STATUS_SUCCESS;
+		}
+	      else
+		{
+		  /* We can have different states for AF_INET and
+		     AF_INET6.  Try to find a useful one for both.  */
+		  if (inet6_status == NSS_STATUS_TRYAGAIN)
+		    status = NSS_STATUS_TRYAGAIN;
+		  else if (status == NSS_STATUS_UNAVAIL
+			   && inet6_status != NSS_STATUS_UNAVAIL)
+		    status = inet6_status;
+		}
+	    }
+	  else
+	    {
+	      /* Could not locate any of the lookup functions.
+		 The NSS lookup code does not consistently set
+		 errno, so we need to supply our own error
+		 code here.  The root cause could either be a
+		 resource allocation failure, or a missing
+		 service function in the DSO (so it should not
+		 be listed in /etc/nsswitch.conf).  Assume the
+		 former, and return EBUSY.  */
+	      status = NSS_STATUS_UNAVAIL;
+	      __set_h_errno (NETDB_INTERNAL);
+	      __set_errno (EBUSY);
+	    }
+	}
+
+      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	break;
+
+      /* The hosts database does not support MERGE.  */
+      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+	do_merge = true;
+
+      nip++;
+      if (nip->module == NULL)
+	no_more = -1;
+    }
+
+  __resolv_context_put (res_ctx);
+
+  /* If we have a failure which sets errno, report it using
+     EAI_SYSTEM.  */
+  if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+      && h_errno == NETDB_INTERNAL)
+    {
+      result = -EAI_SYSTEM;
+      goto out;
+    }
+
+  if (no_data != 0 && no_inet6_data != 0)
+    {
+      /* If both requests timed out report this.  */
+      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+	result = -EAI_AGAIN;
+      else
+	/* We made requests but they turned out no data.  The name
+	   is known, though.  */
+	result = -EAI_NODATA;
+    }
+
+out:
+  if (result != 0)
+    gaih_result_reset (res);
+  return result;
+}
+
 /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
    NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
    the function cannot determine a result, RES->AT is set to NULL and 0
@@ -721,7 +979,7 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
       if (h != NULL)
 	{
 	  /* We found data, convert it.  */
-	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
+	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res))
 	    return -EAI_MEMORY;
 
 	  res->free_at = true;
@@ -799,263 +1057,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	goto process_list;
 #endif
 
-      int no_data = 0;
-      int no_inet6_data = 0;
-      nss_action_list nip;
-      enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
-      enum nss_status status = NSS_STATUS_UNAVAIL;
-      int no_more;
-      struct resolv_context *res_ctx = NULL;
-      bool do_merge = false;
-
-      no_more = !__nss_database_get (nss_database_hosts, &nip);
-
-      /* If we are looking for both IPv4 and IPv6 address we don't
-	 want the lookup functions to automatically promote IPv4
-	 addresses to IPv6 addresses, so we use the no_inet6
-	 function variant.  */
-      res_ctx = __resolv_context_get ();
-      if (res_ctx == NULL)
-	no_more = 1;
-
-      while (!no_more)
-	{
-	  /* Always start afresh; continue should discard previous results
-	     and the hosts database does not support merge.  */
-	  res.at = NULL;
-	  free (res.canon);
-	  free (addrmem);
-	  res.canon = NULL;
-	  addrmem = NULL;
-
-	  if (do_merge)
-	    {
-	      __set_h_errno (NETDB_INTERNAL);
-	      __set_errno (EBUSY);
-	      break;
-	    }
-
-	  no_data = 0;
-	  nss_gethostbyname4_r *fct4 = NULL;
-
-	  /* gethostbyname4_r sends out parallel A and AAAA queries and
-	     is thus only suitable for PF_UNSPEC.  */
-	  if (req->ai_family == PF_UNSPEC)
-	    fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
-
-	  if (fct4 != NULL)
-	    {
-	      while (1)
-		{
-		  status = DL_CALL_FCT (fct4, (name, &res.at,
-					       tmpbuf->data, tmpbuf->length,
-					       &errno, &h_errno,
-					       NULL));
-		  if (status == NSS_STATUS_SUCCESS)
-		    break;
-		  /* gethostbyname4_r may write into AT, so reset it.  */
-		  res.at = NULL;
-		  if (status != NSS_STATUS_TRYAGAIN
-		      || errno != ERANGE || h_errno != NETDB_INTERNAL)
-		    {
-		      if (h_errno == TRY_AGAIN)
-			no_data = EAI_AGAIN;
-		      else
-			no_data = h_errno == NO_DATA;
-		      break;
-		    }
-
-		  if (!scratch_buffer_grow (tmpbuf))
-		    {
-		      __resolv_context_put (res_ctx);
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
-		    }
-		}
-
-	      if (status == NSS_STATUS_SUCCESS)
-		{
-		  assert (!no_data);
-		  no_data = 1;
-
-		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
-		    {
-		      char *canonbuf = __strdup (res.at->name);
-		      if (canonbuf == NULL)
-			{
-			  __resolv_context_put (res_ctx);
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
-			}
-		      res.canon = canonbuf;
-		    }
-
-		  struct gaih_addrtuple **pat = &res.at;
-
-		  while (*pat != NULL)
-		    {
-		      if ((*pat)->family == AF_INET
-			  && req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED) != 0)
-			{
-			  uint32_t *pataddr = (*pat)->addr;
-			  (*pat)->family = AF_INET6;
-			  pataddr[3] = pataddr[0];
-			  pataddr[2] = htonl (0xffff);
-			  pataddr[1] = 0;
-			  pataddr[0] = 0;
-			  pat = &((*pat)->next);
-			  no_data = 0;
-			}
-		      else if (req->ai_family == AF_UNSPEC
-			       || (*pat)->family == req->ai_family)
-			{
-			  pat = &((*pat)->next);
-
-			  no_data = 0;
-			  if (req->ai_family == AF_INET6)
-			    res.got_ipv6 = true;
-			}
-		      else
-			*pat = ((*pat)->next);
-		    }
-		}
-
-	      no_inet6_data = no_data;
-	    }
-	  else
-	    {
-	      nss_gethostbyname3_r *fct = NULL;
-	      if (req->ai_flags & AI_CANONNAME)
-		/* No need to use this function if we do not look for
-		   the canonical name.  The function does not exist in
-		   all NSS modules and therefore the lookup would
-		   often fail.  */
-		fct = __nss_lookup_function (nip, "gethostbyname3_r");
-	      if (fct == NULL)
-		/* We are cheating here.  The gethostbyname2_r
-		   function does not have the same interface as
-		   gethostbyname3_r but the extra arguments the
-		   latter takes are added at the end.  So the
-		   gethostbyname2_r code will just ignore them.  */
-		fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-	      if (fct != NULL)
-		{
-		  struct gaih_addrtuple **pat = &res.at;
-
-		  if (req->ai_family == AF_INET6
-		      || req->ai_family == AF_UNSPEC)
-		    {
-		      gethosts (AF_INET6);
-		      no_inet6_data = no_data;
-		      inet6_status = status;
-		    }
-		  if (req->ai_family == AF_INET
-		      || req->ai_family == AF_UNSPEC
-		      || (req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED)
-			  /* Avoid generating the mapped addresses if we
-			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))
-		    {
-		      gethosts (AF_INET);
-
-		      if (req->ai_family == AF_INET)
-			{
-			  no_inet6_data = no_data;
-			  inet6_status = status;
-			}
-		    }
-
-		  /* If we found one address for AF_INET or AF_INET6,
-		     don't continue the search.  */
-		  if (inet6_status == NSS_STATUS_SUCCESS
-		      || status == NSS_STATUS_SUCCESS)
-		    {
-		      if ((req->ai_flags & AI_CANONNAME) != 0
-			  && res.canon == NULL)
-			{
-			  char *canonbuf = getcanonname (nip, res.at, name);
-			  if (canonbuf == NULL)
-			    {
-			      __resolv_context_put (res_ctx);
-			      result = -EAI_MEMORY;
-			      goto free_and_return;
-			    }
-			  res.canon = canonbuf;
-			}
-		      status = NSS_STATUS_SUCCESS;
-		    }
-		  else
-		    {
-		      /* We can have different states for AF_INET and
-			 AF_INET6.  Try to find a useful one for both.  */
-		      if (inet6_status == NSS_STATUS_TRYAGAIN)
-			status = NSS_STATUS_TRYAGAIN;
-		      else if (status == NSS_STATUS_UNAVAIL
-			       && inet6_status != NSS_STATUS_UNAVAIL)
-			status = inet6_status;
-		    }
-		}
-	      else
-		{
-		  /* Could not locate any of the lookup functions.
-		     The NSS lookup code does not consistently set
-		     errno, so we need to supply our own error
-		     code here.  The root cause could either be a
-		     resource allocation failure, or a missing
-		     service function in the DSO (so it should not
-		     be listed in /etc/nsswitch.conf).  Assume the
-		     former, and return EBUSY.  */
-		  status = NSS_STATUS_UNAVAIL;
-		  __set_h_errno (NETDB_INTERNAL);
-		  __set_errno (EBUSY);
-		}
-	    }
-
-	  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-	    break;
-
-	  /* The hosts database does not support MERGE.  */
-	  if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
-	    do_merge = true;
-
-	  nip++;
-	  if (nip->module == NULL)
-	    no_more = -1;
-	}
-
-      __resolv_context_put (res_ctx);
-
-      /* If we have a failure which sets errno, report it using
-	 EAI_SYSTEM.  */
-      if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
-	  && h_errno == NETDB_INTERNAL)
-	{
-	  result = -EAI_SYSTEM;
-	  goto free_and_return;
-	}
-
-      if (no_data != 0 && no_inet6_data != 0)
-	{
-	  /* If both requests timed out report this.  */
-	  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-	    result = -EAI_AGAIN;
-	  else
-	    /* We made requests but they turned out no data.  The name
-	       is known, though.  */
-	    result = -EAI_NODATA;
-
-	  goto free_and_return;
-	}
+      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
 
-    process_list:
-      if (res.at == NULL)
-	{
-	  result = -EAI_NONAME;
-	  goto free_and_return;
-	}
+      /* None of the lookups worked, so name not found.  */
+      result = -EAI_NONAME;
+      goto free_and_return;
     }
   else
     {
@@ -1086,6 +1095,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
     }
 
+process_list:
   {
     /* Set up the canonical name if we need it.  */
     if ((result = process_canonname (req, orig_name, &res)) != 0)
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 09/12] gaih_inet: make gethosts into a function
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (7 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  4:44     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

The macro is quite a pain to debug, so make gethosts into a function to
make it easier to maintain.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 117 ++++++++++++++++++------------------
 1 file changed, 59 insertions(+), 58 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index c33cc2507b..e71f21f3c9 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -268,63 +268,54 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
   return true;
 }
 
-#define gethosts(_family) \
- {									      \
-  struct hostent th;							      \
-  char *localcanon = NULL;						      \
-  no_data = 0;								      \
-  while (1)								      \
-    {									      \
-      status = DL_CALL_FCT (fct, (name, _family, &th,			      \
-				  tmpbuf->data, tmpbuf->length,		      \
-				  &errno, &h_errno, NULL, &localcanon));      \
-      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL	      \
-	  || errno != ERANGE)						      \
-	break;								      \
-      if (!scratch_buffer_grow (tmpbuf))				      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_MEMORY;						      \
-	  goto out;							      \
-	}								      \
-    }									      \
-  if (status == NSS_STATUS_NOTFOUND					      \
-      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)	      \
-    {									      \
-      if (h_errno == NETDB_INTERNAL)					      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_SYSTEM;						      \
-	  goto out;							      \
-	}								      \
-      if (h_errno == TRY_AGAIN)						      \
-	no_data = EAI_AGAIN;						      \
-      else								      \
-	no_data = h_errno == NO_DATA;					      \
-    }									      \
-  else if (status == NSS_STATUS_SUCCESS)				      \
-    {									      \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_SYSTEM;						      \
-	  goto out;							      \
-	}								      \
-									      \
-      if (localcanon != NULL && res->canon == NULL)			      \
-	{								      \
-	  char *canonbuf = __strdup (localcanon);			      \
-	  if (canonbuf == NULL)						      \
-	    {								      \
-	      __resolv_context_put (res_ctx);				      \
-	      result = -EAI_SYSTEM;					      \
-	      goto out;							      \
-	    }								      \
-	  res->canon = canonbuf;					      \
-	}								      \
-    }									      \
- }
+static int
+gethosts (nss_gethostbyname3_r fct, int family, const char *name,
+	  const struct addrinfo *req, struct scratch_buffer *tmpbuf,
+	  struct gaih_result *res, enum nss_status *statusp, int *no_datap)
+{
+  struct hostent th;
+  char *localcanon = NULL;
+  enum nss_status status;
+
+  *no_datap = 0;
+  while (1)
+    {
+      *statusp = status = DL_CALL_FCT (fct, (name, family, &th,
+					     tmpbuf->data, tmpbuf->length,
+					     &errno, &h_errno, NULL,
+					     &localcanon));
+      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL
+	  || errno != ERANGE)
+	break;
+      if (!scratch_buffer_grow (tmpbuf))
+	return -EAI_MEMORY;
+    }
+  if (status == NSS_STATUS_NOTFOUND
+      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+    {
+      if (h_errno == NETDB_INTERNAL)
+	return -EAI_SYSTEM;
+      if (h_errno == TRY_AGAIN)
+	*no_datap = EAI_AGAIN;
+      else
+	*no_datap = h_errno == NO_DATA;
+    }
+  else if (status == NSS_STATUS_SUCCESS)
+    {
+      if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
+	return -EAI_SYSTEM;
+
+      if (localcanon != NULL && res->canon == NULL)
+	{
+	  char *canonbuf = __strdup (localcanon);
+	  if (canonbuf == NULL)
+	    return  -EAI_SYSTEM;
+	  res->canon = canonbuf;
+	}
+    }
 
+  return 0;
+}
 
 /* This function is called if a canonical name is requested, but if
    the service function did not provide it.  It tries to obtain the
@@ -741,7 +732,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
 	      if (req->ai_family == AF_INET6
 		  || req->ai_family == AF_UNSPEC)
 		{
-		  gethosts (AF_INET6);
+		  if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf,
+					  res, &status, &no_data)) != 0)
+		    {
+		      __resolv_context_put (res_ctx);
+		      goto out;
+		    }
 		  no_inet6_data = no_data;
 		  inet6_status = status;
 		}
@@ -753,7 +749,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
 			 know we are not going to need them.  */
 		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
 		{
-		  gethosts (AF_INET);
+		  if ((result = gethosts (fct, AF_INET, name, req, tmpbuf,
+					  res, &status, &no_data)) != 0)
+		    {
+		      __resolv_context_put (res_ctx);
+		      goto out;
+		    }
 
 		  if (req->ai_family == AF_INET)
 		    {
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 10/12] gaih_inet: split loopback lookup into its own function
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (8 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  4:51     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
  2022-03-14  9:48   ` [PATCH v2 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Flatten the condition nesting and replace the alloca for RET.AT/ATR with
a single array LOCAL_AT[2].  This gets rid of alloca and alloca
accounting.

`git diff -b` is probably the best way to view this change since much of
the diff is whitespace changes.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 127 ++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 65 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index e71f21f3c9..3aeeb0d1e0 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1002,6 +1002,32 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
   return -EAI_NODATA;
 }
 
+/* Add local address information into RES.  RES->AT is assumed to have enough
+   space for two tuples and is zeroed out.  */
+
+static void
+get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
+{
+  struct gaih_addrtuple *atr = res->at;
+  if (req->ai_family == AF_UNSPEC)
+    res->at->next = res->at + 1;
+
+  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+    {
+      res->at->family = AF_INET6;
+      if ((req->ai_flags & AI_PASSIVE) == 0)
+	memcpy (res->at->addr, &in6addr_loopback, sizeof (struct in6_addr));
+      atr = res->at->next;
+    }
+
+  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
+    {
+      atr->family = AF_INET;
+      if ((req->ai_flags & AI_PASSIVE) == 0)
+	atr->addr[0] = htonl (INADDR_LOOPBACK);
+    }
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -1012,10 +1038,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   const char *orig_name = name;
 
-  /* Reserve stack memory for the scratch buffer in the getaddrinfo
-     function.  */
-  size_t alloca_used = sizeof (struct scratch_buffer);
-
   int rc;
   if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
     return rc;
@@ -1025,76 +1047,51 @@ gaih_inet (const char *name, const struct gaih_service *service,
   int result = 0;
 
   struct gaih_result res = {0};
-  if (name != NULL)
+  struct gaih_addrtuple local_at[2] = {0};
+
+  res.at = local_at;
+
+  if (__glibc_unlikely (name == NULL))
     {
-      if (req->ai_flags & AI_IDN)
-	{
-	  char *out;
-	  result = __idna_to_dns_encoding (name, &out);
-	  if (result != 0)
-	    return -result;
-	  name = out;
-	  malloc_name = true;
-	}
+      get_local_addresses (req, &res);
+      goto process_list;
+    }
 
-      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      res.at->scopeid = 0;
-      res.at->next = NULL;
+  if (req->ai_flags & AI_IDN)
+    {
+      char *out;
+      result = __idna_to_dns_encoding (name, &out);
+      if (result != 0)
+	return -result;
+      name = out;
+      malloc_name = true;
+    }
 
-      if ((result = text_to_binary_address (name, req, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = text_to_binary_address (name, req, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
-      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
 #ifdef USE_NSCD
-      if ((result = get_nscd_addresses (name, req, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = get_nscd_addresses (name, req, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 #endif
 
-      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
-
-      /* None of the lookups worked, so name not found.  */
-      result = -EAI_NONAME;
-      goto free_and_return;
-    }
-  else
-    {
-      struct gaih_addrtuple *atr;
-      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
-				     alloca_used);
-      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
-
-      if (req->ai_family == AF_UNSPEC)
-	{
-	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
-	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
-	}
-
-      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	{
-	  res.at->family = AF_INET6;
-	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
-	  atr = res.at->next;
-	}
+  if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
-      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	{
-	  atr->family = AF_INET;
-	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    atr->addr[0] = htonl (INADDR_LOOPBACK);
-	}
-    }
+  /* None of the lookups worked, so name not found.  */
+  result = -EAI_NONAME;
+  goto free_and_return;
 
 process_list:
   {
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 11/12] gaih_inet: Split result generation into its own function
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (9 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  5:05     ` DJ Delorie
  2022-03-14  9:48   ` [PATCH v2 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

Simplify the loop a wee bit and clean up variable names too.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 176 ++++++++++++++++++------------------
 1 file changed, 86 insertions(+), 90 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 3aeeb0d1e0..eebed824a8 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1028,6 +1028,87 @@ get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
     }
 }
 
+/* Generate results in PAI and its count in NADDRS.  Return 0 on success or an
+   error code on failure.  */
+
+static int
+generate_addrinfo (const struct addrinfo *req, struct gaih_result *res,
+		   const struct gaih_servtuple *st, struct addrinfo **pai,
+		   unsigned int *naddrs)
+{
+  size_t socklen;
+  sa_family_t family;
+
+  /* Buffer is the size of an unformatted IPv6 address in printable format.  */
+  for (struct gaih_addrtuple *at = res->at; at != NULL; at = at->next)
+    {
+      family = at->family;
+      if (family == AF_INET6)
+	{
+	  socklen = sizeof (struct sockaddr_in6);
+
+	  /* If we looked up IPv4 mapped address discard them here if
+	     the caller isn't interested in all address and we have
+	     found at least one IPv6 address.  */
+	  if (res->got_ipv6
+	      && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
+	      && IN6_IS_ADDR_V4MAPPED (at->addr))
+	    continue;
+	}
+      else
+	socklen = sizeof (struct sockaddr_in);
+
+      for (int i = 0; st[i].set; i++)
+	{
+	  struct addrinfo *ai;
+	  ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
+	  if (ai == NULL)
+	    return -EAI_MEMORY;
+
+	  ai->ai_flags = req->ai_flags;
+	  ai->ai_family = family;
+	  ai->ai_socktype = st[i].socktype;
+	  ai->ai_protocol = st[i].protocol;
+	  ai->ai_addrlen = socklen;
+	  ai->ai_addr = (void *) (ai + 1);
+
+	  /* We only add the canonical name once.  */
+	  ai->ai_canonname = res->canon;
+	  res->canon = NULL;
+
+#ifdef _HAVE_SA_LEN
+	  ai->ai_addr->sa_len = socklen;
+#endif /* _HAVE_SA_LEN */
+	  ai->ai_addr->sa_family = family;
+
+	  /* In case of an allocation error the list must be NULL
+	     terminated.  */
+	  ai->ai_next = NULL;
+
+	  if (family == AF_INET6)
+	    {
+	      struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) ai->ai_addr;
+	      sin6p->sin6_port = st[i].port;
+	      sin6p->sin6_flowinfo = 0;
+	      memcpy (&sin6p->sin6_addr, at->addr, sizeof (struct in6_addr));
+	      sin6p->sin6_scope_id = at->scopeid;
+	    }
+	  else
+	    {
+	      struct sockaddr_in *sinp = (struct sockaddr_in *) ai->ai_addr;
+	      sinp->sin_port = st[i].port;
+	      memcpy (&sinp->sin_addr, at->addr, sizeof (struct in_addr));
+	      memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
+	    }
+
+	  pai = &(ai->ai_next);
+	}
+
+      ++*naddrs;
+    }
+  return 0;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -1094,98 +1175,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
   goto free_and_return;
 
 process_list:
-  {
-    /* Set up the canonical name if we need it.  */
-    if ((result = process_canonname (req, orig_name, &res)) != 0)
-      goto free_and_return;
-
-    struct gaih_addrtuple *at2 = res.at;
-    size_t socklen;
-    sa_family_t family;
-
-    /*
-      buffer is the size of an unformatted IPv6 address in printable format.
-     */
-    while (at2 != NULL)
-      {
-	family = at2->family;
-	if (family == AF_INET6)
-	  {
-	    socklen = sizeof (struct sockaddr_in6);
-
-	    /* If we looked up IPv4 mapped address discard them here if
-	       the caller isn't interested in all address and we have
-	       found at least one IPv6 address.  */
-	    if (res.got_ipv6
-		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
-		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
-	      goto ignore;
-	  }
-	else
-	  socklen = sizeof (struct sockaddr_in);
-
-	for (int i = 0; st[i].set; i++)
-	  {
-	    struct addrinfo *ai;
-	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
-	    if (ai == NULL)
-	      {
-		result = -EAI_MEMORY;
-		goto free_and_return;
-	      }
-
-	    ai->ai_flags = req->ai_flags;
-	    ai->ai_family = family;
-	    ai->ai_socktype = st[i].socktype;
-	    ai->ai_protocol = st[i].protocol;
-	    ai->ai_addrlen = socklen;
-	    ai->ai_addr = (void *) (ai + 1);
-
-	    /* We only add the canonical name once.  */
-	    ai->ai_canonname = res.canon;
-	    res.canon = NULL;
-
-#ifdef _HAVE_SA_LEN
-	    ai->ai_addr->sa_len = socklen;
-#endif /* _HAVE_SA_LEN */
-	    ai->ai_addr->sa_family = family;
-
-	    /* In case of an allocation error the list must be NULL
-	       terminated.  */
-	    ai->ai_next = NULL;
-
-	    if (family == AF_INET6)
-	      {
-		struct sockaddr_in6 *sin6p =
-		  (struct sockaddr_in6 *) ai->ai_addr;
-
-		sin6p->sin6_port = st[i].port;
-		sin6p->sin6_flowinfo = 0;
-		memcpy (&sin6p->sin6_addr,
-			at2->addr, sizeof (struct in6_addr));
-		sin6p->sin6_scope_id = at2->scopeid;
-	      }
-	    else
-	      {
-		struct sockaddr_in *sinp =
-		  (struct sockaddr_in *) ai->ai_addr;
-		sinp->sin_port = st[i].port;
-		memcpy (&sinp->sin_addr,
-			at2->addr, sizeof (struct in_addr));
-		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
-	      }
-
-	    pai = &(ai->ai_next);
-	  }
-
-	++*naddrs;
+  /* Set up the canonical name if we need it.  */
+  if ((result = process_canonname (req, orig_name, &res)) != 0)
+    goto free_and_return;
 
-      ignore:
-	at2 = at2->next;
-      }
-  }
+  result = generate_addrinfo (req, &res, st, pai, naddrs);
 
- free_and_return:
+free_and_return:
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 12/12] gethosts: Return EAI_MEMORY on allocation failure
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                     ` (10 preceding siblings ...)
  2022-03-14  9:48   ` [PATCH v2 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
@ 2022-03-14  9:48   ` Siddhesh Poyarekar
  2022-03-17  5:06     ` DJ Delorie
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14  9:48 UTC (permalink / raw)
  To: libc-alpha

All other cases of failures due to lack of memory return EAI_MEMORY, so
it seems wrong to return EAI_SYSTEM here.  The only reason
convert_hostent_to_gaih_addrtuple could fail is on calloc failure.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index eebed824a8..5cd988bec5 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -303,13 +303,13 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name,
   else if (status == NSS_STATUS_SUCCESS)
     {
       if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
-	return -EAI_SYSTEM;
+	return -EAI_MEMORY;
 
       if (localcanon != NULL && res->canon == NULL)
 	{
 	  char *canonbuf = __strdup (localcanon);
 	  if (canonbuf == NULL)
-	    return  -EAI_SYSTEM;
+	    return  -EAI_MEMORY;
 	  res->canon = canonbuf;
 	}
     }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-14  9:48   ` [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
@ 2022-03-14 10:30     ` Andreas Schwab
  2022-03-14 14:15       ` Siddhesh Poyarekar
  2022-03-16 20:47     ` DJ Delorie
  1 sibling, 1 reply; 68+ messages in thread
From: Andreas Schwab @ 2022-03-14 10:30 UTC (permalink / raw)
  To: Siddhesh Poyarekar via Libc-alpha; +Cc: Siddhesh Poyarekar

On Mär 14 2022, Siddhesh Poyarekar via Libc-alpha wrote:

> +    fprintf (fp, "192.0.%d.%d gnu.org\n", (i / 256) & 0xff, i & 0xff);

> +  int ret = getaddrinfo ("gnu.org", "80", &hints, &ai);

Shouldn't that use example.org?

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 00/12] getaddrinfo facelift and fixes
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (12 preceding siblings ...)
  2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
@ 2022-03-14 13:21 ` Cristian Rodríguez
  2022-03-14 14:16   ` Siddhesh Poyarekar
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
  14 siblings, 1 reply; 68+ messages in thread
From: Cristian Rodríguez @ 2022-03-14 13:21 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha, fweimer

Do you have a remote that I could add to test this changes and make
suggestions..?

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-14 10:30     ` Andreas Schwab
@ 2022-03-14 14:15       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14 14:15 UTC (permalink / raw)
  To: Andreas Schwab, Siddhesh Poyarekar via Libc-alpha

On 14/03/2022 16:00, Andreas Schwab wrote:
> On Mär 14 2022, Siddhesh Poyarekar via Libc-alpha wrote:
> 
>> +    fprintf (fp, "192.0.%d.%d gnu.org\n", (i / 256) & 0xff, i & 0xff);
> 
>> +  int ret = getaddrinfo ("gnu.org", "80", &hints, &ai);
> 
> Shouldn't that use example.org?
> 

Thanks, fixed in my local copy.

Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 00/12] getaddrinfo facelift and fixes
  2022-03-14 13:21 ` [PATCH 00/12] getaddrinfo facelift and fixes Cristian Rodríguez
@ 2022-03-14 14:16   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-14 14:16 UTC (permalink / raw)
  To: Cristian Rodríguez; +Cc: libc-alpha, fweimer

On 14/03/2022 18:51, Cristian Rodríguez wrote:
> Do you have a remote that I could add to test this changes and make
> suggestions..?
> 

I just updated the siddhesh/gai-cleanup2 branch on sourceware.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-14  9:48   ` [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
  2022-03-14 10:30     ` Andreas Schwab
@ 2022-03-16 20:47     ` DJ Delorie
  2022-03-17  1:39       ` Siddhesh Poyarekar
  1 sibling, 1 reply; 68+ messages in thread
From: DJ Delorie @ 2022-03-16 20:47 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


LGTM
Reviewed-by: DJ Delorie <dj@redhat.com>

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> diff --git a/nss/Makefile b/nss/Makefile
> +tests += tst-nss-gai-actions

New test OK.

> diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
> +enum
> +{
> +  ACTION_MERGE = 0,
> +  ACTION_CONTINUE,
> +};
> +
> +struct test_params
> +{
> +  int action;
> +  int family;
> +  bool canon;
> +};
> +
> +struct support_chroot *chroot_env;

Ok.

> +static void
> +prepare (int argc, char **argv)
> +{
> +  chroot_env = support_chroot_create
> +    ((struct support_chroot_configuration)
> +     {
> +       .resolv_conf = "",
> +       .hosts = "",
> +       .host_conf = "multi on\n",
> +     });
> +}

I'm curious why you chose to go this route rather than using the
test-container framework, which would have done most of this for you?
(but ok)

> +/* Create the /etc/hosts file from outside the chroot.  */
> +static void
> +write_hosts (void)
> +{
> +  const int count = 512;
> +
> +  FILE *fp = xfopen (chroot_env->path_hosts, "w");
> +  fputs ("127.0.0.1   localhost localhost.localdomain\n"
> +         "::1         localhost localhost.localdomain\n",
> +         fp);
> +  for (int i = 1; i < count; ++i)
> +    fprintf (fp, "192.0.%d.%d gnu.org\n", (i / 256) & 0xff, i & 0xff);

That's a lot of gnu.org :-)

> +  xfclose (fp);
> +}

Ok.

> +static const char *
> +family_str (int family)
> +{
> +  switch (family)
> +    {
> +    case AF_UNSPEC:
> +      return "AF_UNSPEC";
> +    case AF_INET:
> +      return "AF_INET";
> +    default:
> +      __builtin_unreachable ();
> +    }
> +}

Ok.

> +static const char *
> +action_str (int action)
> +{
> +  switch (action)
> +    {
> +    case ACTION_MERGE:
> +      return "merge";
> +    case ACTION_CONTINUE:
> +      return "continue";
> +    default:
> +      __builtin_unreachable ();
> +    }
> +}

Ok.

> +/* getaddrinfo test.  To be run from a subprocess.  */
> +static void
> +test_gai (void *closure)
> +{
> +  struct test_params *params = closure;
> +
> +  struct addrinfo hints =
> +    {
> +      .ai_family = params->family,
> +    };
> +
> +  struct addrinfo *ai;
> +
> +  if (params->canon)
> +    hints.ai_flags = AI_CANONNAME;
> +
> +  /* Use /etc/hosts in the chroot.  */
> +  xchroot (chroot_env->path_chroot);
> +
> +  printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n",
> +	  action_str (params->action), family_str (params->family),
> +	  params->canon ? "AI_CANONNAME" : "");
> +
> +  int ret = getaddrinfo ("gnu.org", "80", &hints, &ai);
> +
> +  switch (params->action)
> +    {
> +    case ACTION_MERGE:
> +      if (ret == 0)
> +	{
> +	  char *formatted = support_format_addrinfo (ai, ret);
> +
> +	  printf ("merge unexpectedly succeeded:\n %s\n", formatted);
> +	  support_record_failure ();
> +	  free (formatted);
> +	}
> +      else
> +	return;
> +    case ACTION_CONTINUE:
> +	{
> +	  char *formatted = support_format_addrinfo (ai, ret);
> +
> +	  /* Verify that the result appears exactly once.  */
> +	  const char *expected = "address: STREAM/TCP 192.0.0.1 80\n"
> +	    "address: DGRAM/UDP 192.0.0.1 80\n"
> +	    "address: RAW/IP 192.0.0.1 80\n";
> +
> +	  const char *contains = strstr (formatted, expected);
> +	  const char *contains2 = NULL;
> +
> +	  if (contains != NULL)
> +	    contains2 = strstr (contains + strlen (expected), expected);
> +
> +	  if (contains == NULL || contains2 != NULL)
> +	    {
> +	      printf ("continue failed:\n%s\n", formatted);
> +	      support_record_failure ();
> +	    }
> +
> +	  free (formatted);
> +	  break;
> +	}
> +    default:
> +      __builtin_unreachable ();
> +    }
> +}

Ok.

> +static void
> +test_in_subprocess (int action)
> +{
> +  char buf[32];
> +
> +  snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files",
> +	    action_str (action));
> +  __nss_configure_lookup ("hosts", buf);
> +
> +  struct test_params params =
> +    {
> +      .action = action,
> +      .family = AF_UNSPEC,
> +      .canon = false,
> +    };
> +  support_isolate_in_subprocess (test_gai, &params);
> +  params.family = AF_INET;
> +  support_isolate_in_subprocess (test_gai, &params);
> +  params.canon = true;
> +  support_isolate_in_subprocess (test_gai, &params);
> +}

Ok.

> +static int
> +do_test (void)
> +{
> +  support_become_root ();
> +  if (!support_can_chroot ())
> +    FAIL_UNSUPPORTED ("Cannot chroot\n");
> +
> +  write_hosts ();
> +  test_in_subprocess (ACTION_CONTINUE);
> +  test_in_subprocess (ACTION_MERGE);
> +
> +  support_chroot_free (chroot_env);
> +  return 0;
> +}

Ok.

> +#define PREPARE prepare
> +#include <support/test-driver.c>

Ok.

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> index 18dccd5924..454a27eb2f 100644
> --- a/sysdeps/posix/getaddrinfo.c
> +++ b/sysdeps/posix/getaddrinfo.c
> @@ -458,11 +458,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  
>    if (name != NULL)
>      {
> -      at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
> -      at->family = AF_UNSPEC;
> -      at->scopeid = 0;
> -      at->next = NULL;
> -

Moved below, ok.

> -      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
> +      uint32_t addr[4];
> +      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)

Use temp buffer because not allocated, ok.

> +	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
> +	  at->scopeid = 0;
> +	  at->next = NULL;
> +

Ok.

>  	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
> -	    at->family = AF_INET;
> +	    {
> +	      memcpy (at->addr, addr, sizeof (at->addr));
> +	      at->family = AF_INET;
> +	    }

Ok.

>  	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
>  	    {
> -	      at->addr[3] = at->addr[0];
> +	      at->addr[3] = addr[0];

Ok.

> @@ -493,49 +496,62 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  
>  	  if (req->ai_flags & AI_CANONNAME)
>  	    canon = name;
> +
> +	  goto process_list;

ok.

>  	}
> -      else if (at->family == AF_UNSPEC)
> +
> +      char *scope_delim = strchr (name, SCOPE_DELIMITER);
> +      int e;
> +
> +      if (scope_delim == NULL)
> +	e = inet_pton (AF_INET6, name, addr);
> +      else
> +	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);

Moved out of scope, ok.

> +
> +      if (e > 0)

Moved up, ok.

>  	{
> -	  char *scope_delim = strchr (name, SCOPE_DELIMITER);
> -	  int e;
> -	  if (scope_delim == NULL)
> -	    e = inet_pton (AF_INET6, name, at->addr);

Move up, ok.

> +	  at = alloca_account (sizeof (struct gaih_addrtuple),
> +			       alloca_used);
> +	  at->scopeid = 0;
> +	  at->next = NULL;
> +
> +	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
> +	    {
> +	      memcpy (at->addr, addr, sizeof (at->addr));
> +	      at->family = AF_INET6;
> +	    }
> +	  else if (req->ai_family == AF_INET
> +		   && IN6_IS_ADDR_V4MAPPED (addr))
> +	    {
> +	      at->addr[0] = addr[3];
> +	      at->addr[1] = addr[1];
> +	      at->addr[2] = addr[2];
> +	      at->addr[3] = addr[3];
> +	      at->family = AF_INET;
> +	    }

Moved, ok.

>  	  else
> -	    e = __inet_pton_length (AF_INET6, name, scope_delim - name,
> -				    at->addr);
> -	  if (e > 0)
>  	    {
> -	      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
> -		at->family = AF_INET6;
> -	      else if (req->ai_family == AF_INET
> -		       && IN6_IS_ADDR_V4MAPPED (at->addr))
> -		{
> -		  at->addr[0] = at->addr[3];
> -		  at->family = AF_INET;
> -		}
> -	      else
> -		{
> -		  result = -EAI_ADDRFAMILY;
> -		  goto free_and_return;
> -		}
> -
> -	      if (scope_delim != NULL
> -		  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
> -					   scope_delim + 1,
> -					   &at->scopeid) != 0)
> -		{
> -		  result = -EAI_NONAME;
> -		  goto free_and_return;
> -		}

Moved, ok.

> +	      result = -EAI_ADDRFAMILY;
> +	      goto free_and_return;
> +	    }

Moved, ok.

> -	      if (req->ai_flags & AI_CANONNAME)
> -		canon = name;
> +	  if (scope_delim != NULL
> +	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
> +				       scope_delim + 1,
> +				       &at->scopeid) != 0)
> +	    {
> +	      result = -EAI_NONAME;
> +	      goto free_and_return;

Moved, ok.
>  	    }
> +
> +	  if (req->ai_flags & AI_CANONNAME)
> +	    canon = name;
> +
> +	  goto process_list;
>  	}

Ok.

> -      if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
> +      if ((req->ai_flags & AI_NUMERICHOST) == 0)

Ok.

>  	{
> -	  struct gaih_addrtuple **pat = &at;

Ok.
> +	  bool do_merge = false;

Ok.

> @@ -579,7 +596,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  			  result = -EAI_MEMORY;
>  			  goto free_and_return;
>  			}
> -		      *pat = addrmem;
> +		      at = addrmem;

Ok.

>  		  struct gaih_addrtuple *addrfree = addrmem;
> +		  struct gaih_addrtuple **pat = &at;
> +

Ok.

>  		  free (air);
>  
> -		  if (at->family == AF_UNSPEC)
> -		    {
> -		      result = -EAI_NONAME;
> -		      goto free_and_return;
> -		    }
> -

Ok.

> @@ -732,6 +745,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  
>  	  while (!no_more)
>  	    {
> +	      /* Always start afresh; continue should discard previous results
> +		 and the hosts database does not support merge.  */
> +	      at = NULL;
> +	      free (canonbuf);
> +	      free (addrmem);
> +	      canon = canonbuf = NULL;
> +	      addrmem = NULL;
> +
> +	      if (do_merge)
> +		{
> +		  __set_h_errno (NETDB_INTERNAL);
> +		  __set_errno (EBUSY);
> +		  break;
> +		}
> +

Ok.

> -		      status = DL_CALL_FCT (fct4, (name, pat,
> +		      status = DL_CALL_FCT (fct4, (name, &at,

Ok.

> +		      /* gethostbyname4_r may write into AT, so reset it.  */
> +		      at = NULL;

This is alloca-ted, so no leak here.  ok.

> @@ -774,7 +804,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  		      no_data = 1;
>  
>  		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
> -			canon = (*pat)->name;
> +			canon = at->name;
> +
> +		      struct gaih_addrtuple **pat = &at;

Ok.

>  		      while (*pat != NULL)
>  			{
> @@ -826,6 +858,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  
>  		  if (fct != NULL)
>  		    {
> +		      struct gaih_addrtuple **pat = &at;
> +

Ok.

> +	      /* The hosts database does not support MERGE.  */
> +	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
> +		do_merge = true;
> +

Ok.

>      process_list:
> -      if (at->family == AF_UNSPEC)
> +      if (at == NULL)

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 02/12] gaih_inet: Simplify canon name resolution
  2022-03-14  9:48   ` [PATCH v2 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
@ 2022-03-16 21:12     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-16 21:12 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> Simplify logic for allocation of canon to remove the canonbuf variable;
> canon now always points to an allocated block.  Also pull the canon name
> set into a separate function.

LGTM.

Reviewed-by: DJ Delorie <dj@redhat.com>

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> -	  canonbuf = __strdup (localcanon);				      \
> +	  char *canonbuf = __strdup (localcanon);			      \

Ok.

> @@ -323,6 +323,41 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
> +/* Process looked up canonical name and if necessary, decode to IDNA.  Result
> +   is a new string written to CANONP and the earlier string is freed.  */
> +
> +static int
> +process_canonname (const struct addrinfo *req, const char *orig_name,
> +		   char **canonp)
> +{
> +  char *canon = *canonp;
> +
> +  if ((req->ai_flags & AI_CANONNAME) != 0)
> +    {
> +      bool do_idn = req->ai_flags & AI_CANONIDN;
> +      if (do_idn)
> +	{
> +	  char *out;
> +	  int rc = __idna_from_dns_encoding (canon ?: orig_name, &out);
> +	  if (rc == 0)
> +	    {
> +	      free (canon);
> +	      canon = out;
> +	    }
> +	  else if (rc == EAI_IDN_ENCODE)
> +	    /* Use the punycode name as a fallback.  */
> +	    do_idn = false;
> +	  else
> +	    return -rc;
> +	}
> +      if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL)
> +	return -EAI_MEMORY;
> +    }
> +
> +  *canonp = canon;
> +  return 0;
> +}

Moved from below, ok.

> @@ -332,7 +367,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
>    struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
>    struct gaih_addrtuple *at = NULL;
>    bool got_ipv6 = false;
> -  const char *canon = NULL;
> +  char *canon = NULL;

Ok.

> -  char *canonbuf = NULL;

Ok.


> @@ -495,7 +529,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  	    }
>  
>  	  if (req->ai_flags & AI_CANONNAME)
> -	    canon = name;
> +	    {
> +	      char *canonbuf = __strdup (name);
> +	      if (canonbuf == NULL)
> +		{
> +		  result = -EAI_MEMORY;
> +		  goto free_and_return;
> +		}
> +	      canon = canonbuf;
> +	    }

Ok.

> @@ -545,7 +587,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  	    }
>  
>  	  if (req->ai_flags & AI_CANONNAME)
> -	    canon = name;
> +	    {
> +	      char *canonbuf = __strdup (name);
> +	      if (canonbuf == NULL)
> +		{
> +		  result = -EAI_MEMORY;
> +		  goto free_and_return;
> +		}
> +	      canon = canonbuf;
> +	    }

Ok.

> @@ -676,9 +726,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  		      (*pat)->next = NULL;
>  		      if (added_canon || air->canon == NULL)
>  			(*pat)->name = NULL;
> -		      else if (canonbuf == NULL)
> +		      else if (canon == NULL)
>  			{
> -			  canonbuf = __strdup (air->canon);
> +			  char *canonbuf = __strdup (air->canon);

Ok.

> @@ -748,9 +798,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  	      /* Always start afresh; continue should discard previous results
>  		 and the hosts database does not support merge.  */
>  	      at = NULL;
> -	      free (canonbuf);
> +	      free (canon);
>  	      free (addrmem);
> -	      canon = canonbuf = NULL;
> +	      canon = NULL;
>  	      addrmem = NULL;

Ok.

> @@ -804,7 +854,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  		      no_data = 1;
>  
>  		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
> -			canon = at->name;
> +			{
> +			  char *canonbuf = __strdup (at->name);
> +			  if (canonbuf == NULL)
> +			    {
> +			      __resolv_context_put (res_ctx);
> +			      result = -EAI_MEMORY;
> +			      goto free_and_return;
> +			    }
> +			  canon = canonbuf;
> +			}

Ok.

> @@ -892,7 +951,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  			  if ((req->ai_flags & AI_CANONNAME) != 0
>  			      && canon == NULL)
>  			    {
> -			      canonbuf = getcanonname (nip, at, name);
> +			      char *canonbuf = getcanonname (nip, at, name);

Ok.

> @@ -1003,6 +1062,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
>      }
>  
>    {
> +    /* Set up the canonical name if we need it.  */
> +    if ((result = process_canonname (req, orig_name, &canon)) != 0)
> +      goto free_and_return;
> +

Ok.

> @@ -1013,48 +1076,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
>       */
>      while (at2 != NULL)
>        {
> -	/* Only the first entry gets the canonical name.  */
> -	if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
> -	  {
> -	    if (canon == NULL)
> -	      /* If the canonical name cannot be determined, use
> -		 the passed in string.  */
> -	      canon = orig_name;
> -
> -	    bool do_idn = req->ai_flags & AI_CANONIDN;
> -	    if (do_idn)
> -	      {
> -		char *out;
> -		int rc = __idna_from_dns_encoding (canon, &out);
> -		if (rc == 0)
> -		  canon = out;
> -		else if (rc == EAI_IDN_ENCODE)
> -		  /* Use the punycode name as a fallback.  */
> -		  do_idn = false;
> -		else
> -		  {
> -		    result = -rc;
> -		    goto free_and_return;
> -		  }
> -	      }
> -	    if (!do_idn)
> -	      {
> -		if (canonbuf != NULL)
> -		  /* We already allocated the string using malloc, but
> -		     the buffer is now owned by canon.  */
> -		  canonbuf = NULL;
> -		else
> -		  {
> -		    canon = __strdup (canon);
> -		    if (canon == NULL)
> -		      {
> -			result = -EAI_MEMORY;
> -			goto free_and_return;
> -		      }
> -		  }
> -	      }
> -	  }
> -

Moved above; ok.

> @@ -1077,7 +1098,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
>  	    if (ai == NULL)
>  	      {
> -		free ((char *) canon);

Ok.
> @@ -1137,7 +1157,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
>    if (malloc_name)
>      free ((char *) name);
>    free (addrmem);
> -  free (canonbuf);
> +  free (canon);

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-14  9:48   ` [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
@ 2022-03-16 23:42     ` DJ Delorie
  2022-03-17  2:30       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 68+ messages in thread
From: DJ Delorie @ 2022-03-16 23:42 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> Use realloc in convert_hostent_to_gaih_addrtuple and fix up pointers in
> the result list so that a single block is maintained for
> hostbyname3_r/hostbyname2_r and freed in gaih_inet.  This result is
> never merged with any other results, since the hosts database does not
> permit merging.

It took me a while to realize that you're basically converting the data
structure from a linked list to an array, and ensuring that it's always
handled as an array.  That means the comment preceeding
convert_hostent_to_gaih_addrtuple() is no longer accurate and needs
updating.  Ideally, the users could be optimized to treat it as an array
instead of a list, but it may need to remain a list-like type for
compatibility.

LGTM with that comment change.

Reviewed-by: DJ Delorie <dj@redhat.com>

> -  while (*result)
> -    result = &(*result)->next;
> -

Don't skip to end of "list".  Ok.

> @@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
>    if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
>      return true;
>  
> -  struct gaih_addrtuple *array = calloc (count, sizeof (*array));
> +  struct gaih_addrtuple *array = *result;
> +  size_t old = 0;
> +
> +  while (array != NULL)
> +    {
> +      old++;
> +      array = array->next;
> +    }
> +
> +  array = realloc (*result, (old + count) * sizeof (*array));
> +

Count existing members of *array* and resize base array.  Ok.  The rest
are initialized later, in the unchanged part of the code.

>    if (array == NULL)
>      return false;
>  
> +  *result = array;
> +
> +  /* Update the next pointers on reallocation.  */
> +  for (size_t i = 0; i < old; i++)
> +    array[i].next = array + i + 1;
> +
> +  array += old;
> +
> +  memset (array, 0, count * sizeof (*array));
> +

Ok.

>    for (size_t i = 0; i < count; ++i)
>      {
>        if (family == AF_INET && req->ai_family == AF_INET6)
> @@ -235,7 +252,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
>    array[0].name = h->h_name;
>    array[count - 1].next = NULL;
>  
> -  *result = array;
>    return true;
>  }

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 04/12] gaih_inet: Simplify service resolution
  2022-03-14  9:48   ` [PATCH v2 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
@ 2022-03-17  0:48     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  0:48 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> Refactor the code to split out the service resolution code into a
> separate function.  Allocate the service tuples array just once to the
> size of the typeproto array, thus avoiding the unnecessary pointer
> chasing and stack allocations.

LGTM

Reviewed-by: DJ Delorie <dj@redhat.com>

>  struct gaih_servtuple
>    {
> -    struct gaih_servtuple *next;
>      int socktype;
>      int protocol;
>      int port;
> +    bool set;
>    };
>  
> -static const struct gaih_servtuple nullserv;
> -

Ok.

> @@ -180,11 +178,11 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
>      }
>    while (r);
>  
> -  st->next = NULL;
>    st->socktype = tp->socktype;
>    st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
>  		  ? req->ai_protocol : tp->protocol);
>    st->port = s->s_port;
> +  st->set = true;

Ok.

> @@ -375,20 +373,11 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
>  }
>  
>  static int
> -gaih_inet (const char *name, const struct gaih_service *service,
> -	   const struct addrinfo *req, struct addrinfo **pai,
> -	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
> +get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
> +		struct gaih_servtuple *st, struct scratch_buffer *tmpbuf)
>  {

Split out but the names of passed variables remain the same; ok.

> +  int i;
>    const struct gaih_typeproto *tp = gaih_inet_typeproto;
> -  struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
> -  struct gaih_addrtuple *at = NULL;
> -  bool got_ipv6 = false;
> -  char *canon = NULL;
> -  const char *orig_name = name;
> -
> -  /* Reserve stack memory for the scratch buffer in the getaddrinfo
> -     function.  */
> -  size_t alloca_used = sizeof (struct scratch_buffer);

This part done in the parent function; ok.

> @@ -410,98 +399,88 @@ gaih_inet (const char *name, const struct gaih_service *service,
>  	}
>      }
>  
> -  int port = 0;
> -  if (service != NULL)
> +  if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
> +    return -EAI_SERVICE;
> +
> +  if (service == NULL || service->num >= 0)
>      {
> -      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
> -	return -EAI_SERVICE;
> +      int port = service != NULL ? htons (service->num) : 0;

Ok.

> +      if (req->ai_socktype || req->ai_protocol)
>  	{
> +	  st[0].socktype = tp->socktype;
> +	  st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
> +			  ? req->ai_protocol : tp->protocol);
> +	  st[0].port = port;
> +	  st[0].set = true;
> +	  return 0;
> +	}

ok.


> +      /* Neither socket type nor protocol is set.  Return all socket types
> +	 we know about.  */
> +      for (i = 0, ++tp; tp->name[0]; ++tp)
> +	if (tp->defaultflag)
> +	  {
> +	    st[i].socktype = tp->socktype;
> +	    st[i].protocol = tp->protocol;
> +	    st[i].port = port;
> +	    st[i++].set = true;
> +	  }

Ok.

> -      if (service->num < 0)
> -	  if (tp->name[0])
> -	    {
> -	      st = (struct gaih_servtuple *)
> -		alloca_account (sizeof (struct gaih_servtuple), alloca_used);
> -
> -	      int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf);
> -	      if (__glibc_unlikely (rc != 0))
> -		return rc;
> -	    }
> -	  else
> -	    {
> -	      struct gaih_servtuple **pst = &st;
> -	      for (tp++; tp->name[0]; tp++)
> -		{
> -		  struct gaih_servtuple *newp;
>  
> -		  if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
> -		    continue;
>  
> -		  if (req->ai_socktype != 0
> -		      && req->ai_socktype != tp->socktype)
> -		    continue;
> -		  if (req->ai_protocol != 0
> -		      && !(tp->protoflag & GAI_PROTO_PROTOANY)
> -		      && req->ai_protocol != tp->protocol)
> -		    continue;
>  
> -		  newp = (struct gaih_servtuple *)
> -		    alloca_account (sizeof (struct gaih_servtuple),
> -				    alloca_used);
> +      return 0;
> +    }

Ok.

> -		  if (gaih_inet_serv (service->name,
> -				      tp, req, newp, tmpbuf) != 0)
> -		    continue;

Ok.

> +  if (tp->name[0])
> +    return gaih_inet_serv (service->name, tp, req, st, tmpbuf);

Ok.

> +  for (i = 0, tp++; tp->name[0]; tp++)
> +      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
> +	continue;
> +      if (req->ai_socktype != 0
> +	  && req->ai_socktype != tp->socktype)
> +	continue;
> +      if (req->ai_protocol != 0
> +	  && !(tp->protoflag & GAI_PROTO_PROTOANY)
> +	  && req->ai_protocol != tp->protocol)
> +	continue;
> +      if (gaih_inet_serv (service->name,
> +			  tp, req, &st[i], tmpbuf) != 0)
> +	continue;
> +      i++;

> +  if (!st[0].set)
> +    return -EAI_SERVICE;
> +
> +  return 0;
> +}

Ok.

> -		  *pst = newp;
> -		  pst = &(newp->next);
> -		}
> -	      if (st == (struct gaih_servtuple *) &nullserv)
> -		return -EAI_SERVICE;
> -	    }
> -	}
> -      else
> -	{
> -	  port = htons (service->num);
> -	  goto got_port;
> -	}
> -    }
> -  else
>      {
> -    got_port:
>  
> -      if (req->ai_socktype || req->ai_protocol)
> -	{
> -	  st = alloca_account (sizeof (struct gaih_servtuple), alloca_used);
> -	  st->next = NULL;
> -	  st->socktype = tp->socktype;
> -	  st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
> -			  ? req->ai_protocol : tp->protocol);
> -	  st->port = port;
> -	}
> -      else
> -	{
> -	  /* Neither socket type nor protocol is set.  Return all socket types
> -	     we know about.  */
> -	  struct gaih_servtuple **lastp = &st;
> -	  for (++tp; tp->name[0]; ++tp)
> -	    if (tp->defaultflag)
> -	      {
> -		struct gaih_servtuple *newp;
>  
> -		newp = alloca_account (sizeof (struct gaih_servtuple),
> -				       alloca_used);
> -		newp->next = NULL;
> -		newp->socktype = tp->socktype;
> -		newp->protocol = tp->protocol;
> -		newp->port = port;
>  
> -		*lastp = newp;
> -		lastp = &newp->next;
> -	      }
> -	}
>      }

Ok.

> +
> +static int
> +gaih_inet (const char *name, const struct gaih_service *service,
> +	   const struct addrinfo *req, struct addrinfo **pai,
> +	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
> +{
> +  struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
> +			   / sizeof (struct gaih_typeproto)] = {0};
> +
> +  struct gaih_addrtuple *at = NULL;
> +  bool got_ipv6 = false;
> +  char *canon = NULL;
> +  const char *orig_name = name;
> +
> +  /* Reserve stack memory for the scratch buffer in the getaddrinfo
> +     function.  */
> +  size_t alloca_used = sizeof (struct scratch_buffer);
> +
> +  int rc;
> +  if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
> +    return rc;
> +

Ok.

> -    struct gaih_servtuple *st2;

Ok.

> -	for (st2 = st; st2 != NULL; st2 = st2->next)
> +	for (int i = 0; st[i].set; i++)

Ok.
> -	    ai->ai_socktype = st2->socktype;
> -	    ai->ai_protocol = st2->protocol;
> +	    ai->ai_socktype = st[i].socktype;
> +	    ai->ai_protocol = st[i].protocol;

Ok.

> -		sin6p->sin6_port = st2->port;
> +		sin6p->sin6_port = st[i].port;

Ok.

> -		sinp->sin_port = st2->port;
> +		sinp->sin_port = st[i].port;

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-16 20:47     ` DJ Delorie
@ 2022-03-17  1:39       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  1:39 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

On 17/03/2022 02:17, DJ Delorie wrote:
>> +static void
>> +prepare (int argc, char **argv)
>> +{
>> +  chroot_env = support_chroot_create
>> +    ((struct support_chroot_configuration)
>> +     {
>> +       .resolv_conf = "",
>> +       .hosts = "",
>> +       .host_conf = "multi on\n",
>> +     });
>> +}
> 
> I'm curious why you chose to go this route rather than using the
> test-container framework, which would have done most of this for you?
> (but ok)

I had initially written this test with dns *and* files modules and the 
former required me to stay out of the container.  Now that the dns 
module bit is gone, I could put it back into a container.

I'll respin this patch because...

> 
>> +/* Create the /etc/hosts file from outside the chroot.  */
>> +static void
>> +write_hosts (void)
>> +{
>> +  const int count = 512;
>> +
>> +  FILE *fp = xfopen (chroot_env->path_hosts, "w");
>> +  fputs ("127.0.0.1   localhost localhost.localdomain\n"
>> +         "::1         localhost localhost.localdomain\n",
>> +         fp);
>> +  for (int i = 1; i < count; ++i)
>> +    fprintf (fp, "192.0.%d.%d gnu.org\n", (i / 256) & 0xff, i & 0xff);
> 
> That's a lot of gnu.org :-)

... Andreas suggested that I replace this with example.org.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-16 23:42     ` DJ Delorie
@ 2022-03-17  2:30       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  2:30 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

On 17/03/2022 05:12, DJ Delorie via Libc-alpha wrote:
> Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
>> Use realloc in convert_hostent_to_gaih_addrtuple and fix up pointers in
>> the result list so that a single block is maintained for
>> hostbyname3_r/hostbyname2_r and freed in gaih_inet.  This result is
>> never merged with any other results, since the hosts database does not
>> permit merging.
> 
> It took me a while to realize that you're basically converting the data
> structure from a linked list to an array, and ensuring that it's always
> handled as an array.  That means the comment preceeding
> convert_hostent_to_gaih_addrtuple() is no longer accurate and needs
> updating.  Ideally, the users could be optimized to treat it as an array
> instead of a list, but it may need to remain a list-like type for
> compatibility.

Thanks, I'll update the comment.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 05/12] gaih_inet: make numeric lookup a separate routine
  2022-03-14  9:48   ` [PATCH v2 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
@ 2022-03-17  4:10     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  4:10 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> . . .

git's default patch method results in too messy a patch to review in
this case, so I applied the patch and re-diffed it with alternate
options (diff -EZdbwpU5) and I'll review *that*.

LGTM.

Reviewed-by: DJ Delorie <dj@redhat.com>

> @@ -114,10 +114,16 @@ struct gaih_typeproto
> +struct gaih_result
> +{
> +  struct gaih_addrtuple *at;
> +  char *canon;
> +};

Ok.

> @@ -295,20 +301,20 @@ convert_hostent_to_gaih_addrtuple (const
> -      if (localcanon != NULL && canon == NULL)				      \
> +      if (localcanon != NULL && res.canon == NULL)			      \

Ok.

> -	  canon = canonbuf;						      \
> +	  res.canon = canonbuf;						      \

Ok.

>  static int
>  process_canonname (const struct addrinfo *req, const char *orig_name,
> -		   char **canonp)
> +		   struct gaih_result *res)
>  {
> -  char *canon = *canonp;
> +  char *canon = res->canon;

Ok.

>  
> -  *canonp = canon;
> +  res->canon = canon;

Ok.

> +/* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
> +   NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
> +   the function cannot determine a result, RES->AT is set to NULL and 0
> +   returned.  */
> +
>  static int
> -gaih_inet (const char *name, const struct gaih_service *service,
> -	   const struct addrinfo *req, struct addrinfo **pai,
> -	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
> +text_to_binary_address (const char *name, const struct addrinfo *req,
> +			struct gaih_result *res)

Ok.

>  {
> -  struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
> -			   / sizeof (struct gaih_typeproto)] = {0};
> -
> -  struct gaih_addrtuple *at = NULL;
> -  bool got_ipv6 = false;
> -  char *canon = NULL;
> -  const char *orig_name = name;
> -
> -  /* Reserve stack memory for the scratch buffer in the getaddrinfo
> -     function.  */
> -  size_t alloca_used = sizeof (struct scratch_buffer);
> -
> -  int rc;
> -  if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
> -    return rc;
> -
> -  bool malloc_name = false;
> -  struct gaih_addrtuple *addrmem = NULL;
>    int result = 0;
>  
> -  if (name != NULL)
> -    {
> -      if (req->ai_flags & AI_IDN)
> -	{
> -	  char *out;
> -	  result = __idna_to_dns_encoding (name, &out);
> -	  if (result != 0)
> -	    return -result;
> -	  name = out;
> -	  malloc_name = true;
> -	}

"Moved", ok.

> +  struct gaih_addrtuple *at = res->at;
> +  assert (at != NULL);

Ok.

> -      uint32_t addr[4];
> -      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
> +  memset (at->addr, 0, sizeof (at->addr));
> +  if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)

Ok.

>  	{
> -	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
> -	  at->scopeid = 0;
> -	  at->next = NULL;
> -

at is pre-filled, ok.

>  	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
> -	    {
> -	      memcpy (at->addr, addr, sizeof (at->addr));
>  	      at->family = AF_INET;
> -	    }

Ok.

>  	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
>  	    {
> -	      at->addr[3] = addr[0];
> +	  at->addr[3] = at->addr[0];
>  	      at->addr[2] = htonl (0xffff);
>  	      at->addr[1] = 0;
>  	      at->addr[0] = 0;

Ok.

> -	      goto free_and_return;
> +	  goto out;

Ok.

> -		  goto free_and_return;
> +	      goto out;
>  		}
> -	      canon = canonbuf;
> +	  res->canon = canonbuf;
>  	    }
> -
> -	  goto process_list;
> +      return 0;
>  	}

Ok.

>        char *scope_delim = strchr (name, SCOPE_DELIMITER);
>        int e;
>  
>        if (scope_delim == NULL)
> -	e = inet_pton (AF_INET6, name, addr);
> +    e = inet_pton (AF_INET6, name, at->addr);
>        else
> -	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
> +    e = __inet_pton_length (AF_INET6, name, scope_delim - name, at->addr);

Ok.

>        if (e > 0)
>  	{
> -	  at = alloca_account (sizeof (struct gaih_addrtuple),
> -			       alloca_used);
> -	  at->scopeid = 0;
> -	  at->next = NULL;
> -

Ok.

>  	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
> -	    {
> -	      memcpy (at->addr, addr, sizeof (at->addr));
>  	      at->family = AF_INET6;
> -	    }

Ok.

>  	  else if (req->ai_family == AF_INET
> -		   && IN6_IS_ADDR_V4MAPPED (addr))
> +	       && IN6_IS_ADDR_V4MAPPED (at->addr))
>  	    {
> -	      at->addr[0] = addr[3];
> -	      at->addr[1] = addr[1];
> -	      at->addr[2] = addr[2];
> -	      at->addr[3] = addr[3];
> +	  at->addr[0] = at->addr[3];
>  	      at->family = AF_INET;

Ok.

>  	      result = -EAI_ADDRFAMILY;
> -	      goto free_and_return;
> +	  goto out;

Ok.

>  	  if (scope_delim != NULL
>  	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
> -				       scope_delim + 1,
> -				       &at->scopeid) != 0)
> +				   scope_delim + 1, &at->scopeid) != 0)

Ok.

>  	    {
>  	      result = -EAI_NONAME;
> -	      goto free_and_return;
> +	  goto out;
>  	    }

Ok.

>  		  result = -EAI_MEMORY;
> -		  goto free_and_return;
> +	      goto out;

Ok.

> -	      canon = canonbuf;
> +	  res->canon = canonbuf;
> +	}
> +      return 0;

Ok.

> -	  goto process_list;
> +  if ((req->ai_flags & AI_NUMERICHOST))
> +    result = -EAI_NONAME;
> +
> +out:
> +  res->at = NULL;
> +  return result;
>  	}

Ok.

> -      if ((req->ai_flags & AI_NUMERICHOST) == 0)

Ok.

> +static int
> +gaih_inet (const char *name, const struct gaih_service *service,
> +	   const struct addrinfo *req, struct addrinfo **pai,
> +	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
> +{
> +  struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
> +			   / sizeof (struct gaih_typeproto)] = {0};
> +
> +  bool got_ipv6 = false;
> +  const char *orig_name = name;
> +
> +  /* Reserve stack memory for the scratch buffer in the getaddrinfo
> +     function.  */
> +  size_t alloca_used = sizeof (struct scratch_buffer);
> +
> +  int rc;
> +  if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
> +    return rc;
> +
> +  bool malloc_name = false;
> +  struct gaih_addrtuple *addrmem = NULL;
> +  int result = 0;
> +
> +  struct gaih_result res = {0};
> +  if (name != NULL)
>  	{
> +      if (req->ai_flags & AI_IDN)
> +	{
> +	  char *out;
> +	  result = __idna_to_dns_encoding (name, &out);
> +	  if (result != 0)
> +	    return -result;
> +	  name = out;
> +	  malloc_name = true;
> +	}
> +
> +      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
> +      res.at->scopeid = 0;
> +      res.at->next = NULL;
> +
> +      if ((result = text_to_binary_address (name, req, &res)) != 0)
> +	goto free_and_return;
> +      else if (res.at != NULL)
> +	goto process_list;
> +

"Moved", ok.

> -		      at = addrmem;
> +		  res.at = addrmem;

Ok.

> -		  struct gaih_addrtuple **pat = &at;
> +	      struct gaih_addrtuple **pat = &res.at;

Ok.

> -		      else if (canon == NULL)
> +		  else if (res.canon == NULL)

Ok.

> -			  canon = (*pat)->name = canonbuf;
> +		      res.canon = (*pat)->name = canonbuf;

Ok.

>  	      /* Always start afresh; continue should discard previous results
>  		 and the hosts database does not support merge.  */
> -	      at = NULL;
> -	      free (canon);
> +	  res.at = NULL;
> +	  free (res.canon);
>  	      free (addrmem);
> -	      canon = NULL;
> +	  res.canon = NULL;
>  	      addrmem = NULL;

Ok.

> -		      status = DL_CALL_FCT (fct4, (name, &at,
> +		  status = DL_CALL_FCT (fct4, (name, &res.at,

Ok.

> -		      at = NULL;
> +		  res.at = NULL;

Ok.

> -		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
> +		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
>  			{
> -			  char *canonbuf = __strdup (at->name);
> +		      char *canonbuf = __strdup (res.at->name);

Ok.

> -			  canon = canonbuf;
> +		      res.canon = canonbuf;
>  			}
>  
> -		      struct gaih_addrtuple **pat = &at;
> +		  struct gaih_addrtuple **pat = &res.at;

Ok.

> -		      struct gaih_addrtuple **pat = &at;
> +		  struct gaih_addrtuple **pat = &res.at;

Ok.

>  			  if ((req->ai_flags & AI_CANONNAME) != 0
> -			      && canon == NULL)
> +			  && res.canon == NULL)

Ok.

> -			      char *canonbuf = getcanonname (nip, at, name);
> +			  char *canonbuf = getcanonname (nip, res.at, name);

Ok.

> -			      canon = canonbuf;
> +			  res.canon = canonbuf;

Ok.

> -	}

Ok, matches "if (name != NULL) {"

>      process_list:
> -      if (at == NULL)
> +      if (res.at == NULL)

Ok.

>        struct gaih_addrtuple *atr;
> -      atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
> +      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
> +				     alloca_used);
> -      memset (at, '\0', sizeof (struct gaih_addrtuple));
> +      memset (res.at, '\0', sizeof (struct gaih_addrtuple));

Ok.

>        if (req->ai_family == AF_UNSPEC)
>  	{
> -	  at->next = __alloca (sizeof (struct gaih_addrtuple));
> +	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
> -	  memset (at->next, '\0', sizeof (struct gaih_addrtuple));
> +	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
>  	}

Ok.

>        if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
>  	{
> -	  at->family = AF_INET6;
> +	  res.at->family = AF_INET6;
>  	  if ((req->ai_flags & AI_PASSIVE) == 0)
> -	    memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
> +	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
> -	  atr = at->next;
> +	  atr = res.at->next;
>  	}

Ok.

>    {
>      /* Set up the canonical name if we need it.  */
> -    if ((result = process_canonname (req, orig_name, &canon)) != 0)
> +    if ((result = process_canonname (req, orig_name, &res)) != 0)
>        goto free_and_return;

Ok.

> -    struct gaih_addrtuple *at2 = at;
> +    struct gaih_addrtuple *at2 = res.at;

Ok.

>  	    /* We only add the canonical name once.  */
> -	    ai->ai_canonname = (char *) canon;
> +	    ai->ai_canonname = res.canon;
> -	    canon = NULL;
> +	    res.canon = NULL;

Ok.

>   free_and_return:
>    if (malloc_name)
>      free ((char *) name);
>    free (addrmem);
> -  free (canon);
> +  free (res.canon);

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 06/12] gaih_inet: Split simple gethostbyname into its own function
  2022-03-14  9:48   ` [PATCH v2 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
@ 2022-03-17  4:20     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  4:20 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> Add a free_at flag in gaih_result to indicate if res.at needs to be
> freed by the caller.

LGTM with one comment to be added.

Reviewed-by: DJ Delorie <dj@redhat.com>

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> index d7b6eae9fc..bcceab7d07 100644
> --- a/sysdeps/posix/getaddrinfo.c
> +++ b/sysdeps/posix/getaddrinfo.c
> @@ -120,6 +120,7 @@ struct gaih_result
>  {
>    struct gaih_addrtuple *at;
>    char *canon;
> +  bool free_at;
>  };

Ok.

> @@ -565,6 +566,60 @@ out:
>    return result;
>  }
>  
> +/* If possible, call the simple, old functions, which do not support IPv6 scope
> +   ids, nor retrieving the canonical name.  */
> +
> +static int
> +try_simple_gethostbyname (const char *name, const struct addrinfo *req,
> +			  struct scratch_buffer *tmpbuf,
> +			  struct gaih_result *res)
> +{
> +  res->at = NULL;
> +
> +  if (req->ai_family != AF_INET || (req->ai_flags & AI_CANONNAME) != 0)
> +    return 0;
> +
> +  int rc;
> +  struct hostent th;
> +  struct hostent *h;
> +
> +  while (1)
> +    {
> +      rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf->data,
> +			       tmpbuf->length, &h, &h_errno);
> +      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
> +	break;
> +      if (!scratch_buffer_grow (tmpbuf))
> +	return -EAI_MEMORY;
> +    }
> +
> +  if (rc == 0)
> +    {
> +      if (h != NULL)
> +	{
> +	  /* We found data, convert it.  */
> +	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
> +	    return -EAI_MEMORY;
> +
> +	  res->free_at = true;


Please add a comment here that res->at will either be the result of a
realloc() or will be NULL, either of which may be safely passed to
free().  convert_hostent_to_gaih_addrtuple() has a true return path that
doesn't include the allocation, which may confuse future readers.

Otherwise ok

> +	  return 0;
> +	}
> +      if (h_errno == NO_DATA)
> +	return -EAI_NODATA;
> +
> +      return -EAI_NONAME;
> +    }
> +
> +  if (h_errno == NETDB_INTERNAL)
> +    return -EAI_SYSTEM;
> +  if (h_errno == TRY_AGAIN)
> +    return -EAI_AGAIN;
> +
> +  /* We made requests but they turned out no data.
> +     The name is known, though.  */
> +  return -EAI_NODATA;
> +}

Ok.

>  static int
>  gaih_inet (const char *name, const struct gaih_service *service,
>  	   const struct addrinfo *req, struct addrinfo **pai,
> @@ -610,6 +665,11 @@ gaih_inet (const char *name, const struct gaih_service *service,
>        else if (res.at != NULL)
>  	goto process_list;
>  
> +      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
> +	goto free_and_return;
> +      else if (res.at != NULL)
> +	goto process_list;
> +

Ok.

> @@ -619,69 +679,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
>        struct resolv_context *res_ctx = NULL;
>        bool do_merge = false;
>  
> -      /* If we do not have to look for IPv6 addresses or the canonical
> -	 name, use the simple, old functions, which do not support
> - . . .
> -
> -	  goto process_list;
> -	}

Ok.

> +  if (res.free_at)
> +    free (res.at);

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 07/12] gaih_inet: Split nscd lookup code into its own function.
  2022-03-14  9:48   ` [PATCH v2 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
@ 2022-03-17  4:31     ` DJ Delorie
  2022-03-17  6:22       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  4:31 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> Add a new member got_ipv6 to indicate if the results have an IPv6
> result and use it instead of the local got_ipv6.

One problem - need to clear res->got_ipv6 at some point (I couldn't find
a global "memset &res 0" that might indirectly clear it).  I think once
you get a valid ipv6 response, it wlil get stuck set to true.

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> index bcceab7d07..ba5028ad7b 100644
> --- a/sysdeps/posix/getaddrinfo.c
> +++ b/sysdeps/posix/getaddrinfo.c
> @@ -121,6 +121,7 @@ struct gaih_result
>    struct gaih_addrtuple *at;
>    char *canon;
>    bool free_at;
> +  bool got_ipv6;

Ok.

>  
> -	got_ipv6 = true;						      \
> +	res.got_ipv6 = true;						      \

Ok.

>   }
>  
> @@ -467,6 +468,128 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
>    return 0;
>  }
>  
> +#ifdef USE_NSCD
> +/* Query addresses from nscd cache, returning a non-zero value on error.
> +   RES members have the lookup result; RES->AT is NULL if there were no errors
> +   but also no results.  */
> +
> +static int
> +get_nscd_addresses (const char *name, const struct addrinfo *req,
> +		    struct gaih_result *res)
> +{
> + . . .
> +}
> +#endif

Moved, Ok.

>  /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
>     NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
>     the function cannot determine a result, RES->AT is set to NULL and 0
> @@ -628,7 +751,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
>    struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
>  			   / sizeof (struct gaih_typeproto)] = {0};
>  
> -  bool got_ipv6 = false;

Nothing seems to set res.got_ipv6 to false?

> @@ -670,6 +792,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
>        else if (res.at != NULL)
>  	goto process_list;
>  
> +#ifdef USE_NSCD
> +      if ((result = get_nscd_addresses (name, req, &res)) != 0)
> +	goto free_and_return;
> +      else if (res.at != NULL)
> +	goto process_list;
> +#endif

Ok.

> -#ifdef USE_NSCD
> -      if (__nss_not_use_nscd_hosts > 0
> - . . .
> -	}
> -#endif

Ok.

> -			    got_ipv6 = true;
> +			    res.got_ipv6 = true;

Ok.

> -			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
> +			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))

Ok.

> -	    if (got_ipv6
> +	    if (res.got_ipv6

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 08/12] gaih_inet: separate nss lookup loop into its own function
  2022-03-14  9:48   ` [PATCH v2 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
@ 2022-03-17  4:42     ` DJ Delorie
  2022-03-17  4:59       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  4:42 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> ---

One memory leak needs fixing...

> +static void
> +gaih_result_reset (struct gaih_result *res)
> +{
> +  if (res->free_at)
> +    free (res->at);
> +  free (res->canon);
> +  memset (res, 0, sizeof (*res));
> +}

(I think this fixes the problem in [07/12] ;)

> -/* Convert struct hostent to a list of struct gaih_addrtuple objects.
> -   h_name is not copied, and the struct hostent object must not be
> -   deallocated prematurely.  *RESULT must be NULL or a pointer to a
> -   linked-list.  The new addresses are appended at the end.  */
> +/* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
> +   is not copied, and the struct hostent object must not be deallocated
> +   prematurely.  *RESULT must be NULL or a pointer to a linked-list.  The new
> +   addresses are appended at the end.  */

no changes, ok.

>  static bool
> -convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
> -				   int family,
> -				   struct hostent *h,
> -				   struct gaih_addrtuple **result)
> +convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
> +				   struct hostent *h, struct gaih_result *res)

Ok.

> -  struct gaih_addrtuple *array = *result;
> +  struct gaih_addrtuple *array = res->at;

Ok.

> -  array = realloc (*result, (old + count) * sizeof (*array));
> +  array = res->at = realloc (res->at, (old + count) * sizeof (*array));
>  
>    if (array == NULL)
>      return false;
>  
> -  *result = array;

This is not the same - if realloc fails, the original res->at is lost
and can no longer be free'd.

> +  res->got_ipv6 = family == AF_INET6;
> +  res->free_at = true;

Ok.

> -	  goto free_and_return;						      \
> +	  goto out;							      \

Ok.

> -	  goto free_and_return;						      \
> +	  goto out;							      \

Ok.

> -      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem))   \
> +      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \

Ok.

> -	  goto free_and_return;						      \
> +	  goto out;							      \

Ok.

> -      *pat = addrmem;							      \
>  									      \
> -      if (localcanon != NULL && res.canon == NULL)			      \
> +      if (localcanon != NULL && res->canon == NULL)			      \

Ok.

> -	      goto free_and_return;					      \
> +	      goto out;							      \

Ok.

> -	  res.canon = canonbuf;						      \
> +	  res->canon = canonbuf;					      \

Ok.

> -      if (_family == AF_INET6 && *pat != NULL)				      \
> -	res.got_ipv6 = true;						      \

Ok.

> +static int
> +get_nss_addresses (const char *name, const struct addrinfo *req,
> +		   struct scratch_buffer *tmpbuf, struct gaih_result *res)
> +{
> + . . .
> +}

Moved, ok.

> -	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
> +	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res))
>  	    return -EAI_MEMORY;

Ok.

> -      int no_data = 0;
> -      int no_inet6_data = 0;
> - . . . 
> -	}

Moved, ok.

> +      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
> +	goto free_and_return;
> +      else if (res.at != NULL)
> +	goto process_list;

Ok.

> -    process_list:
> -      if (res.at == NULL)
> -	{
> -	  result = -EAI_NONAME;
> -	  goto free_and_return;
> -	}
> +      /* None of the lookups worked, so name not found.  */
> +      result = -EAI_NONAME;
> +      goto free_and_return;

Ok.

>  
> +process_list:
>    {
>      /* Set up the canonical name if we need it.  */
>      if ((result = process_canonname (req, orig_name, &res)) != 0)

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 09/12] gaih_inet: make gethosts into a function
  2022-03-14  9:48   ` [PATCH v2 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
@ 2022-03-17  4:44     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  4:44 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> The macro is quite a pain to debug, so make gethosts into a function to
> make it easier to maintain.

LGTM

Reviewed-by: DJ Delorie <dj@redhat.com>

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> index c33cc2507b..e71f21f3c9 100644
> --- a/sysdeps/posix/getaddrinfo.c
> +++ b/sysdeps/posix/getaddrinfo.c
> @@ -268,63 +268,54 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
>    return true;
>  }
>  
> -#define gethosts(_family) \
> - {									      \
> -  struct hostent th;							      \
> -  char *localcanon = NULL;						      \
> -  no_data = 0;								      \
> -  while (1)								      \
> -    {									      \
> -      status = DL_CALL_FCT (fct, (name, _family, &th,			      \
> -				  tmpbuf->data, tmpbuf->length,		      \
> -				  &errno, &h_errno, NULL, &localcanon));      \
> -      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL	      \
> -	  || errno != ERANGE)						      \
> -	break;								      \
> -      if (!scratch_buffer_grow (tmpbuf))				      \
> -	{								      \
> -	  __resolv_context_put (res_ctx);				      \
> -	  result = -EAI_MEMORY;						      \
> -	  goto out;							      \
> -	}								      \
> -    }									      \
> -  if (status == NSS_STATUS_NOTFOUND					      \
> -      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)	      \
> -    {									      \
> -      if (h_errno == NETDB_INTERNAL)					      \
> -	{								      \
> -	  __resolv_context_put (res_ctx);				      \
> -	  result = -EAI_SYSTEM;						      \
> -	  goto out;							      \
> -	}								      \
> -      if (h_errno == TRY_AGAIN)						      \
> -	no_data = EAI_AGAIN;						      \
> -      else								      \
> -	no_data = h_errno == NO_DATA;					      \
> -    }									      \
> -  else if (status == NSS_STATUS_SUCCESS)				      \
> -    {									      \
> -      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
> -	{								      \
> -	  __resolv_context_put (res_ctx);				      \
> -	  result = -EAI_SYSTEM;						      \
> -	  goto out;							      \
> -	}								      \
> -									      \
> -      if (localcanon != NULL && res->canon == NULL)			      \
> -	{								      \
> -	  char *canonbuf = __strdup (localcanon);			      \
> -	  if (canonbuf == NULL)						      \
> -	    {								      \
> -	      __resolv_context_put (res_ctx);				      \
> -	      result = -EAI_SYSTEM;					      \
> -	      goto out;							      \
> -	    }								      \
> -	  res->canon = canonbuf;					      \
> -	}								      \
> -    }									      \
> - }
> +static int
> +gethosts (nss_gethostbyname3_r fct, int family, const char *name,
> +	  const struct addrinfo *req, struct scratch_buffer *tmpbuf,
> +	  struct gaih_result *res, enum nss_status *statusp, int *no_datap)
> +{
> +  struct hostent th;
> +  char *localcanon = NULL;
> +  enum nss_status status;
> +
> +  *no_datap = 0;
> +  while (1)
> +    {
> +      *statusp = status = DL_CALL_FCT (fct, (name, family, &th,
> +					     tmpbuf->data, tmpbuf->length,
> +					     &errno, &h_errno, NULL,
> +					     &localcanon));
> +      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL
> +	  || errno != ERANGE)
> +	break;
> +      if (!scratch_buffer_grow (tmpbuf))
> +	return -EAI_MEMORY;
> +    }
> +  if (status == NSS_STATUS_NOTFOUND
> +      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
> +    {
> +      if (h_errno == NETDB_INTERNAL)
> +	return -EAI_SYSTEM;
> +      if (h_errno == TRY_AGAIN)
> +	*no_datap = EAI_AGAIN;
> +      else
> +	*no_datap = h_errno == NO_DATA;
> +    }
> +  else if (status == NSS_STATUS_SUCCESS)
> +    {
> +      if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
> +	return -EAI_SYSTEM;
> +
> +      if (localcanon != NULL && res->canon == NULL)
> +	{
> +	  char *canonbuf = __strdup (localcanon);
> +	  if (canonbuf == NULL)
> +	    return  -EAI_SYSTEM;
> +	  res->canon = canonbuf;
> +	}
> +    }
>  
> +  return 0;
> +}
>  
>  /* This function is called if a canonical name is requested, but if
>     the service function did not provide it.  It tries to obtain the
> @@ -741,7 +732,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
>  	      if (req->ai_family == AF_INET6
>  		  || req->ai_family == AF_UNSPEC)
>  		{
> -		  gethosts (AF_INET6);
> +		  if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf,
> +					  res, &status, &no_data)) != 0)
> +		    {
> +		      __resolv_context_put (res_ctx);
> +		      goto out;
> +		    }
>  		  no_inet6_data = no_data;
>  		  inet6_status = status;
>  		}
> @@ -753,7 +749,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
>  			 know we are not going to need them.  */
>  		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
>  		{
> -		  gethosts (AF_INET);
> +		  if ((result = gethosts (fct, AF_INET, name, req, tmpbuf,
> +					  res, &status, &no_data)) != 0)
> +		    {
> +		      __resolv_context_put (res_ctx);
> +		      goto out;
> +		    }
>  
>  		  if (req->ai_family == AF_INET)
>  		    {


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 10/12] gaih_inet: split loopback lookup into its own function
  2022-03-14  9:48   ` [PATCH v2 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
@ 2022-03-17  4:51     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  4:51 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:

> Flatten the condition nesting and replace the alloca for RET.AT/ATR with
> a single array LOCAL_AT[2].  This gets rid of alloca and alloca
> accounting.
>
> `git diff -b` is probably the best way to view this change since much of
> the diff is whitespace changes.

reviewing alternate diff then... ;-)

LGTM.

Reviewed-by: DJ Delorie <dj@redhat.com>

> +/* Add local address information into RES.  RES->AT is assumed to have enough
> +   space for two tuples and is zeroed out.  */
> +
> +static void
> +get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
> +{
> +  struct gaih_addrtuple *atr = res->at;
> +  if (req->ai_family == AF_UNSPEC)
> +    res->at->next = res->at + 1;
> +
> +  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
> +    {
> +      res->at->family = AF_INET6;
> +      if ((req->ai_flags & AI_PASSIVE) == 0)
> +	memcpy (res->at->addr, &in6addr_loopback, sizeof (struct in6_addr));
> +      atr = res->at->next;
> +    }
> +
> +  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
> +    {
> +      atr->family = AF_INET;
> +      if ((req->ai_flags & AI_PASSIVE) == 0)
> +	atr->addr[0] = htonl (INADDR_LOOPBACK);
> +    }
> +}

Ok.

>  
> -  /* Reserve stack memory for the scratch buffer in the getaddrinfo
> -     function.  */
> -  size_t alloca_used = sizeof (struct scratch_buffer);
> -

Ok.

>    struct gaih_result res = {0};
> +  struct gaih_addrtuple local_at[2] = {0};
> +
> +  res.at = local_at;
> +
> +  if (__glibc_unlikely (name == NULL))
> -  if (name != NULL)
>      {
> +      get_local_addresses (req, &res);
> +      goto process_list;
> +    }
> +

Ok.

>  
> -      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
> -      res.at->scopeid = 0;
> -      res.at->next = NULL;

Ok.

> -  else
> -    {
> -      struct gaih_addrtuple *atr;
> -      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
> -				     alloca_used);
> -      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
> -
> -      if (req->ai_family == AF_UNSPEC)
> -	{
> -	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
> -	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
> -	}
> -
> -      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
> -	{
> -	  res.at->family = AF_INET6;
> -	  if ((req->ai_flags & AI_PASSIVE) == 0)
> -	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
> -	  atr = res.at->next;
> -	}
> -
> -      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
> -	{
> -	  atr->family = AF_INET;
> -	  if ((req->ai_flags & AI_PASSIVE) == 0)
> -	    atr->addr[0] = htonl (INADDR_LOOPBACK);
> -	}
> -    }

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 08/12] gaih_inet: separate nss lookup loop into its own function
  2022-03-17  4:42     ` DJ Delorie
@ 2022-03-17  4:59       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  4:59 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

On 17/03/2022 10:12, DJ Delorie wrote:
> Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
>> ---
> 
> One memory leak needs fixing...
> 
>> +static void
>> +gaih_result_reset (struct gaih_result *res)
>> +{
>> +  if (res->free_at)
>> +    free (res->at);
>> +  free (res->canon);
>> +  memset (res, 0, sizeof (*res));
>> +}
> 
> (I think this fixes the problem in [07/12] ;)

Hah, this is why my first though on your comment on 7/12 was "what do 
you mean?  I memset the whole block" :)  I'll move this bit into 7/12.

>> -/* Convert struct hostent to a list of struct gaih_addrtuple objects.
>> -   h_name is not copied, and the struct hostent object must not be
>> -   deallocated prematurely.  *RESULT must be NULL or a pointer to a
>> -   linked-list.  The new addresses are appended at the end.  */
>> +/* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
>> +   is not copied, and the struct hostent object must not be deallocated
>> +   prematurely.  *RESULT must be NULL or a pointer to a linked-list.  The new
>> +   addresses are appended at the end.  */
> 
> no changes, ok.
> 
>>   static bool
>> -convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
>> -				   int family,
>> -				   struct hostent *h,
>> -				   struct gaih_addrtuple **result)
>> +convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
>> +				   struct hostent *h, struct gaih_result *res)
> 
> Ok.
> 
>> -  struct gaih_addrtuple *array = *result;
>> +  struct gaih_addrtuple *array = res->at;
> 
> Ok.
> 
>> -  array = realloc (*result, (old + count) * sizeof (*array));
>> +  array = res->at = realloc (res->at, (old + count) * sizeof (*array));
>>   
>>     if (array == NULL)
>>       return false;
>>   
>> -  *result = array;
> 
> This is not the same - if realloc fails, the original res->at is lost
> and can no longer be free'd.
> 

Oops, indeed.  I'll fix this.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 11/12] gaih_inet: Split result generation into its own function
  2022-03-14  9:48   ` [PATCH v2 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
@ 2022-03-17  5:05     ` DJ Delorie
  2022-03-17  5:11       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  5:05 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> Simplify the loop a wee bit and clean up variable names too.

FYI combining "split out" and "minor changes" makes it harder to review,
because the minor changes are nearly impossible to spot in the large
chunks of "moving".

I had one comment about a leak (see below) but it's independent of this
patch set (maybe?).

LGTM.

Reviewed-by: DJ Delorie <dj@redhat.com>

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> index 3aeeb0d1e0..eebed824a8 100644
> --- a/sysdeps/posix/getaddrinfo.c
> +++ b/sysdeps/posix/getaddrinfo.c
> @@ -1028,6 +1028,87 @@ get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
>      }
>  }
>  
> +/* Generate results in PAI and its count in NADDRS.  Return 0 on success or an
> +   error code on failure.  */
> +
> +static int
> +generate_addrinfo (const struct addrinfo *req, struct gaih_result *res,
> +		   const struct gaih_servtuple *st, struct addrinfo **pai,
> +		   unsigned int *naddrs)
> +{
> + . . .

Ok.

> +      for (int i = 0; st[i].set; i++)
> +	{
> +	  struct addrinfo *ai;
> +	  ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
> +	  if (ai == NULL)
> +	    return -EAI_MEMORY;

I think there might be a memory leak here if malloc fails on the second
or later iteration of the loop.  However, we would have had that leak
before, too, and if we're out of memory... this type of leak is likely
not what you'd be worried about.

> + . . .
> +  return 0;
> +}
> +

Ok.

>  
>  process_list:
> +  /* Set up the canonical name if we need it.  */
> +  if ((result = process_canonname (req, orig_name, &res)) != 0)
> +    goto free_and_return;
> -  {
> -    /* Set up the canonical name if we need it.  */
> -    if ((result = process_canonname (req, orig_name, &res)) != 0)
> -      goto free_and_return;
> -
> - . . .
> -      }
> -  }

Ok.

> +  result = generate_addrinfo (req, &res, st, pai, naddrs);

Ok.

> - free_and_return:
> +free_and_return:

Ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 12/12] gethosts: Return EAI_MEMORY on allocation failure
  2022-03-14  9:48   ` [PATCH v2 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
@ 2022-03-17  5:06     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17  5:06 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
> All other cases of failures due to lack of memory return EAI_MEMORY, so
> it seems wrong to return EAI_SYSTEM here.  The only reason
> convert_hostent_to_gaih_addrtuple could fail is on calloc failure.

LGTM.

Reviewed-by: DJ Delorie <dj@redhat.com>

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
> index eebed824a8..5cd988bec5 100644
> --- a/sysdeps/posix/getaddrinfo.c
> +++ b/sysdeps/posix/getaddrinfo.c
> @@ -303,13 +303,13 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name,
>    else if (status == NSS_STATUS_SUCCESS)
>      {
>        if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
> -	return -EAI_SYSTEM;
> +	return -EAI_MEMORY;
>  
>        if (localcanon != NULL && res->canon == NULL)
>  	{
>  	  char *canonbuf = __strdup (localcanon);
>  	  if (canonbuf == NULL)
> -	    return  -EAI_SYSTEM;
> +	    return  -EAI_MEMORY;
>  	  res->canon = canonbuf;
>  	}
>      }


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 11/12] gaih_inet: Split result generation into its own function
  2022-03-17  5:05     ` DJ Delorie
@ 2022-03-17  5:11       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  5:11 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

On 17/03/2022 10:35, DJ Delorie wrote:
> Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
>> Simplify the loop a wee bit and clean up variable names too.
> 
> FYI combining "split out" and "minor changes" makes it harder to review,
> because the minor changes are nearly impossible to spot in the large
> chunks of "moving".

Ack, I got a bit lazy :/

> I had one comment about a leak (see below) but it's independent of this
> patch set (maybe?).

Yeah, it's a preexisting leak, but it only manifests when the system is 
out of memory.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 07/12] gaih_inet: Split nscd lookup code into its own function.
  2022-03-17  4:31     ` DJ Delorie
@ 2022-03-17  6:22       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  6:22 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

On 17/03/2022 10:01, DJ Delorie via Libc-alpha wrote:
> 
> Siddhesh Poyarekar via Libc-alpha <libc-alpha@sourceware.org> writes:
>> Add a new member got_ipv6 to indicate if the results have an IPv6
>> result and use it instead of the local got_ipv6.
> 
> One problem - need to clear res->got_ipv6 at some point (I couldn't find
> a global "memset &res 0" that might indirectly clear it).  I think once
> you get a valid ipv6 response, it wlil get stuck set to true.

I took a closer look at this and the nscd bit doesn't seem to need this; 
it's the nss code below that needs the initialization.  I've fixed 1/12 
to resolve it; 8/12 will then propagate it to gaih_result.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 00/12] getaddrinfo facelift and fixes
  2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
                   ` (13 preceding siblings ...)
  2022-03-14 13:21 ` [PATCH 00/12] getaddrinfo facelift and fixes Cristian Rodríguez
@ 2022-03-17  8:11 ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
                     ` (11 more replies)
  14 siblings, 12 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

The gaih_inet implementation, which forms the core of getaddrinfo, is
quite complex in its implementation, making it hard to follow or debug.
Particularly, allocations for gaih_addrtuples to store intermediate
results are particularly hard to track because of the way in which it is
written.

This patchset is an attempt at cleaning up the implementation to make it
much easier to follow.  In the process, it also fixes a couple of bugs,
one that was the trigger for this cleanup and another that was
discovered during the cleanup.

Changes from v2:
- 1/12: reinitialized got_ipv6 too, changed test case to run in a
  container.
- 8/12: Fix leak on realloc failure
- Rest: updated review comments.

Changes from v1:
- Fixed nit: boolean coercion in convert_hostent_to_gaih_addrtuple
- Dropped DNS tests in tst-nss-gai-actions test since they are
  unnecessary and the files tests are sufficient.

Siddhesh Poyarekar (12):
  Simplify allocations and fix merge and continue actions [BZ #28931]
  gaih_inet: Simplify canon name resolution
  getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  gaih_inet: Simplify service resolution
  gaih_inet: make numeric lookup a separate routine
  gaih_inet: Split simple gethostbyname into its own function
  gaih_inet: Split nscd lookup code into its own function.
  gaih_inet: separate nss lookup loop into its own function
  gaih_inet: make gethosts into a function
  gaih_inet: split loopback lookup into its own function
  gaih_inet: Split result generation into its own function
  gethosts: Return EAI_MEMORY on allocation failure

 nss/Makefile                               |    1 +
 nss/tst-nss-gai-actions.c                  |  149 ++
 nss/tst-nss-gai-actions.root/etc/host.conf |    1 +
 nss/tst-nss-gai-actions.root/etc/hosts     |  508 +++++++
 sysdeps/posix/getaddrinfo.c                | 1520 +++++++++++---------
 5 files changed, 1464 insertions(+), 715 deletions(-)
 create mode 100644 nss/tst-nss-gai-actions.c
 create mode 100644 nss/tst-nss-gai-actions.root/etc/host.conf
 create mode 100644 nss/tst-nss-gai-actions.root/etc/hosts

-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17 21:47     ` DJ Delorie
  2022-03-17  8:11   ` [PATCH v3 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
                     ` (10 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Allocations for address tuples is currently a bit confusing because of
the pointer chasing through PAT, making it hard to observe the sequence
in which allocations have been made.  Narrow scope of the pointer
chasing through PAT so that it is only used where necessary.

This also tightens actions behaviour with the hosts database in
getaddrinfo to comply with the manual text.  The "continue" action
discards previous results and the "merge" action results in an immedate
lookup failure.  Consequently, chaining of allocations across modules is
no longer necessary, thus opening up cleanup opportunities.

A test has been added that checks some combinations to ensure that they
work correctly.

Resolves: BZ #28931

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 nss/Makefile                               |   1 +
 nss/tst-nss-gai-actions.c                  | 149 ++++++
 nss/tst-nss-gai-actions.root/etc/host.conf |   1 +
 nss/tst-nss-gai-actions.root/etc/hosts     | 508 +++++++++++++++++++++
 sysdeps/posix/getaddrinfo.c                | 143 +++---
 5 files changed, 750 insertions(+), 52 deletions(-)
 create mode 100644 nss/tst-nss-gai-actions.c
 create mode 100644 nss/tst-nss-gai-actions.root/etc/host.conf
 create mode 100644 nss/tst-nss-gai-actions.root/etc/hosts

diff --git a/nss/Makefile b/nss/Makefile
index 42a59535cb..d8b06b44fb 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -76,6 +76,7 @@ tests-container := \
   tst-nss-db-endgrent \
   tst-nss-db-endpwent \
   tst-nss-files-hosts-long \
+  tst-nss-gai-actions \
   tst-nss-test3 \
   tst-reload1 \
   tst-reload2 \
diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
new file mode 100644
index 0000000000..efca6cd183
--- /dev/null
+++ b/nss/tst-nss-gai-actions.c
@@ -0,0 +1,149 @@
+/* Test continue and merge NSS actions for getaddrinfo.
+   Copyright The GNU Toolchain Authors.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <gnu/lib-names.h>
+#include <nss.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+enum
+{
+  ACTION_MERGE = 0,
+  ACTION_CONTINUE,
+};
+
+static const char *
+family_str (int family)
+{
+  switch (family)
+    {
+    case AF_UNSPEC:
+      return "AF_UNSPEC";
+    case AF_INET:
+      return "AF_INET";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static const char *
+action_str (int action)
+{
+  switch (action)
+    {
+    case ACTION_MERGE:
+      return "merge";
+    case ACTION_CONTINUE:
+      return "continue";
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static void
+do_one_test (int action, int family, bool canon)
+{
+  struct addrinfo hints =
+    {
+      .ai_family = family,
+    };
+
+  struct addrinfo *ai;
+
+  if (canon)
+    hints.ai_flags = AI_CANONNAME;
+
+  printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n",
+	  action_str (action), family_str (family),
+	  canon ? "AI_CANONNAME" : "");
+
+  int ret = getaddrinfo ("example.org", "80", &hints, &ai);
+
+  switch (action)
+    {
+    case ACTION_MERGE:
+      if (ret == 0)
+	{
+	  char *formatted = support_format_addrinfo (ai, ret);
+
+	  printf ("merge unexpectedly succeeded:\n %s\n", formatted);
+	  support_record_failure ();
+	  free (formatted);
+	}
+      else
+	return;
+    case ACTION_CONTINUE:
+	{
+	  char *formatted = support_format_addrinfo (ai, ret);
+
+	  /* Verify that the result appears exactly once.  */
+	  const char *expected = "address: STREAM/TCP 192.0.0.1 80\n"
+	    "address: DGRAM/UDP 192.0.0.1 80\n"
+	    "address: RAW/IP 192.0.0.1 80\n";
+
+	  const char *contains = strstr (formatted, expected);
+	  const char *contains2 = NULL;
+
+	  if (contains != NULL)
+	    contains2 = strstr (contains + strlen (expected), expected);
+
+	  if (contains == NULL || contains2 != NULL)
+	    {
+	      printf ("continue failed:\n%s\n", formatted);
+	      support_record_failure ();
+	    }
+
+	  free (formatted);
+	  break;
+	}
+    default:
+      __builtin_unreachable ();
+    }
+}
+
+static void
+do_one_test_set (int action)
+{
+  char buf[32];
+
+  snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files",
+	    action_str (action));
+  __nss_configure_lookup ("hosts", buf);
+
+  do_one_test (action, AF_UNSPEC, false);
+  do_one_test (action, AF_INET, false);
+  do_one_test (action, AF_INET, true);
+}
+
+static int
+do_test (void)
+{
+  do_one_test_set (ACTION_CONTINUE);
+  do_one_test_set (ACTION_MERGE);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nss/tst-nss-gai-actions.root/etc/host.conf b/nss/tst-nss-gai-actions.root/etc/host.conf
new file mode 100644
index 0000000000..d1a59f73a9
--- /dev/null
+++ b/nss/tst-nss-gai-actions.root/etc/host.conf
@@ -0,0 +1 @@
+multi on
diff --git a/nss/tst-nss-gai-actions.root/etc/hosts b/nss/tst-nss-gai-actions.root/etc/hosts
new file mode 100644
index 0000000000..50ce9774dc
--- /dev/null
+++ b/nss/tst-nss-gai-actions.root/etc/hosts
@@ -0,0 +1,508 @@
+192.0.0.1	example.org
+192.0.0.2	example.org
+192.0.0.3	example.org
+192.0.0.4	example.org
+192.0.0.5	example.org
+192.0.0.6	example.org
+192.0.0.7	example.org
+192.0.0.8	example.org
+192.0.0.9	example.org
+192.0.0.10	example.org
+192.0.0.11	example.org
+192.0.0.12	example.org
+192.0.0.13	example.org
+192.0.0.14	example.org
+192.0.0.15	example.org
+192.0.0.16	example.org
+192.0.0.17	example.org
+192.0.0.18	example.org
+192.0.0.19	example.org
+192.0.0.20	example.org
+192.0.0.21	example.org
+192.0.0.22	example.org
+192.0.0.23	example.org
+192.0.0.24	example.org
+192.0.0.25	example.org
+192.0.0.26	example.org
+192.0.0.27	example.org
+192.0.0.28	example.org
+192.0.0.29	example.org
+192.0.0.30	example.org
+192.0.0.31	example.org
+192.0.0.32	example.org
+192.0.0.33	example.org
+192.0.0.34	example.org
+192.0.0.35	example.org
+192.0.0.36	example.org
+192.0.0.37	example.org
+192.0.0.38	example.org
+192.0.0.39	example.org
+192.0.0.40	example.org
+192.0.0.41	example.org
+192.0.0.42	example.org
+192.0.0.43	example.org
+192.0.0.44	example.org
+192.0.0.45	example.org
+192.0.0.46	example.org
+192.0.0.47	example.org
+192.0.0.48	example.org
+192.0.0.49	example.org
+192.0.0.50	example.org
+192.0.0.51	example.org
+192.0.0.52	example.org
+192.0.0.53	example.org
+192.0.0.54	example.org
+192.0.0.55	example.org
+192.0.0.56	example.org
+192.0.0.57	example.org
+192.0.0.58	example.org
+192.0.0.59	example.org
+192.0.0.60	example.org
+192.0.0.61	example.org
+192.0.0.62	example.org
+192.0.0.63	example.org
+192.0.0.64	example.org
+192.0.0.65	example.org
+192.0.0.66	example.org
+192.0.0.67	example.org
+192.0.0.68	example.org
+192.0.0.69	example.org
+192.0.0.70	example.org
+192.0.0.71	example.org
+192.0.0.72	example.org
+192.0.0.73	example.org
+192.0.0.74	example.org
+192.0.0.75	example.org
+192.0.0.76	example.org
+192.0.0.77	example.org
+192.0.0.78	example.org
+192.0.0.79	example.org
+192.0.0.80	example.org
+192.0.0.81	example.org
+192.0.0.82	example.org
+192.0.0.83	example.org
+192.0.0.84	example.org
+192.0.0.85	example.org
+192.0.0.86	example.org
+192.0.0.87	example.org
+192.0.0.88	example.org
+192.0.0.89	example.org
+192.0.0.90	example.org
+192.0.0.91	example.org
+192.0.0.92	example.org
+192.0.0.93	example.org
+192.0.0.94	example.org
+192.0.0.95	example.org
+192.0.0.96	example.org
+192.0.0.97	example.org
+192.0.0.98	example.org
+192.0.0.99	example.org
+192.0.0.100	example.org
+192.0.0.101	example.org
+192.0.0.102	example.org
+192.0.0.103	example.org
+192.0.0.104	example.org
+192.0.0.105	example.org
+192.0.0.106	example.org
+192.0.0.107	example.org
+192.0.0.108	example.org
+192.0.0.109	example.org
+192.0.0.110	example.org
+192.0.0.111	example.org
+192.0.0.112	example.org
+192.0.0.113	example.org
+192.0.0.114	example.org
+192.0.0.115	example.org
+192.0.0.116	example.org
+192.0.0.117	example.org
+192.0.0.118	example.org
+192.0.0.119	example.org
+192.0.0.120	example.org
+192.0.0.121	example.org
+192.0.0.122	example.org
+192.0.0.123	example.org
+192.0.0.124	example.org
+192.0.0.125	example.org
+192.0.0.126	example.org
+192.0.0.127	example.org
+192.0.0.128	example.org
+192.0.0.129	example.org
+192.0.0.130	example.org
+192.0.0.131	example.org
+192.0.0.132	example.org
+192.0.0.133	example.org
+192.0.0.134	example.org
+192.0.0.135	example.org
+192.0.0.136	example.org
+192.0.0.137	example.org
+192.0.0.138	example.org
+192.0.0.139	example.org
+192.0.0.140	example.org
+192.0.0.141	example.org
+192.0.0.142	example.org
+192.0.0.143	example.org
+192.0.0.144	example.org
+192.0.0.145	example.org
+192.0.0.146	example.org
+192.0.0.147	example.org
+192.0.0.148	example.org
+192.0.0.149	example.org
+192.0.0.150	example.org
+192.0.0.151	example.org
+192.0.0.152	example.org
+192.0.0.153	example.org
+192.0.0.154	example.org
+192.0.0.155	example.org
+192.0.0.156	example.org
+192.0.0.157	example.org
+192.0.0.158	example.org
+192.0.0.159	example.org
+192.0.0.160	example.org
+192.0.0.161	example.org
+192.0.0.162	example.org
+192.0.0.163	example.org
+192.0.0.164	example.org
+192.0.0.165	example.org
+192.0.0.166	example.org
+192.0.0.167	example.org
+192.0.0.168	example.org
+192.0.0.169	example.org
+192.0.0.170	example.org
+192.0.0.171	example.org
+192.0.0.172	example.org
+192.0.0.173	example.org
+192.0.0.174	example.org
+192.0.0.175	example.org
+192.0.0.176	example.org
+192.0.0.177	example.org
+192.0.0.178	example.org
+192.0.0.179	example.org
+192.0.0.180	example.org
+192.0.0.181	example.org
+192.0.0.182	example.org
+192.0.0.183	example.org
+192.0.0.184	example.org
+192.0.0.185	example.org
+192.0.0.186	example.org
+192.0.0.187	example.org
+192.0.0.188	example.org
+192.0.0.189	example.org
+192.0.0.190	example.org
+192.0.0.191	example.org
+192.0.0.192	example.org
+192.0.0.193	example.org
+192.0.0.194	example.org
+192.0.0.195	example.org
+192.0.0.196	example.org
+192.0.0.197	example.org
+192.0.0.198	example.org
+192.0.0.199	example.org
+192.0.0.200	example.org
+192.0.0.201	example.org
+192.0.0.202	example.org
+192.0.0.203	example.org
+192.0.0.204	example.org
+192.0.0.205	example.org
+192.0.0.206	example.org
+192.0.0.207	example.org
+192.0.0.208	example.org
+192.0.0.209	example.org
+192.0.0.210	example.org
+192.0.0.211	example.org
+192.0.0.212	example.org
+192.0.0.213	example.org
+192.0.0.214	example.org
+192.0.0.215	example.org
+192.0.0.216	example.org
+192.0.0.217	example.org
+192.0.0.218	example.org
+192.0.0.219	example.org
+192.0.0.220	example.org
+192.0.0.221	example.org
+192.0.0.222	example.org
+192.0.0.223	example.org
+192.0.0.224	example.org
+192.0.0.225	example.org
+192.0.0.226	example.org
+192.0.0.227	example.org
+192.0.0.228	example.org
+192.0.0.229	example.org
+192.0.0.230	example.org
+192.0.0.231	example.org
+192.0.0.232	example.org
+192.0.0.233	example.org
+192.0.0.234	example.org
+192.0.0.235	example.org
+192.0.0.236	example.org
+192.0.0.237	example.org
+192.0.0.238	example.org
+192.0.0.239	example.org
+192.0.0.240	example.org
+192.0.0.241	example.org
+192.0.0.242	example.org
+192.0.0.243	example.org
+192.0.0.244	example.org
+192.0.0.245	example.org
+192.0.0.246	example.org
+192.0.0.247	example.org
+192.0.0.248	example.org
+192.0.0.249	example.org
+192.0.0.250	example.org
+192.0.0.251	example.org
+192.0.0.252	example.org
+192.0.0.253	example.org
+192.0.0.254	example.org
+192.0.1.1	example.org
+192.0.1.2	example.org
+192.0.1.3	example.org
+192.0.1.4	example.org
+192.0.1.5	example.org
+192.0.1.6	example.org
+192.0.1.7	example.org
+192.0.1.8	example.org
+192.0.1.9	example.org
+192.0.1.10	example.org
+192.0.1.11	example.org
+192.0.1.12	example.org
+192.0.1.13	example.org
+192.0.1.14	example.org
+192.0.1.15	example.org
+192.0.1.16	example.org
+192.0.1.17	example.org
+192.0.1.18	example.org
+192.0.1.19	example.org
+192.0.1.20	example.org
+192.0.1.21	example.org
+192.0.1.22	example.org
+192.0.1.23	example.org
+192.0.1.24	example.org
+192.0.1.25	example.org
+192.0.1.26	example.org
+192.0.1.27	example.org
+192.0.1.28	example.org
+192.0.1.29	example.org
+192.0.1.30	example.org
+192.0.1.31	example.org
+192.0.1.32	example.org
+192.0.1.33	example.org
+192.0.1.34	example.org
+192.0.1.35	example.org
+192.0.1.36	example.org
+192.0.1.37	example.org
+192.0.1.38	example.org
+192.0.1.39	example.org
+192.0.1.40	example.org
+192.0.1.41	example.org
+192.0.1.42	example.org
+192.0.1.43	example.org
+192.0.1.44	example.org
+192.0.1.45	example.org
+192.0.1.46	example.org
+192.0.1.47	example.org
+192.0.1.48	example.org
+192.0.1.49	example.org
+192.0.1.50	example.org
+192.0.1.51	example.org
+192.0.1.52	example.org
+192.0.1.53	example.org
+192.0.1.54	example.org
+192.0.1.55	example.org
+192.0.1.56	example.org
+192.0.1.57	example.org
+192.0.1.58	example.org
+192.0.1.59	example.org
+192.0.1.60	example.org
+192.0.1.61	example.org
+192.0.1.62	example.org
+192.0.1.63	example.org
+192.0.1.64	example.org
+192.0.1.65	example.org
+192.0.1.66	example.org
+192.0.1.67	example.org
+192.0.1.68	example.org
+192.0.1.69	example.org
+192.0.1.70	example.org
+192.0.1.71	example.org
+192.0.1.72	example.org
+192.0.1.73	example.org
+192.0.1.74	example.org
+192.0.1.75	example.org
+192.0.1.76	example.org
+192.0.1.77	example.org
+192.0.1.78	example.org
+192.0.1.79	example.org
+192.0.1.80	example.org
+192.0.1.81	example.org
+192.0.1.82	example.org
+192.0.1.83	example.org
+192.0.1.84	example.org
+192.0.1.85	example.org
+192.0.1.86	example.org
+192.0.1.87	example.org
+192.0.1.88	example.org
+192.0.1.89	example.org
+192.0.1.90	example.org
+192.0.1.91	example.org
+192.0.1.92	example.org
+192.0.1.93	example.org
+192.0.1.94	example.org
+192.0.1.95	example.org
+192.0.1.96	example.org
+192.0.1.97	example.org
+192.0.1.98	example.org
+192.0.1.99	example.org
+192.0.1.100	example.org
+192.0.1.101	example.org
+192.0.1.102	example.org
+192.0.1.103	example.org
+192.0.1.104	example.org
+192.0.1.105	example.org
+192.0.1.106	example.org
+192.0.1.107	example.org
+192.0.1.108	example.org
+192.0.1.109	example.org
+192.0.1.110	example.org
+192.0.1.111	example.org
+192.0.1.112	example.org
+192.0.1.113	example.org
+192.0.1.114	example.org
+192.0.1.115	example.org
+192.0.1.116	example.org
+192.0.1.117	example.org
+192.0.1.118	example.org
+192.0.1.119	example.org
+192.0.1.120	example.org
+192.0.1.121	example.org
+192.0.1.122	example.org
+192.0.1.123	example.org
+192.0.1.124	example.org
+192.0.1.125	example.org
+192.0.1.126	example.org
+192.0.1.127	example.org
+192.0.1.128	example.org
+192.0.1.129	example.org
+192.0.1.130	example.org
+192.0.1.131	example.org
+192.0.1.132	example.org
+192.0.1.133	example.org
+192.0.1.134	example.org
+192.0.1.135	example.org
+192.0.1.136	example.org
+192.0.1.137	example.org
+192.0.1.138	example.org
+192.0.1.139	example.org
+192.0.1.140	example.org
+192.0.1.141	example.org
+192.0.1.142	example.org
+192.0.1.143	example.org
+192.0.1.144	example.org
+192.0.1.145	example.org
+192.0.1.146	example.org
+192.0.1.147	example.org
+192.0.1.148	example.org
+192.0.1.149	example.org
+192.0.1.150	example.org
+192.0.1.151	example.org
+192.0.1.152	example.org
+192.0.1.153	example.org
+192.0.1.154	example.org
+192.0.1.155	example.org
+192.0.1.156	example.org
+192.0.1.157	example.org
+192.0.1.158	example.org
+192.0.1.159	example.org
+192.0.1.160	example.org
+192.0.1.161	example.org
+192.0.1.162	example.org
+192.0.1.163	example.org
+192.0.1.164	example.org
+192.0.1.165	example.org
+192.0.1.166	example.org
+192.0.1.167	example.org
+192.0.1.168	example.org
+192.0.1.169	example.org
+192.0.1.170	example.org
+192.0.1.171	example.org
+192.0.1.172	example.org
+192.0.1.173	example.org
+192.0.1.174	example.org
+192.0.1.175	example.org
+192.0.1.176	example.org
+192.0.1.177	example.org
+192.0.1.178	example.org
+192.0.1.179	example.org
+192.0.1.180	example.org
+192.0.1.181	example.org
+192.0.1.182	example.org
+192.0.1.183	example.org
+192.0.1.184	example.org
+192.0.1.185	example.org
+192.0.1.186	example.org
+192.0.1.187	example.org
+192.0.1.188	example.org
+192.0.1.189	example.org
+192.0.1.190	example.org
+192.0.1.191	example.org
+192.0.1.192	example.org
+192.0.1.193	example.org
+192.0.1.194	example.org
+192.0.1.195	example.org
+192.0.1.196	example.org
+192.0.1.197	example.org
+192.0.1.198	example.org
+192.0.1.199	example.org
+192.0.1.200	example.org
+192.0.1.201	example.org
+192.0.1.202	example.org
+192.0.1.203	example.org
+192.0.1.204	example.org
+192.0.1.205	example.org
+192.0.1.206	example.org
+192.0.1.207	example.org
+192.0.1.208	example.org
+192.0.1.209	example.org
+192.0.1.210	example.org
+192.0.1.211	example.org
+192.0.1.212	example.org
+192.0.1.213	example.org
+192.0.1.214	example.org
+192.0.1.215	example.org
+192.0.1.216	example.org
+192.0.1.217	example.org
+192.0.1.218	example.org
+192.0.1.219	example.org
+192.0.1.220	example.org
+192.0.1.221	example.org
+192.0.1.222	example.org
+192.0.1.223	example.org
+192.0.1.224	example.org
+192.0.1.225	example.org
+192.0.1.226	example.org
+192.0.1.227	example.org
+192.0.1.228	example.org
+192.0.1.229	example.org
+192.0.1.230	example.org
+192.0.1.231	example.org
+192.0.1.232	example.org
+192.0.1.233	example.org
+192.0.1.234	example.org
+192.0.1.235	example.org
+192.0.1.236	example.org
+192.0.1.237	example.org
+192.0.1.238	example.org
+192.0.1.239	example.org
+192.0.1.240	example.org
+192.0.1.241	example.org
+192.0.1.242	example.org
+192.0.1.243	example.org
+192.0.1.244	example.org
+192.0.1.245	example.org
+192.0.1.246	example.org
+192.0.1.247	example.org
+192.0.1.248	example.org
+192.0.1.249	example.org
+192.0.1.250	example.org
+192.0.1.251	example.org
+192.0.1.252	example.org
+192.0.1.253	example.org
+192.0.1.254	example.org
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 18dccd5924..3d9bea60c6 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -458,11 +458,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   if (name != NULL)
     {
-      at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      at->family = AF_UNSPEC;
-      at->scopeid = 0;
-      at->next = NULL;
-
       if (req->ai_flags & AI_IDN)
 	{
 	  char *out;
@@ -473,13 +468,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+      uint32_t addr[4];
+      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
 	{
+	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+	  at->scopeid = 0;
+	  at->next = NULL;
+
 	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    at->family = AF_INET;
+	    {
+	      memcpy (at->addr, addr, sizeof (at->addr));
+	      at->family = AF_INET;
+	    }
 	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
 	    {
-	      at->addr[3] = at->addr[0];
+	      at->addr[3] = addr[0];
 	      at->addr[2] = htonl (0xffff);
 	      at->addr[1] = 0;
 	      at->addr[0] = 0;
@@ -493,49 +496,62 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  if (req->ai_flags & AI_CANONNAME)
 	    canon = name;
+
+	  goto process_list;
 	}
-      else if (at->family == AF_UNSPEC)
+
+      char *scope_delim = strchr (name, SCOPE_DELIMITER);
+      int e;
+
+      if (scope_delim == NULL)
+	e = inet_pton (AF_INET6, name, addr);
+      else
+	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
+
+      if (e > 0)
 	{
-	  char *scope_delim = strchr (name, SCOPE_DELIMITER);
-	  int e;
-	  if (scope_delim == NULL)
-	    e = inet_pton (AF_INET6, name, at->addr);
+	  at = alloca_account (sizeof (struct gaih_addrtuple),
+			       alloca_used);
+	  at->scopeid = 0;
+	  at->next = NULL;
+
+	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+	    {
+	      memcpy (at->addr, addr, sizeof (at->addr));
+	      at->family = AF_INET6;
+	    }
+	  else if (req->ai_family == AF_INET
+		   && IN6_IS_ADDR_V4MAPPED (addr))
+	    {
+	      at->addr[0] = addr[3];
+	      at->addr[1] = addr[1];
+	      at->addr[2] = addr[2];
+	      at->addr[3] = addr[3];
+	      at->family = AF_INET;
+	    }
 	  else
-	    e = __inet_pton_length (AF_INET6, name, scope_delim - name,
-				    at->addr);
-	  if (e > 0)
 	    {
-	      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-		at->family = AF_INET6;
-	      else if (req->ai_family == AF_INET
-		       && IN6_IS_ADDR_V4MAPPED (at->addr))
-		{
-		  at->addr[0] = at->addr[3];
-		  at->family = AF_INET;
-		}
-	      else
-		{
-		  result = -EAI_ADDRFAMILY;
-		  goto free_and_return;
-		}
-
-	      if (scope_delim != NULL
-		  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
-					   scope_delim + 1,
-					   &at->scopeid) != 0)
-		{
-		  result = -EAI_NONAME;
-		  goto free_and_return;
-		}
+	      result = -EAI_ADDRFAMILY;
+	      goto free_and_return;
+	    }
 
-	      if (req->ai_flags & AI_CANONNAME)
-		canon = name;
+	  if (scope_delim != NULL
+	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+				       scope_delim + 1,
+				       &at->scopeid) != 0)
+	    {
+	      result = -EAI_NONAME;
+	      goto free_and_return;
 	    }
+
+	  if (req->ai_flags & AI_CANONNAME)
+	    canon = name;
+
+	  goto process_list;
 	}
 
-      if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
+      if ((req->ai_flags & AI_NUMERICHOST) == 0)
 	{
-	  struct gaih_addrtuple **pat = &at;
 	  int no_data = 0;
 	  int no_inet6_data = 0;
 	  nss_action_list nip;
@@ -543,6 +559,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  enum nss_status status = NSS_STATUS_UNAVAIL;
 	  int no_more;
 	  struct resolv_context *res_ctx = NULL;
+	  bool do_merge = false;
 
 	  /* If we do not have to look for IPv6 addresses or the canonical
 	     name, use the simple, old functions, which do not support
@@ -579,7 +596,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  result = -EAI_MEMORY;
 			  goto free_and_return;
 			}
-		      *pat = addrmem;
+		      at = addrmem;
 		    }
 		  else
 		    {
@@ -632,6 +649,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		    }
 
 		  struct gaih_addrtuple *addrfree = addrmem;
+		  struct gaih_addrtuple **pat = &at;
+
 		  for (int i = 0; i < air->naddrs; ++i)
 		    {
 		      socklen_t size = (air->family[i] == AF_INET
@@ -695,12 +714,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 		  free (air);
 
-		  if (at->family == AF_UNSPEC)
-		    {
-		      result = -EAI_NONAME;
-		      goto free_and_return;
-		    }
-
 		  goto process_list;
 		}
 	      else if (err == 0)
@@ -732,6 +745,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	  while (!no_more)
 	    {
+	      /* Always start afresh; continue should discard previous results
+		 and the hosts database does not support merge.  */
+	      at = NULL;
+	      free (canonbuf);
+	      free (addrmem);
+	      canon = canonbuf = NULL;
+	      addrmem = NULL;
+	      got_ipv6 = false;
+
+	      if (do_merge)
+		{
+		  __set_h_errno (NETDB_INTERNAL);
+		  __set_errno (EBUSY);
+		  break;
+		}
+
 	      no_data = 0;
 	      nss_gethostbyname4_r *fct4 = NULL;
 
@@ -744,12 +773,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		{
 		  while (1)
 		    {
-		      status = DL_CALL_FCT (fct4, (name, pat,
+		      status = DL_CALL_FCT (fct4, (name, &at,
 						   tmpbuf->data, tmpbuf->length,
 						   &errno, &h_errno,
 						   NULL));
 		      if (status == NSS_STATUS_SUCCESS)
 			break;
+		      /* gethostbyname4_r may write into AT, so reset it.  */
+		      at = NULL;
 		      if (status != NSS_STATUS_TRYAGAIN
 			  || errno != ERANGE || h_errno != NETDB_INTERNAL)
 			{
@@ -774,7 +805,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      no_data = 1;
 
 		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
-			canon = (*pat)->name;
+			canon = at->name;
+
+		      struct gaih_addrtuple **pat = &at;
 
 		      while (*pat != NULL)
 			{
@@ -826,6 +859,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 		  if (fct != NULL)
 		    {
+		      struct gaih_addrtuple **pat = &at;
+
 		      if (req->ai_family == AF_INET6
 			  || req->ai_family == AF_UNSPEC)
 			{
@@ -899,6 +934,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
 		break;
 
+	      /* The hosts database does not support MERGE.  */
+	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+		do_merge = true;
+
 	      nip++;
 	      if (nip->module == NULL)
 		no_more = -1;
@@ -930,7 +969,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
 
     process_list:
-      if (at->family == AF_UNSPEC)
+      if (at == NULL)
 	{
 	  result = -EAI_NONAME;
 	  goto free_and_return;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 02/12] gaih_inet: Simplify canon name resolution
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
                     ` (9 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Simplify logic for allocation of canon to remove the canonbuf variable;
canon now always points to an allocated block.  Also pull the canon name
set into a separate function.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 130 +++++++++++++++++++++---------------
 1 file changed, 75 insertions(+), 55 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 3d9bea60c6..0629fd147b 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -285,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 									      \
       if (localcanon != NULL && canon == NULL)				      \
 	{								      \
-	  canonbuf = __strdup (localcanon);				      \
+	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
 	    {								      \
 	      __resolv_context_put (res_ctx);				      \
@@ -323,6 +323,41 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
   return __strdup (name);
 }
 
+/* Process looked up canonical name and if necessary, decode to IDNA.  Result
+   is a new string written to CANONP and the earlier string is freed.  */
+
+static int
+process_canonname (const struct addrinfo *req, const char *orig_name,
+		   char **canonp)
+{
+  char *canon = *canonp;
+
+  if ((req->ai_flags & AI_CANONNAME) != 0)
+    {
+      bool do_idn = req->ai_flags & AI_CANONIDN;
+      if (do_idn)
+	{
+	  char *out;
+	  int rc = __idna_from_dns_encoding (canon ?: orig_name, &out);
+	  if (rc == 0)
+	    {
+	      free (canon);
+	      canon = out;
+	    }
+	  else if (rc == EAI_IDN_ENCODE)
+	    /* Use the punycode name as a fallback.  */
+	    do_idn = false;
+	  else
+	    return -rc;
+	}
+      if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL)
+	return -EAI_MEMORY;
+    }
+
+  *canonp = canon;
+  return 0;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -332,7 +367,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
   struct gaih_addrtuple *at = NULL;
   bool got_ipv6 = false;
-  const char *canon = NULL;
+  char *canon = NULL;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -453,7 +488,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   bool malloc_name = false;
   struct gaih_addrtuple *addrmem = NULL;
-  char *canonbuf = NULL;
   int result = 0;
 
   if (name != NULL)
@@ -495,7 +529,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    }
 
 	  if (req->ai_flags & AI_CANONNAME)
-	    canon = name;
+	    {
+	      char *canonbuf = __strdup (name);
+	      if (canonbuf == NULL)
+		{
+		  result = -EAI_MEMORY;
+		  goto free_and_return;
+		}
+	      canon = canonbuf;
+	    }
 
 	  goto process_list;
 	}
@@ -545,7 +587,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    }
 
 	  if (req->ai_flags & AI_CANONNAME)
-	    canon = name;
+	    {
+	      char *canonbuf = __strdup (name);
+	      if (canonbuf == NULL)
+		{
+		  result = -EAI_MEMORY;
+		  goto free_and_return;
+		}
+	      canon = canonbuf;
+	    }
 
 	  goto process_list;
 	}
@@ -676,9 +726,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      (*pat)->next = NULL;
 		      if (added_canon || air->canon == NULL)
 			(*pat)->name = NULL;
-		      else if (canonbuf == NULL)
+		      else if (canon == NULL)
 			{
-			  canonbuf = __strdup (air->canon);
+			  char *canonbuf = __strdup (air->canon);
 			  if (canonbuf == NULL)
 			    {
 			      result = -EAI_MEMORY;
@@ -748,9 +798,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      /* Always start afresh; continue should discard previous results
 		 and the hosts database does not support merge.  */
 	      at = NULL;
-	      free (canonbuf);
+	      free (canon);
 	      free (addrmem);
-	      canon = canonbuf = NULL;
+	      canon = NULL;
 	      addrmem = NULL;
 	      got_ipv6 = false;
 
@@ -805,7 +855,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		      no_data = 1;
 
 		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
-			canon = at->name;
+			{
+			  char *canonbuf = __strdup (at->name);
+			  if (canonbuf == NULL)
+			    {
+			      __resolv_context_put (res_ctx);
+			      result = -EAI_MEMORY;
+			      goto free_and_return;
+			    }
+			  canon = canonbuf;
+			}
 
 		      struct gaih_addrtuple **pat = &at;
 
@@ -893,7 +952,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  if ((req->ai_flags & AI_CANONNAME) != 0
 			      && canon == NULL)
 			    {
-			      canonbuf = getcanonname (nip, at, name);
+			      char *canonbuf = getcanonname (nip, at, name);
 			      if (canonbuf == NULL)
 				{
 				  __resolv_context_put (res_ctx);
@@ -1004,6 +1063,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
     }
 
   {
+    /* Set up the canonical name if we need it.  */
+    if ((result = process_canonname (req, orig_name, &canon)) != 0)
+      goto free_and_return;
+
     struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
     size_t socklen;
@@ -1014,48 +1077,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
      */
     while (at2 != NULL)
       {
-	/* Only the first entry gets the canonical name.  */
-	if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
-	  {
-	    if (canon == NULL)
-	      /* If the canonical name cannot be determined, use
-		 the passed in string.  */
-	      canon = orig_name;
-
-	    bool do_idn = req->ai_flags & AI_CANONIDN;
-	    if (do_idn)
-	      {
-		char *out;
-		int rc = __idna_from_dns_encoding (canon, &out);
-		if (rc == 0)
-		  canon = out;
-		else if (rc == EAI_IDN_ENCODE)
-		  /* Use the punycode name as a fallback.  */
-		  do_idn = false;
-		else
-		  {
-		    result = -rc;
-		    goto free_and_return;
-		  }
-	      }
-	    if (!do_idn)
-	      {
-		if (canonbuf != NULL)
-		  /* We already allocated the string using malloc, but
-		     the buffer is now owned by canon.  */
-		  canonbuf = NULL;
-		else
-		  {
-		    canon = __strdup (canon);
-		    if (canon == NULL)
-		      {
-			result = -EAI_MEMORY;
-			goto free_and_return;
-		      }
-		  }
-	      }
-	  }
-
 	family = at2->family;
 	if (family == AF_INET6)
 	  {
@@ -1078,7 +1099,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
 	    if (ai == NULL)
 	      {
-		free ((char *) canon);
 		result = -EAI_MEMORY;
 		goto free_and_return;
 	      }
@@ -1138,7 +1158,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-  free (canonbuf);
+  free (canon);
 
   return result;
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852]
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
                     ` (8 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Use realloc in convert_hostent_to_gaih_addrtuple and fix up pointers in
the result list so that a single block is maintained for
hostbyname3_r/hostbyname2_r and freed in gaih_inet.  This result is
never merged with any other results, since the hosts database does not
permit merging.

Resolves BZ #28852.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 0629fd147b..e9deb2da6a 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -189,19 +189,16 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
   return 0;
 }
 
-/* Convert struct hostent to a list of struct gaih_addrtuple objects.
-   h_name is not copied, and the struct hostent object must not be
-   deallocated prematurely.  *RESULT must be NULL or a pointer to a
-   linked-list.  The new addresses are appended at the end.  */
+/* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
+   is not copied, and the struct hostent object must not be deallocated
+   prematurely.  The new addresses are appended to the tuple array in
+   RESULT.  */
 static bool
 convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 				   int family,
 				   struct hostent *h,
 				   struct gaih_addrtuple **result)
 {
-  while (*result)
-    result = &(*result)->next;
-
   /* Count the number of addresses in h->h_addr_list.  */
   size_t count = 0;
   for (char **p = h->h_addr_list; *p != NULL; ++p)
@@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
     return true;
 
-  struct gaih_addrtuple *array = calloc (count, sizeof (*array));
+  struct gaih_addrtuple *array = *result;
+  size_t old = 0;
+
+  while (array != NULL)
+    {
+      old++;
+      array = array->next;
+    }
+
+  array = realloc (*result, (old + count) * sizeof (*array));
+
   if (array == NULL)
     return false;
 
+  *result = array;
+
+  /* Update the next pointers on reallocation.  */
+  for (size_t i = 0; i < old; i++)
+    array[i].next = array + i + 1;
+
+  array += old;
+
+  memset (array, 0, count * sizeof (*array));
+
   for (size_t i = 0; i < count; ++i)
     {
       if (family == AF_INET && req->ai_family == AF_INET6)
@@ -235,7 +252,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   array[0].name = h->h_name;
   array[count - 1].next = NULL;
 
-  *result = array;
   return true;
 }
 
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 04/12] gaih_inet: Simplify service resolution
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (2 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
                     ` (7 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Refactor the code to split out the service resolution code into a
separate function.  Allocate the service tuples array just once to the
size of the typeproto array, thus avoiding the unnecessary pointer
chasing and stack allocations.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 178 ++++++++++++++++--------------------
 1 file changed, 78 insertions(+), 100 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index e9deb2da6a..dae5e9f55f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -100,14 +100,12 @@ struct gaih_service
 
 struct gaih_servtuple
   {
-    struct gaih_servtuple *next;
     int socktype;
     int protocol;
     int port;
+    bool set;
   };
 
-static const struct gaih_servtuple nullserv;
-
 
 struct gaih_typeproto
   {
@@ -180,11 +178,11 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
     }
   while (r);
 
-  st->next = NULL;
   st->socktype = tp->socktype;
   st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
 		  ? req->ai_protocol : tp->protocol);
   st->port = s->s_port;
+  st->set = true;
 
   return 0;
 }
@@ -375,20 +373,11 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
 }
 
 static int
-gaih_inet (const char *name, const struct gaih_service *service,
-	   const struct addrinfo *req, struct addrinfo **pai,
-	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
+get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
+		struct gaih_servtuple *st, struct scratch_buffer *tmpbuf)
 {
+  int i;
   const struct gaih_typeproto *tp = gaih_inet_typeproto;
-  struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
-  struct gaih_addrtuple *at = NULL;
-  bool got_ipv6 = false;
-  char *canon = NULL;
-  const char *orig_name = name;
-
-  /* Reserve stack memory for the scratch buffer in the getaddrinfo
-     function.  */
-  size_t alloca_used = sizeof (struct scratch_buffer);
 
   if (req->ai_protocol || req->ai_socktype)
     {
@@ -410,98 +399,88 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
     }
 
-  int port = 0;
-  if (service != NULL)
+  if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+    return -EAI_SERVICE;
+
+  if (service == NULL || service->num >= 0)
     {
-      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-	return -EAI_SERVICE;
+      int port = service != NULL ? htons (service->num) : 0;
 
-      if (service->num < 0)
+      if (req->ai_socktype || req->ai_protocol)
 	{
-	  if (tp->name[0])
-	    {
-	      st = (struct gaih_servtuple *)
-		alloca_account (sizeof (struct gaih_servtuple), alloca_used);
-
-	      int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf);
-	      if (__glibc_unlikely (rc != 0))
-		return rc;
-	    }
-	  else
-	    {
-	      struct gaih_servtuple **pst = &st;
-	      for (tp++; tp->name[0]; tp++)
-		{
-		  struct gaih_servtuple *newp;
+	  st[0].socktype = tp->socktype;
+	  st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+			  ? req->ai_protocol : tp->protocol);
+	  st[0].port = port;
+	  st[0].set = true;
 
-		  if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
-		    continue;
+	  return 0;
+	}
 
-		  if (req->ai_socktype != 0
-		      && req->ai_socktype != tp->socktype)
-		    continue;
-		  if (req->ai_protocol != 0
-		      && !(tp->protoflag & GAI_PROTO_PROTOANY)
-		      && req->ai_protocol != tp->protocol)
-		    continue;
+      /* Neither socket type nor protocol is set.  Return all socket types
+	 we know about.  */
+      for (i = 0, ++tp; tp->name[0]; ++tp)
+	if (tp->defaultflag)
+	  {
+	    st[i].socktype = tp->socktype;
+	    st[i].protocol = tp->protocol;
+	    st[i].port = port;
+	    st[i++].set = true;
+	  }
 
-		  newp = (struct gaih_servtuple *)
-		    alloca_account (sizeof (struct gaih_servtuple),
-				    alloca_used);
+      return 0;
+    }
 
-		  if (gaih_inet_serv (service->name,
-				      tp, req, newp, tmpbuf) != 0)
-		    continue;
+  if (tp->name[0])
+    return gaih_inet_serv (service->name, tp, req, st, tmpbuf);
 
-		  *pst = newp;
-		  pst = &(newp->next);
-		}
-	      if (st == (struct gaih_servtuple *) &nullserv)
-		return -EAI_SERVICE;
-	    }
-	}
-      else
-	{
-	  port = htons (service->num);
-	  goto got_port;
-	}
-    }
-  else
+  for (i = 0, tp++; tp->name[0]; tp++)
     {
-    got_port:
+      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+	continue;
 
-      if (req->ai_socktype || req->ai_protocol)
-	{
-	  st = alloca_account (sizeof (struct gaih_servtuple), alloca_used);
-	  st->next = NULL;
-	  st->socktype = tp->socktype;
-	  st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
-			  ? req->ai_protocol : tp->protocol);
-	  st->port = port;
-	}
-      else
-	{
-	  /* Neither socket type nor protocol is set.  Return all socket types
-	     we know about.  */
-	  struct gaih_servtuple **lastp = &st;
-	  for (++tp; tp->name[0]; ++tp)
-	    if (tp->defaultflag)
-	      {
-		struct gaih_servtuple *newp;
+      if (req->ai_socktype != 0
+	  && req->ai_socktype != tp->socktype)
+	continue;
+      if (req->ai_protocol != 0
+	  && !(tp->protoflag & GAI_PROTO_PROTOANY)
+	  && req->ai_protocol != tp->protocol)
+	continue;
 
-		newp = alloca_account (sizeof (struct gaih_servtuple),
-				       alloca_used);
-		newp->next = NULL;
-		newp->socktype = tp->socktype;
-		newp->protocol = tp->protocol;
-		newp->port = port;
+      if (gaih_inet_serv (service->name,
+			  tp, req, &st[i], tmpbuf) != 0)
+	continue;
 
-		*lastp = newp;
-		lastp = &newp->next;
-	      }
-	}
+      i++;
     }
 
+  if (!st[0].set)
+    return -EAI_SERVICE;
+
+  return 0;
+}
+
+static int
+gaih_inet (const char *name, const struct gaih_service *service,
+	   const struct addrinfo *req, struct addrinfo **pai,
+	   unsigned int *naddrs, struct scratch_buffer *tmpbuf)
+{
+  struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
+			   / sizeof (struct gaih_typeproto)] = {0};
+
+  struct gaih_addrtuple *at = NULL;
+  bool got_ipv6 = false;
+  char *canon = NULL;
+  const char *orig_name = name;
+
+  /* Reserve stack memory for the scratch buffer in the getaddrinfo
+     function.  */
+  size_t alloca_used = sizeof (struct scratch_buffer);
+
+  int rc;
+  if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
+    return rc;
+
   bool malloc_name = false;
   struct gaih_addrtuple *addrmem = NULL;
   int result = 0;
@@ -1083,7 +1062,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
     if ((result = process_canonname (req, orig_name, &canon)) != 0)
       goto free_and_return;
 
-    struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
     size_t socklen;
     sa_family_t family;
@@ -1109,7 +1087,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	else
 	  socklen = sizeof (struct sockaddr_in);
 
-	for (st2 = st; st2 != NULL; st2 = st2->next)
+	for (int i = 0; st[i].set; i++)
 	  {
 	    struct addrinfo *ai;
 	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
@@ -1121,8 +1099,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	    ai->ai_flags = req->ai_flags;
 	    ai->ai_family = family;
-	    ai->ai_socktype = st2->socktype;
-	    ai->ai_protocol = st2->protocol;
+	    ai->ai_socktype = st[i].socktype;
+	    ai->ai_protocol = st[i].protocol;
 	    ai->ai_addrlen = socklen;
 	    ai->ai_addr = (void *) (ai + 1);
 
@@ -1144,7 +1122,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		struct sockaddr_in6 *sin6p =
 		  (struct sockaddr_in6 *) ai->ai_addr;
 
-		sin6p->sin6_port = st2->port;
+		sin6p->sin6_port = st[i].port;
 		sin6p->sin6_flowinfo = 0;
 		memcpy (&sin6p->sin6_addr,
 			at2->addr, sizeof (struct in6_addr));
@@ -1154,7 +1132,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      {
 		struct sockaddr_in *sinp =
 		  (struct sockaddr_in *) ai->ai_addr;
-		sinp->sin_port = st2->port;
+		sinp->sin_port = st[i].port;
 		memcpy (&sinp->sin_addr,
 			at2->addr, sizeof (struct in_addr));
 		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 05/12] gaih_inet: make numeric lookup a separate routine
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (3 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
                     ` (6 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Introduce the gaih_result structure and general paradigm for cleanups
that follow to process the lookup request and return a result.  A lookup
function (like text_to_binary_address), should return an integer error
code and set members of gaih_result based on what it finds.  If the
function does not have a result and no errors have occurred during the
lookup, it should return 0 and res.at should be set to NULL, allowing a
subsequent function to do the lookup until we run out of options.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 891 ++++++++++++++++++------------------
 1 file changed, 452 insertions(+), 439 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index dae5e9f55f..19bb13db59 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -116,6 +116,12 @@ struct gaih_typeproto
     char name[8];
   };
 
+struct gaih_result
+{
+  struct gaih_addrtuple *at;
+  char *canon;
+};
+
 /* Values for `protoflag'.  */
 #define GAI_PROTO_NOSERVICE	1
 #define GAI_PROTO_PROTOANY	2
@@ -297,7 +303,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	}								      \
       *pat = addrmem;							      \
 									      \
-      if (localcanon != NULL && canon == NULL)				      \
+      if (localcanon != NULL && res.canon == NULL)			      \
 	{								      \
 	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
@@ -306,7 +312,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	      result = -EAI_SYSTEM;					      \
 	      goto free_and_return;					      \
 	    }								      \
-	  canon = canonbuf;						      \
+	  res.canon = canonbuf;						      \
 	}								      \
       if (_family == AF_INET6 && *pat != NULL)				      \
 	got_ipv6 = true;						      \
@@ -342,9 +348,9 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
 
 static int
 process_canonname (const struct addrinfo *req, const char *orig_name,
-		   char **canonp)
+		   struct gaih_result *res)
 {
-  char *canon = *canonp;
+  char *canon = res->canon;
 
   if ((req->ai_flags & AI_CANONNAME) != 0)
     {
@@ -368,7 +374,7 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
 	return -EAI_MEMORY;
     }
 
-  *canonp = canon;
+  res->canon = canon;
   return 0;
 }
 
@@ -460,6 +466,105 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
   return 0;
 }
 
+/* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
+   NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
+   the function cannot determine a result, RES->AT is set to NULL and 0
+   returned.  */
+
+static int
+text_to_binary_address (const char *name, const struct addrinfo *req,
+			struct gaih_result *res)
+{
+  struct gaih_addrtuple *at = res->at;
+  int result = 0;
+
+  assert (at != NULL);
+
+  memset (at->addr, 0, sizeof (at->addr));
+  if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
+    {
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
+	at->family = AF_INET;
+      else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
+	{
+	  at->addr[3] = at->addr[0];
+	  at->addr[2] = htonl (0xffff);
+	  at->addr[1] = 0;
+	  at->addr[0] = 0;
+	  at->family = AF_INET6;
+	}
+      else
+	{
+	  result = -EAI_ADDRFAMILY;
+	  goto out;
+	}
+
+      if (req->ai_flags & AI_CANONNAME)
+	{
+	  char *canonbuf = __strdup (name);
+	  if (canonbuf == NULL)
+	    {
+	      result = -EAI_MEMORY;
+	      goto out;
+	    }
+	  res->canon = canonbuf;
+	}
+      return 0;
+    }
+
+  char *scope_delim = strchr (name, SCOPE_DELIMITER);
+  int e;
+
+  if (scope_delim == NULL)
+    e = inet_pton (AF_INET6, name, at->addr);
+  else
+    e = __inet_pton_length (AF_INET6, name, scope_delim - name, at->addr);
+
+  if (e > 0)
+    {
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+	at->family = AF_INET6;
+      else if (req->ai_family == AF_INET
+	       && IN6_IS_ADDR_V4MAPPED (at->addr))
+	{
+	  at->addr[0] = at->addr[3];
+	  at->family = AF_INET;
+	}
+      else
+	{
+	  result = -EAI_ADDRFAMILY;
+	  goto out;
+	}
+
+      if (scope_delim != NULL
+	  && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+				   scope_delim + 1, &at->scopeid) != 0)
+	{
+	  result = -EAI_NONAME;
+	  goto out;
+	}
+
+      if (req->ai_flags & AI_CANONNAME)
+	{
+	  char *canonbuf = __strdup (name);
+	  if (canonbuf == NULL)
+	    {
+	      result = -EAI_MEMORY;
+	      goto out;
+	    }
+	  res->canon = canonbuf;
+	}
+      return 0;
+    }
+
+  if ((req->ai_flags & AI_NUMERICHOST))
+    result = -EAI_NONAME;
+
+out:
+  res->at = NULL;
+  return result;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -468,9 +573,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
 			   / sizeof (struct gaih_typeproto)] = {0};
 
-  struct gaih_addrtuple *at = NULL;
   bool got_ipv6 = false;
-  char *canon = NULL;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -485,6 +588,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_addrtuple *addrmem = NULL;
   int result = 0;
 
+  struct gaih_result res = {0};
   if (name != NULL)
     {
       if (req->ai_flags & AI_IDN)
@@ -497,533 +601,441 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      uint32_t addr[4];
-      if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
+      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
+      res.at->scopeid = 0;
+      res.at->next = NULL;
+
+      if ((result = text_to_binary_address (name, req, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+
+      int no_data = 0;
+      int no_inet6_data = 0;
+      nss_action_list nip;
+      enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+      enum nss_status status = NSS_STATUS_UNAVAIL;
+      int no_more;
+      struct resolv_context *res_ctx = NULL;
+      bool do_merge = false;
+
+      /* If we do not have to look for IPv6 addresses or the canonical
+	 name, use the simple, old functions, which do not support
+	 IPv6 scope ids, nor retrieving the canonical name.  */
+      if (req->ai_family == AF_INET
+	  && (req->ai_flags & AI_CANONNAME) == 0)
 	{
-	  at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-	  at->scopeid = 0;
-	  at->next = NULL;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    {
-	      memcpy (at->addr, addr, sizeof (at->addr));
-	      at->family = AF_INET;
-	    }
-	  else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
-	    {
-	      at->addr[3] = addr[0];
-	      at->addr[2] = htonl (0xffff);
-	      at->addr[1] = 0;
-	      at->addr[0] = 0;
-	      at->family = AF_INET6;
-	    }
-	  else
-	    {
-	      result = -EAI_ADDRFAMILY;
-	      goto free_and_return;
-	    }
+	  int rc;
+	  struct hostent th;
+	  struct hostent *h;
 
-	  if (req->ai_flags & AI_CANONNAME)
+	  while (1)
 	    {
-	      char *canonbuf = __strdup (name);
-	      if (canonbuf == NULL)
+	      rc = __gethostbyname2_r (name, AF_INET, &th,
+				       tmpbuf->data, tmpbuf->length,
+				       &h, &h_errno);
+	      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
+		break;
+	      if (!scratch_buffer_grow (tmpbuf))
 		{
 		  result = -EAI_MEMORY;
 		  goto free_and_return;
 		}
-	      canon = canonbuf;
 	    }
 
-	  goto process_list;
-	}
-
-      char *scope_delim = strchr (name, SCOPE_DELIMITER);
-      int e;
-
-      if (scope_delim == NULL)
-	e = inet_pton (AF_INET6, name, addr);
-      else
-	e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
-
-      if (e > 0)
-	{
-	  at = alloca_account (sizeof (struct gaih_addrtuple),
-			       alloca_used);
-	  at->scopeid = 0;
-	  at->next = NULL;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	    {
-	      memcpy (at->addr, addr, sizeof (at->addr));
-	      at->family = AF_INET6;
-	    }
-	  else if (req->ai_family == AF_INET
-		   && IN6_IS_ADDR_V4MAPPED (addr))
+	  if (rc == 0)
 	    {
-	      at->addr[0] = addr[3];
-	      at->addr[1] = addr[1];
-	      at->addr[2] = addr[2];
-	      at->addr[3] = addr[3];
-	      at->family = AF_INET;
+	      if (h != NULL)
+		{
+		  /* We found data, convert it.  */
+		  if (!convert_hostent_to_gaih_addrtuple
+		      (req, AF_INET, h, &addrmem))
+		    {
+		      result = -EAI_MEMORY;
+		      goto free_and_return;
+		    }
+		  res.at = addrmem;
+		}
+	      else
+		{
+		  if (h_errno == NO_DATA)
+		    result = -EAI_NODATA;
+		  else
+		    result = -EAI_NONAME;
+		  goto free_and_return;
+		}
 	    }
 	  else
 	    {
-	      result = -EAI_ADDRFAMILY;
-	      goto free_and_return;
-	    }
+	      if (h_errno == NETDB_INTERNAL)
+		result = -EAI_SYSTEM;
+	      else if (h_errno == TRY_AGAIN)
+		result = -EAI_AGAIN;
+	      else
+		/* We made requests but they turned out no data.
+		   The name is known, though.  */
+		result = -EAI_NODATA;
 
-	  if (scope_delim != NULL
-	      && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
-				       scope_delim + 1,
-				       &at->scopeid) != 0)
-	    {
-	      result = -EAI_NONAME;
 	      goto free_and_return;
 	    }
 
-	  if (req->ai_flags & AI_CANONNAME)
+	  goto process_list;
+	}
+
+#ifdef USE_NSCD
+      if (__nss_not_use_nscd_hosts > 0
+	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+	__nss_not_use_nscd_hosts = 0;
+
+      if (!__nss_not_use_nscd_hosts
+	  && !__nss_database_custom[NSS_DBSIDX_hosts])
+	{
+	  /* Try to use nscd.  */
+	  struct nscd_ai_result *air = NULL;
+	  int err = __nscd_getai (name, &air, &h_errno);
+	  if (air != NULL)
 	    {
-	      char *canonbuf = __strdup (name);
-	      if (canonbuf == NULL)
+	      /* Transform into gaih_addrtuple list.  */
+	      bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
+	      char *addrs = air->addrs;
+
+	      addrmem = calloc (air->naddrs, sizeof (*addrmem));
+	      if (addrmem == NULL)
 		{
 		  result = -EAI_MEMORY;
 		  goto free_and_return;
 		}
-	      canon = canonbuf;
-	    }
 
-	  goto process_list;
-	}
-
-      if ((req->ai_flags & AI_NUMERICHOST) == 0)
-	{
-	  int no_data = 0;
-	  int no_inet6_data = 0;
-	  nss_action_list nip;
-	  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
-	  enum nss_status status = NSS_STATUS_UNAVAIL;
-	  int no_more;
-	  struct resolv_context *res_ctx = NULL;
-	  bool do_merge = false;
-
-	  /* If we do not have to look for IPv6 addresses or the canonical
-	     name, use the simple, old functions, which do not support
-	     IPv6 scope ids, nor retrieving the canonical name.  */
-	  if (req->ai_family == AF_INET
-	      && (req->ai_flags & AI_CANONNAME) == 0)
-	    {
-	      int rc;
-	      struct hostent th;
-	      struct hostent *h;
+	      struct gaih_addrtuple *addrfree = addrmem;
+	      struct gaih_addrtuple **pat = &res.at;
 
-	      while (1)
+	      for (int i = 0; i < air->naddrs; ++i)
 		{
-		  rc = __gethostbyname2_r (name, AF_INET, &th,
-					   tmpbuf->data, tmpbuf->length,
-					   &h, &h_errno);
-		  if (rc != ERANGE || h_errno != NETDB_INTERNAL)
-		    break;
-		  if (!scratch_buffer_grow (tmpbuf))
+		  socklen_t size = (air->family[i] == AF_INET
+				    ? INADDRSZ : IN6ADDRSZ);
+
+		  if (!((air->family[i] == AF_INET
+			 && req->ai_family == AF_INET6
+			 && (req->ai_flags & AI_V4MAPPED) != 0)
+			|| req->ai_family == AF_UNSPEC
+			|| air->family[i] == req->ai_family))
 		    {
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
+		      /* Skip over non-matching result.  */
+		      addrs += size;
+		      continue;
 		    }
-		}
 
-	      if (rc == 0)
-		{
-		  if (h != NULL)
+		  if (*pat == NULL)
+		    {
+		      *pat = addrfree++;
+		      (*pat)->scopeid = 0;
+		    }
+		  uint32_t *pataddr = (*pat)->addr;
+		  (*pat)->next = NULL;
+		  if (added_canon || air->canon == NULL)
+		    (*pat)->name = NULL;
+		  else if (res.canon == NULL)
 		    {
-		      /* We found data, convert it.  */
-		      if (!convert_hostent_to_gaih_addrtuple
-			  (req, AF_INET, h, &addrmem))
+		      char *canonbuf = __strdup (air->canon);
+		      if (canonbuf == NULL)
 			{
 			  result = -EAI_MEMORY;
 			  goto free_and_return;
 			}
-		      at = addrmem;
+		      res.canon = (*pat)->name = canonbuf;
 		    }
-		  else
+
+		  if (air->family[i] == AF_INET
+		      && req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED))
 		    {
-		      if (h_errno == NO_DATA)
-			result = -EAI_NODATA;
-		      else
-			result = -EAI_NONAME;
-		      goto free_and_return;
+		      (*pat)->family = AF_INET6;
+		      pataddr[3] = *(uint32_t *) addrs;
+		      pataddr[2] = htonl (0xffff);
+		      pataddr[1] = 0;
+		      pataddr[0] = 0;
+		      pat = &((*pat)->next);
+		      added_canon = true;
+		    }
+		  else if (req->ai_family == AF_UNSPEC
+			   || air->family[i] == req->ai_family)
+		    {
+		      (*pat)->family = air->family[i];
+		      memcpy (pataddr, addrs, size);
+		      pat = &((*pat)->next);
+		      added_canon = true;
+		      if (air->family[i] == AF_INET6)
+			got_ipv6 = true;
 		    }
+		  addrs += size;
 		}
-	      else
-		{
-		  if (h_errno == NETDB_INTERNAL)
-		    result = -EAI_SYSTEM;
-		  else if (h_errno == TRY_AGAIN)
-		    result = -EAI_AGAIN;
-		  else
-		    /* We made requests but they turned out no data.
-		       The name is known, though.  */
-		    result = -EAI_NODATA;
 
-		  goto free_and_return;
-		}
+	      free (air);
 
 	      goto process_list;
 	    }
+	  else if (err == 0)
+	    /* The database contains a negative entry.  */
+	    goto free_and_return;
+	  else if (__nss_not_use_nscd_hosts == 0)
+	    {
+	      if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+		result = -EAI_MEMORY;
+	      else if (h_errno == TRY_AGAIN)
+		result = -EAI_AGAIN;
+	      else
+		result = -EAI_SYSTEM;
 
-#ifdef USE_NSCD
-	  if (__nss_not_use_nscd_hosts > 0
-	      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-	    __nss_not_use_nscd_hosts = 0;
+	      goto free_and_return;
+	    }
+	}
+#endif
+
+      no_more = !__nss_database_get (nss_database_hosts, &nip);
 
-	  if (!__nss_not_use_nscd_hosts
-	      && !__nss_database_custom[NSS_DBSIDX_hosts])
+      /* If we are looking for both IPv4 and IPv6 address we don't
+	 want the lookup functions to automatically promote IPv4
+	 addresses to IPv6 addresses, so we use the no_inet6
+	 function variant.  */
+      res_ctx = __resolv_context_get ();
+      if (res_ctx == NULL)
+	no_more = 1;
+
+      while (!no_more)
+	{
+	  /* Always start afresh; continue should discard previous results
+	     and the hosts database does not support merge.  */
+	  res.at = NULL;
+	  free (res.canon);
+	  free (addrmem);
+	  res.canon = NULL;
+	  addrmem = NULL;
+	  got_ipv6 = false;
+
+	  if (do_merge)
 	    {
-	      /* Try to use nscd.  */
-	      struct nscd_ai_result *air = NULL;
-	      int err = __nscd_getai (name, &air, &h_errno);
-	      if (air != NULL)
+	      __set_h_errno (NETDB_INTERNAL);
+	      __set_errno (EBUSY);
+	      break;
+	    }
+
+	  no_data = 0;
+	  nss_gethostbyname4_r *fct4 = NULL;
+
+	  /* gethostbyname4_r sends out parallel A and AAAA queries and
+	     is thus only suitable for PF_UNSPEC.  */
+	  if (req->ai_family == PF_UNSPEC)
+	    fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+
+	  if (fct4 != NULL)
+	    {
+	      while (1)
 		{
-		  /* Transform into gaih_addrtuple list.  */
-		  bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
-		  char *addrs = air->addrs;
+		  status = DL_CALL_FCT (fct4, (name, &res.at,
+					       tmpbuf->data, tmpbuf->length,
+					       &errno, &h_errno,
+					       NULL));
+		  if (status == NSS_STATUS_SUCCESS)
+		    break;
+		  /* gethostbyname4_r may write into AT, so reset it.  */
+		  res.at = NULL;
+		  if (status != NSS_STATUS_TRYAGAIN
+		      || errno != ERANGE || h_errno != NETDB_INTERNAL)
+		    {
+		      if (h_errno == TRY_AGAIN)
+			no_data = EAI_AGAIN;
+		      else
+			no_data = h_errno == NO_DATA;
+		      break;
+		    }
 
-		  addrmem = calloc (air->naddrs, sizeof (*addrmem));
-		  if (addrmem == NULL)
+		  if (!scratch_buffer_grow (tmpbuf))
 		    {
+		      __resolv_context_put (res_ctx);
 		      result = -EAI_MEMORY;
 		      goto free_and_return;
 		    }
+		}
 
-		  struct gaih_addrtuple *addrfree = addrmem;
-		  struct gaih_addrtuple **pat = &at;
+	      if (status == NSS_STATUS_SUCCESS)
+		{
+		  assert (!no_data);
+		  no_data = 1;
 
-		  for (int i = 0; i < air->naddrs; ++i)
+		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
 		    {
-		      socklen_t size = (air->family[i] == AF_INET
-					? INADDRSZ : IN6ADDRSZ);
-
-		      if (!((air->family[i] == AF_INET
-			     && req->ai_family == AF_INET6
-			     && (req->ai_flags & AI_V4MAPPED) != 0)
-			    || req->ai_family == AF_UNSPEC
-			    || air->family[i] == req->ai_family))
+		      char *canonbuf = __strdup (res.at->name);
+		      if (canonbuf == NULL)
 			{
-			  /* Skip over non-matching result.  */
-			  addrs += size;
-			  continue;
+			  __resolv_context_put (res_ctx);
+			  result = -EAI_MEMORY;
+			  goto free_and_return;
 			}
+		      res.canon = canonbuf;
+		    }
 
-		      if (*pat == NULL)
-			{
-			  *pat = addrfree++;
-			  (*pat)->scopeid = 0;
-			}
-		      uint32_t *pataddr = (*pat)->addr;
-		      (*pat)->next = NULL;
-		      if (added_canon || air->canon == NULL)
-			(*pat)->name = NULL;
-		      else if (canon == NULL)
-			{
-			  char *canonbuf = __strdup (air->canon);
-			  if (canonbuf == NULL)
-			    {
-			      result = -EAI_MEMORY;
-			      goto free_and_return;
-			    }
-			  canon = (*pat)->name = canonbuf;
-			}
+		  struct gaih_addrtuple **pat = &res.at;
 
-		      if (air->family[i] == AF_INET
+		  while (*pat != NULL)
+		    {
+		      if ((*pat)->family == AF_INET
 			  && req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED))
+			  && (req->ai_flags & AI_V4MAPPED) != 0)
 			{
+			  uint32_t *pataddr = (*pat)->addr;
 			  (*pat)->family = AF_INET6;
-			  pataddr[3] = *(uint32_t *) addrs;
+			  pataddr[3] = pataddr[0];
 			  pataddr[2] = htonl (0xffff);
 			  pataddr[1] = 0;
 			  pataddr[0] = 0;
 			  pat = &((*pat)->next);
-			  added_canon = true;
+			  no_data = 0;
 			}
 		      else if (req->ai_family == AF_UNSPEC
-			       || air->family[i] == req->ai_family)
+			       || (*pat)->family == req->ai_family)
 			{
-			  (*pat)->family = air->family[i];
-			  memcpy (pataddr, addrs, size);
 			  pat = &((*pat)->next);
-			  added_canon = true;
-			  if (air->family[i] == AF_INET6)
+
+			  no_data = 0;
+			  if (req->ai_family == AF_INET6)
 			    got_ipv6 = true;
 			}
-		      addrs += size;
+		      else
+			*pat = ((*pat)->next);
 		    }
-
-		  free (air);
-
-		  goto process_list;
 		}
-	      else if (err == 0)
-		/* The database contains a negative entry.  */
-		goto free_and_return;
-	      else if (__nss_not_use_nscd_hosts == 0)
-		{
-		  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-		    result = -EAI_MEMORY;
-		  else if (h_errno == TRY_AGAIN)
-		    result = -EAI_AGAIN;
-		  else
-		    result = -EAI_SYSTEM;
 
-		  goto free_and_return;
-		}
+	      no_inet6_data = no_data;
 	    }
-#endif
-
-	  no_more = !__nss_database_get (nss_database_hosts, &nip);
-
-	  /* If we are looking for both IPv4 and IPv6 address we don't
-	     want the lookup functions to automatically promote IPv4
-	     addresses to IPv6 addresses, so we use the no_inet6
-	     function variant.  */
-	  res_ctx = __resolv_context_get ();
-	  if (res_ctx == NULL)
-	    no_more = 1;
-
-	  while (!no_more)
+	  else
 	    {
-	      /* Always start afresh; continue should discard previous results
-		 and the hosts database does not support merge.  */
-	      at = NULL;
-	      free (canon);
-	      free (addrmem);
-	      canon = NULL;
-	      addrmem = NULL;
-	      got_ipv6 = false;
-
-	      if (do_merge)
+	      nss_gethostbyname3_r *fct = NULL;
+	      if (req->ai_flags & AI_CANONNAME)
+		/* No need to use this function if we do not look for
+		   the canonical name.  The function does not exist in
+		   all NSS modules and therefore the lookup would
+		   often fail.  */
+		fct = __nss_lookup_function (nip, "gethostbyname3_r");
+	      if (fct == NULL)
+		/* We are cheating here.  The gethostbyname2_r
+		   function does not have the same interface as
+		   gethostbyname3_r but the extra arguments the
+		   latter takes are added at the end.  So the
+		   gethostbyname2_r code will just ignore them.  */
+		fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	      if (fct != NULL)
 		{
-		  __set_h_errno (NETDB_INTERNAL);
-		  __set_errno (EBUSY);
-		  break;
-		}
-
-	      no_data = 0;
-	      nss_gethostbyname4_r *fct4 = NULL;
-
-	      /* gethostbyname4_r sends out parallel A and AAAA queries and
-		 is thus only suitable for PF_UNSPEC.  */
-	      if (req->ai_family == PF_UNSPEC)
-		fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+		  struct gaih_addrtuple **pat = &res.at;
 
-	      if (fct4 != NULL)
-		{
-		  while (1)
+		  if (req->ai_family == AF_INET6
+		      || req->ai_family == AF_UNSPEC)
 		    {
-		      status = DL_CALL_FCT (fct4, (name, &at,
-						   tmpbuf->data, tmpbuf->length,
-						   &errno, &h_errno,
-						   NULL));
-		      if (status == NSS_STATUS_SUCCESS)
-			break;
-		      /* gethostbyname4_r may write into AT, so reset it.  */
-		      at = NULL;
-		      if (status != NSS_STATUS_TRYAGAIN
-			  || errno != ERANGE || h_errno != NETDB_INTERNAL)
-			{
-			  if (h_errno == TRY_AGAIN)
-			    no_data = EAI_AGAIN;
-			  else
-			    no_data = h_errno == NO_DATA;
-			  break;
-			}
+		      gethosts (AF_INET6);
+		      no_inet6_data = no_data;
+		      inet6_status = status;
+		    }
+		  if (req->ai_family == AF_INET
+		      || req->ai_family == AF_UNSPEC
+		      || (req->ai_family == AF_INET6
+			  && (req->ai_flags & AI_V4MAPPED)
+			  /* Avoid generating the mapped addresses if we
+			     know we are not going to need them.  */
+			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+		    {
+		      gethosts (AF_INET);
 
-		      if (!scratch_buffer_grow (tmpbuf))
+		      if (req->ai_family == AF_INET)
 			{
-			  __resolv_context_put (res_ctx);
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
+			  no_inet6_data = no_data;
+			  inet6_status = status;
 			}
 		    }
 
-		  if (status == NSS_STATUS_SUCCESS)
+		  /* If we found one address for AF_INET or AF_INET6,
+		     don't continue the search.  */
+		  if (inet6_status == NSS_STATUS_SUCCESS
+		      || status == NSS_STATUS_SUCCESS)
 		    {
-		      assert (!no_data);
-		      no_data = 1;
-
-		      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
+		      if ((req->ai_flags & AI_CANONNAME) != 0
+			  && res.canon == NULL)
 			{
-			  char *canonbuf = __strdup (at->name);
+			  char *canonbuf = getcanonname (nip, res.at, name);
 			  if (canonbuf == NULL)
 			    {
 			      __resolv_context_put (res_ctx);
 			      result = -EAI_MEMORY;
 			      goto free_and_return;
 			    }
-			  canon = canonbuf;
-			}
-
-		      struct gaih_addrtuple **pat = &at;
-
-		      while (*pat != NULL)
-			{
-			  if ((*pat)->family == AF_INET
-			      && req->ai_family == AF_INET6
-			      && (req->ai_flags & AI_V4MAPPED) != 0)
-			    {
-			      uint32_t *pataddr = (*pat)->addr;
-			      (*pat)->family = AF_INET6;
-			      pataddr[3] = pataddr[0];
-			      pataddr[2] = htonl (0xffff);
-			      pataddr[1] = 0;
-			      pataddr[0] = 0;
-			      pat = &((*pat)->next);
-			      no_data = 0;
-			    }
-			  else if (req->ai_family == AF_UNSPEC
-				   || (*pat)->family == req->ai_family)
-			    {
-			      pat = &((*pat)->next);
-
-			      no_data = 0;
-			      if (req->ai_family == AF_INET6)
-				got_ipv6 = true;
-			    }
-			  else
-			    *pat = ((*pat)->next);
-			}
-		    }
-
-		  no_inet6_data = no_data;
-		}
-	      else
-		{
-		  nss_gethostbyname3_r *fct = NULL;
-		  if (req->ai_flags & AI_CANONNAME)
-		    /* No need to use this function if we do not look for
-		       the canonical name.  The function does not exist in
-		       all NSS modules and therefore the lookup would
-		       often fail.  */
-		    fct = __nss_lookup_function (nip, "gethostbyname3_r");
-		  if (fct == NULL)
-		    /* We are cheating here.  The gethostbyname2_r
-		       function does not have the same interface as
-		       gethostbyname3_r but the extra arguments the
-		       latter takes are added at the end.  So the
-		       gethostbyname2_r code will just ignore them.  */
-		    fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-		  if (fct != NULL)
-		    {
-		      struct gaih_addrtuple **pat = &at;
-
-		      if (req->ai_family == AF_INET6
-			  || req->ai_family == AF_UNSPEC)
-			{
-			  gethosts (AF_INET6);
-			  no_inet6_data = no_data;
-			  inet6_status = status;
-			}
-		      if (req->ai_family == AF_INET
-			  || req->ai_family == AF_UNSPEC
-			  || (req->ai_family == AF_INET6
-			      && (req->ai_flags & AI_V4MAPPED)
-			      /* Avoid generating the mapped addresses if we
-				 know we are not going to need them.  */
-			      && ((req->ai_flags & AI_ALL) || !got_ipv6)))
-			{
-			  gethosts (AF_INET);
-
-			  if (req->ai_family == AF_INET)
-			    {
-			      no_inet6_data = no_data;
-			      inet6_status = status;
-			    }
-			}
-
-		      /* If we found one address for AF_INET or AF_INET6,
-			 don't continue the search.  */
-		      if (inet6_status == NSS_STATUS_SUCCESS
-			  || status == NSS_STATUS_SUCCESS)
-			{
-			  if ((req->ai_flags & AI_CANONNAME) != 0
-			      && canon == NULL)
-			    {
-			      char *canonbuf = getcanonname (nip, at, name);
-			      if (canonbuf == NULL)
-				{
-				  __resolv_context_put (res_ctx);
-				  result = -EAI_MEMORY;
-				  goto free_and_return;
-				}
-			      canon = canonbuf;
-			    }
-			  status = NSS_STATUS_SUCCESS;
-			}
-		      else
-			{
-			  /* We can have different states for AF_INET and
-			     AF_INET6.  Try to find a useful one for both.  */
-			  if (inet6_status == NSS_STATUS_TRYAGAIN)
-			    status = NSS_STATUS_TRYAGAIN;
-			  else if (status == NSS_STATUS_UNAVAIL
-				   && inet6_status != NSS_STATUS_UNAVAIL)
-			    status = inet6_status;
+			  res.canon = canonbuf;
 			}
+		      status = NSS_STATUS_SUCCESS;
 		    }
 		  else
 		    {
-		      /* Could not locate any of the lookup functions.
-			 The NSS lookup code does not consistently set
-			 errno, so we need to supply our own error
-			 code here.  The root cause could either be a
-			 resource allocation failure, or a missing
-			 service function in the DSO (so it should not
-			 be listed in /etc/nsswitch.conf).  Assume the
-			 former, and return EBUSY.  */
-		      status = NSS_STATUS_UNAVAIL;
-		     __set_h_errno (NETDB_INTERNAL);
-		     __set_errno (EBUSY);
+		      /* We can have different states for AF_INET and
+			 AF_INET6.  Try to find a useful one for both.  */
+		      if (inet6_status == NSS_STATUS_TRYAGAIN)
+			status = NSS_STATUS_TRYAGAIN;
+		      else if (status == NSS_STATUS_UNAVAIL
+			       && inet6_status != NSS_STATUS_UNAVAIL)
+			status = inet6_status;
 		    }
 		}
+	      else
+		{
+		  /* Could not locate any of the lookup functions.
+		     The NSS lookup code does not consistently set
+		     errno, so we need to supply our own error
+		     code here.  The root cause could either be a
+		     resource allocation failure, or a missing
+		     service function in the DSO (so it should not
+		     be listed in /etc/nsswitch.conf).  Assume the
+		     former, and return EBUSY.  */
+		  status = NSS_STATUS_UNAVAIL;
+		  __set_h_errno (NETDB_INTERNAL);
+		  __set_errno (EBUSY);
+		}
+	    }
 
-	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-		break;
+	  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	    break;
 
-	      /* The hosts database does not support MERGE.  */
-	      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
-		do_merge = true;
+	  /* The hosts database does not support MERGE.  */
+	  if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+	    do_merge = true;
 
-	      nip++;
-	      if (nip->module == NULL)
-		no_more = -1;
-	    }
+	  nip++;
+	  if (nip->module == NULL)
+	    no_more = -1;
+	}
 
-	  __resolv_context_put (res_ctx);
+      __resolv_context_put (res_ctx);
 
-	  /* If we have a failure which sets errno, report it using
-	     EAI_SYSTEM.  */
-	  if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
-	      && h_errno == NETDB_INTERNAL)
-	    {
-	      result = -EAI_SYSTEM;
-	      goto free_and_return;
-	    }
+      /* If we have a failure which sets errno, report it using
+	 EAI_SYSTEM.  */
+      if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+	  && h_errno == NETDB_INTERNAL)
+	{
+	  result = -EAI_SYSTEM;
+	  goto free_and_return;
+	}
 
-	  if (no_data != 0 && no_inet6_data != 0)
-	    {
-	      /* If both requests timed out report this.  */
-	      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		/* We made requests but they turned out no data.  The name
-		   is known, though.  */
-		result = -EAI_NODATA;
+      if (no_data != 0 && no_inet6_data != 0)
+	{
+	  /* If both requests timed out report this.  */
+	  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+	    result = -EAI_AGAIN;
+	  else
+	    /* We made requests but they turned out no data.  The name
+	       is known, though.  */
+	    result = -EAI_NODATA;
 
-	      goto free_and_return;
-	    }
+	  goto free_and_return;
 	}
 
     process_list:
-      if (at == NULL)
+      if (res.at == NULL)
 	{
 	  result = -EAI_NONAME;
 	  goto free_and_return;
@@ -1032,21 +1044,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
   else
     {
       struct gaih_addrtuple *atr;
-      atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      memset (at, '\0', sizeof (struct gaih_addrtuple));
+      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
+				     alloca_used);
+      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
 
       if (req->ai_family == AF_UNSPEC)
 	{
-	  at->next = __alloca (sizeof (struct gaih_addrtuple));
-	  memset (at->next, '\0', sizeof (struct gaih_addrtuple));
+	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
+	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
 	}
 
       if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
 	{
-	  at->family = AF_INET6;
+	  res.at->family = AF_INET6;
 	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
-	  atr = at->next;
+	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
+	  atr = res.at->next;
 	}
 
       if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
@@ -1059,10 +1072,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   {
     /* Set up the canonical name if we need it.  */
-    if ((result = process_canonname (req, orig_name, &canon)) != 0)
+    if ((result = process_canonname (req, orig_name, &res)) != 0)
       goto free_and_return;
 
-    struct gaih_addrtuple *at2 = at;
+    struct gaih_addrtuple *at2 = res.at;
     size_t socklen;
     sa_family_t family;
 
@@ -1105,8 +1118,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    ai->ai_addr = (void *) (ai + 1);
 
 	    /* We only add the canonical name once.  */
-	    ai->ai_canonname = (char *) canon;
-	    canon = NULL;
+	    ai->ai_canonname = res.canon;
+	    res.canon = NULL;
 
 #ifdef _HAVE_SA_LEN
 	    ai->ai_addr->sa_len = socklen;
@@ -1152,7 +1165,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-  free (canon);
+  free (res.canon);
 
   return result;
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 06/12] gaih_inet: Split simple gethostbyname into its own function
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (4 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
                     ` (5 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Add a free_at flag in gaih_result to indicate if res.at needs to be
freed by the caller.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 127 ++++++++++++++++++------------------
 1 file changed, 64 insertions(+), 63 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 19bb13db59..1137c959ac 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -120,6 +120,7 @@ struct gaih_result
 {
   struct gaih_addrtuple *at;
   char *canon;
+  bool free_at;
 };
 
 /* Values for `protoflag'.  */
@@ -565,6 +566,62 @@ out:
   return result;
 }
 
+/* If possible, call the simple, old functions, which do not support IPv6 scope
+   ids, nor retrieving the canonical name.  */
+
+static int
+try_simple_gethostbyname (const char *name, const struct addrinfo *req,
+			  struct scratch_buffer *tmpbuf,
+			  struct gaih_result *res)
+{
+  res->at = NULL;
+
+  if (req->ai_family != AF_INET || (req->ai_flags & AI_CANONNAME) != 0)
+    return 0;
+
+  int rc;
+  struct hostent th;
+  struct hostent *h;
+
+  while (1)
+    {
+      rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf->data,
+			       tmpbuf->length, &h, &h_errno);
+      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
+	break;
+      if (!scratch_buffer_grow (tmpbuf))
+	return -EAI_MEMORY;
+    }
+
+  if (rc == 0)
+    {
+      if (h != NULL)
+	{
+	  /* We found data, convert it.  RES->AT from the conversion will
+	     either be an allocated block or NULL, both of which are safe to
+	     pass to free ().  */
+	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
+	    return -EAI_MEMORY;
+
+	  res->free_at = true;
+	  return 0;
+	}
+      if (h_errno == NO_DATA)
+	return -EAI_NODATA;
+
+      return -EAI_NONAME;
+    }
+
+  if (h_errno == NETDB_INTERNAL)
+    return -EAI_SYSTEM;
+  if (h_errno == TRY_AGAIN)
+    return -EAI_AGAIN;
+
+  /* We made requests but they turned out no data.
+     The name is known, though.  */
+  return -EAI_NODATA;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -610,6 +667,11 @@ gaih_inet (const char *name, const struct gaih_service *service,
       else if (res.at != NULL)
 	goto process_list;
 
+      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+
       int no_data = 0;
       int no_inet6_data = 0;
       nss_action_list nip;
@@ -619,69 +681,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
       struct resolv_context *res_ctx = NULL;
       bool do_merge = false;
 
-      /* If we do not have to look for IPv6 addresses or the canonical
-	 name, use the simple, old functions, which do not support
-	 IPv6 scope ids, nor retrieving the canonical name.  */
-      if (req->ai_family == AF_INET
-	  && (req->ai_flags & AI_CANONNAME) == 0)
-	{
-	  int rc;
-	  struct hostent th;
-	  struct hostent *h;
-
-	  while (1)
-	    {
-	      rc = __gethostbyname2_r (name, AF_INET, &th,
-				       tmpbuf->data, tmpbuf->length,
-				       &h, &h_errno);
-	      if (rc != ERANGE || h_errno != NETDB_INTERNAL)
-		break;
-	      if (!scratch_buffer_grow (tmpbuf))
-		{
-		  result = -EAI_MEMORY;
-		  goto free_and_return;
-		}
-	    }
-
-	  if (rc == 0)
-	    {
-	      if (h != NULL)
-		{
-		  /* We found data, convert it.  */
-		  if (!convert_hostent_to_gaih_addrtuple
-		      (req, AF_INET, h, &addrmem))
-		    {
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
-		    }
-		  res.at = addrmem;
-		}
-	      else
-		{
-		  if (h_errno == NO_DATA)
-		    result = -EAI_NODATA;
-		  else
-		    result = -EAI_NONAME;
-		  goto free_and_return;
-		}
-	    }
-	  else
-	    {
-	      if (h_errno == NETDB_INTERNAL)
-		result = -EAI_SYSTEM;
-	      else if (h_errno == TRY_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		/* We made requests but they turned out no data.
-		   The name is known, though.  */
-		result = -EAI_NODATA;
-
-	      goto free_and_return;
-	    }
-
-	  goto process_list;
-	}
-
 #ifdef USE_NSCD
       if (__nss_not_use_nscd_hosts > 0
 	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
@@ -1165,6 +1164,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
+  if (res.free_at)
+    free (res.at);
   free (res.canon);
 
   return result;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 07/12] gaih_inet: Split nscd lookup code into its own function.
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (5 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17 22:02     ` DJ Delorie
  2022-03-17  8:11   ` [PATCH v3 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
                     ` (4 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Add a new member got_ipv6 to indicate if the results have an IPv6
result and use it instead of the local got_ipv6.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 248 +++++++++++++++++++-----------------
 1 file changed, 134 insertions(+), 114 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 1137c959ac..01be932b3f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -121,6 +121,7 @@ struct gaih_result
   struct gaih_addrtuple *at;
   char *canon;
   bool free_at;
+  bool got_ipv6;
 };
 
 /* Values for `protoflag'.  */
@@ -316,7 +317,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	  res.canon = canonbuf;						      \
 	}								      \
       if (_family == AF_INET6 && *pat != NULL)				      \
-	got_ipv6 = true;						      \
+	res.got_ipv6 = true;						      \
     }									      \
  }
 
@@ -467,6 +468,128 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
   return 0;
 }
 
+#ifdef USE_NSCD
+/* Query addresses from nscd cache, returning a non-zero value on error.
+   RES members have the lookup result; RES->AT is NULL if there were no errors
+   but also no results.  */
+
+static int
+get_nscd_addresses (const char *name, const struct addrinfo *req,
+		    struct gaih_result *res)
+{
+  if (__nss_not_use_nscd_hosts > 0
+      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+    __nss_not_use_nscd_hosts = 0;
+
+  res->at = NULL;
+
+  if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts])
+    return 0;
+
+  /* Try to use nscd.  */
+  struct nscd_ai_result *air = NULL;
+  int err = __nscd_getai (name, &air, &h_errno);
+
+  if (__glibc_unlikely (air == NULL))
+    {
+      /* The database contains a negative entry.  */
+      if (err == 0)
+	return -EAI_NONAME;
+      if (__nss_not_use_nscd_hosts == 0)
+	{
+	  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+	    return -EAI_MEMORY;
+	  if (h_errno == TRY_AGAIN)
+	    return -EAI_AGAIN;
+	  return -EAI_SYSTEM;
+	}
+      return 0;
+    }
+
+  /* Transform into gaih_addrtuple list.  */
+  int result = 0;
+  char *addrs = air->addrs;
+
+  struct gaih_addrtuple *addrfree = calloc (air->naddrs, sizeof (*addrfree));
+  struct gaih_addrtuple *at = calloc (air->naddrs, sizeof (*at));
+  if (at == NULL)
+    {
+      result = -EAI_MEMORY;
+      goto out;
+    }
+
+  res->free_at = true;
+
+  int count = 0;
+  for (int i = 0; i < air->naddrs; ++i)
+    {
+      socklen_t size = (air->family[i] == AF_INET
+			? INADDRSZ : IN6ADDRSZ);
+
+      if (!((air->family[i] == AF_INET
+	     && req->ai_family == AF_INET6
+	     && (req->ai_flags & AI_V4MAPPED) != 0)
+	    || req->ai_family == AF_UNSPEC
+	    || air->family[i] == req->ai_family))
+	{
+	  /* Skip over non-matching result.  */
+	  addrs += size;
+	  continue;
+	}
+
+      if (air->family[i] == AF_INET && req->ai_family == AF_INET6
+	  && (req->ai_flags & AI_V4MAPPED))
+	{
+	  at[count].family = AF_INET6;
+	  at[count].addr[3] = *(uint32_t *) addrs;
+	  at[count].addr[2] = htonl (0xffff);
+	}
+      else if (req->ai_family == AF_UNSPEC
+	       || air->family[count] == req->ai_family)
+	{
+	  at[count].family = air->family[count];
+	  memcpy (at[count].addr, addrs, size);
+	  if (air->family[count] == AF_INET6)
+	    res->got_ipv6 = true;
+	}
+      at[count].next = at + count + 1;
+      count++;
+      addrs += size;
+    }
+
+  if ((req->ai_flags & AI_CANONNAME) && air->canon != NULL)
+    {
+      char *canonbuf = __strdup (air->canon);
+      if (canonbuf == NULL)
+	{
+	  result = -EAI_MEMORY;
+	  goto out;
+	}
+      res->canon = canonbuf;
+    }
+
+  if (count == 0)
+    {
+      result = -EAI_NONAME;
+      goto out;
+    }
+
+  at[count - 1].next = NULL;
+
+  res->at = at;
+
+out:
+  free (air);
+  if (result != 0)
+    {
+      free (at);
+      res->free_at = false;
+    }
+
+  return result;
+}
+#endif
+
 /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
    NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
    the function cannot determine a result, RES->AT is set to NULL and 0
@@ -630,7 +753,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
 			   / sizeof (struct gaih_typeproto)] = {0};
 
-  bool got_ipv6 = false;
   const char *orig_name = name;
 
   /* Reserve stack memory for the scratch buffer in the getaddrinfo
@@ -672,6 +794,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
       else if (res.at != NULL)
 	goto process_list;
 
+#ifdef USE_NSCD
+      if ((result = get_nscd_addresses (name, req, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
+#endif
+
       int no_data = 0;
       int no_inet6_data = 0;
       nss_action_list nip;
@@ -681,115 +810,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
       struct resolv_context *res_ctx = NULL;
       bool do_merge = false;
 
-#ifdef USE_NSCD
-      if (__nss_not_use_nscd_hosts > 0
-	  && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-	__nss_not_use_nscd_hosts = 0;
-
-      if (!__nss_not_use_nscd_hosts
-	  && !__nss_database_custom[NSS_DBSIDX_hosts])
-	{
-	  /* Try to use nscd.  */
-	  struct nscd_ai_result *air = NULL;
-	  int err = __nscd_getai (name, &air, &h_errno);
-	  if (air != NULL)
-	    {
-	      /* Transform into gaih_addrtuple list.  */
-	      bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
-	      char *addrs = air->addrs;
-
-	      addrmem = calloc (air->naddrs, sizeof (*addrmem));
-	      if (addrmem == NULL)
-		{
-		  result = -EAI_MEMORY;
-		  goto free_and_return;
-		}
-
-	      struct gaih_addrtuple *addrfree = addrmem;
-	      struct gaih_addrtuple **pat = &res.at;
-
-	      for (int i = 0; i < air->naddrs; ++i)
-		{
-		  socklen_t size = (air->family[i] == AF_INET
-				    ? INADDRSZ : IN6ADDRSZ);
-
-		  if (!((air->family[i] == AF_INET
-			 && req->ai_family == AF_INET6
-			 && (req->ai_flags & AI_V4MAPPED) != 0)
-			|| req->ai_family == AF_UNSPEC
-			|| air->family[i] == req->ai_family))
-		    {
-		      /* Skip over non-matching result.  */
-		      addrs += size;
-		      continue;
-		    }
-
-		  if (*pat == NULL)
-		    {
-		      *pat = addrfree++;
-		      (*pat)->scopeid = 0;
-		    }
-		  uint32_t *pataddr = (*pat)->addr;
-		  (*pat)->next = NULL;
-		  if (added_canon || air->canon == NULL)
-		    (*pat)->name = NULL;
-		  else if (res.canon == NULL)
-		    {
-		      char *canonbuf = __strdup (air->canon);
-		      if (canonbuf == NULL)
-			{
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
-			}
-		      res.canon = (*pat)->name = canonbuf;
-		    }
-
-		  if (air->family[i] == AF_INET
-		      && req->ai_family == AF_INET6
-		      && (req->ai_flags & AI_V4MAPPED))
-		    {
-		      (*pat)->family = AF_INET6;
-		      pataddr[3] = *(uint32_t *) addrs;
-		      pataddr[2] = htonl (0xffff);
-		      pataddr[1] = 0;
-		      pataddr[0] = 0;
-		      pat = &((*pat)->next);
-		      added_canon = true;
-		    }
-		  else if (req->ai_family == AF_UNSPEC
-			   || air->family[i] == req->ai_family)
-		    {
-		      (*pat)->family = air->family[i];
-		      memcpy (pataddr, addrs, size);
-		      pat = &((*pat)->next);
-		      added_canon = true;
-		      if (air->family[i] == AF_INET6)
-			got_ipv6 = true;
-		    }
-		  addrs += size;
-		}
-
-	      free (air);
-
-	      goto process_list;
-	    }
-	  else if (err == 0)
-	    /* The database contains a negative entry.  */
-	    goto free_and_return;
-	  else if (__nss_not_use_nscd_hosts == 0)
-	    {
-	      if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-		result = -EAI_MEMORY;
-	      else if (h_errno == TRY_AGAIN)
-		result = -EAI_AGAIN;
-	      else
-		result = -EAI_SYSTEM;
-
-	      goto free_and_return;
-	    }
-	}
-#endif
-
       no_more = !__nss_database_get (nss_database_hosts, &nip);
 
       /* If we are looking for both IPv4 and IPv6 address we don't
@@ -897,7 +917,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 			  no_data = 0;
 			  if (req->ai_family == AF_INET6)
-			    got_ipv6 = true;
+			    res.got_ipv6 = true;
 			}
 		      else
 			*pat = ((*pat)->next);
@@ -940,7 +960,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 			  && (req->ai_flags & AI_V4MAPPED)
 			  /* Avoid generating the mapped addresses if we
 			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !got_ipv6)))
+			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))
 		    {
 		      gethosts (AF_INET);
 
@@ -1091,7 +1111,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    /* If we looked up IPv4 mapped address discard them here if
 	       the caller isn't interested in all address and we have
 	       found at least one IPv6 address.  */
-	    if (got_ipv6
+	    if (res.got_ipv6
 		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
 		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
 	      goto ignore;
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 08/12] gaih_inet: separate nss lookup loop into its own function
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (6 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17 22:05     ` DJ Delorie
  2022-03-17  8:11   ` [PATCH v3 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
                     ` (3 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 sysdeps/posix/getaddrinfo.c | 563 ++++++++++++++++++------------------
 1 file changed, 286 insertions(+), 277 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 01be932b3f..f70ce2c76b 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -159,6 +159,14 @@ static const struct addrinfo default_hints =
     .ai_next = NULL
   };
 
+static void
+gaih_result_reset (struct gaih_result *res)
+{
+  if (res->free_at)
+    free (res->at);
+  free (res->canon);
+  memset (res, 0, sizeof (*res));
+}
 
 static int
 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
@@ -197,13 +205,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
 
 /* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
    is not copied, and the struct hostent object must not be deallocated
-   prematurely.  The new addresses are appended to the tuple array in
-   RESULT.  */
+   prematurely.  The new addresses are appended to the tuple array in RES.  */
 static bool
-convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
-				   int family,
-				   struct hostent *h,
-				   struct gaih_addrtuple **result)
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+				   struct hostent *h, struct gaih_result *res)
 {
   /* Count the number of addresses in h->h_addr_list.  */
   size_t count = 0;
@@ -215,7 +220,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
   if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
     return true;
 
-  struct gaih_addrtuple *array = *result;
+  struct gaih_addrtuple *array = res->at;
   size_t old = 0;
 
   while (array != NULL)
@@ -224,12 +229,14 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
       array = array->next;
     }
 
-  array = realloc (*result, (old + count) * sizeof (*array));
+  array = realloc (res->at, (old + count) * sizeof (*array));
 
   if (array == NULL)
     return false;
 
-  *result = array;
+  res->got_ipv6 = family == AF_INET6;
+  res->at = array;
+  res->free_at = true;
 
   /* Update the next pointers on reallocation.  */
   for (size_t i = 0; i < old; i++)
@@ -278,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_MEMORY;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
     }									      \
   if (status == NSS_STATUS_NOTFOUND					      \
@@ -288,7 +295,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_SYSTEM;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
       if (h_errno == TRY_AGAIN)						      \
 	no_data = EAI_AGAIN;						      \
@@ -297,27 +304,24 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
     }									      \
   else if (status == NSS_STATUS_SUCCESS)				      \
     {									      \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem))   \
+      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
 	{								      \
 	  __resolv_context_put (res_ctx);				      \
 	  result = -EAI_SYSTEM;						      \
-	  goto free_and_return;						      \
+	  goto out;							      \
 	}								      \
-      *pat = addrmem;							      \
 									      \
-      if (localcanon != NULL && res.canon == NULL)			      \
+      if (localcanon != NULL && res->canon == NULL)			      \
 	{								      \
 	  char *canonbuf = __strdup (localcanon);			      \
 	  if (canonbuf == NULL)						      \
 	    {								      \
 	      __resolv_context_put (res_ctx);				      \
 	      result = -EAI_SYSTEM;					      \
-	      goto free_and_return;					      \
+	      goto out;							      \
 	    }								      \
-	  res.canon = canonbuf;						      \
+	  res->canon = canonbuf;					      \
 	}								      \
-      if (_family == AF_INET6 && *pat != NULL)				      \
-	res.got_ipv6 = true;						      \
     }									      \
  }
 
@@ -590,6 +594,260 @@ out:
 }
 #endif
 
+static int
+get_nss_addresses (const char *name, const struct addrinfo *req,
+		   struct scratch_buffer *tmpbuf, struct gaih_result *res)
+{
+  int no_data = 0;
+  int no_inet6_data = 0;
+  nss_action_list nip;
+  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+  enum nss_status status = NSS_STATUS_UNAVAIL;
+  int no_more;
+  struct resolv_context *res_ctx = NULL;
+  bool do_merge = false;
+  int result = 0;
+
+  no_more = !__nss_database_get (nss_database_hosts, &nip);
+
+  /* If we are looking for both IPv4 and IPv6 address we don't
+     want the lookup functions to automatically promote IPv4
+     addresses to IPv6 addresses, so we use the no_inet6
+     function variant.  */
+  res_ctx = __resolv_context_get ();
+  if (res_ctx == NULL)
+    no_more = 1;
+
+  while (!no_more)
+    {
+      /* Always start afresh; continue should discard previous results
+	 and the hosts database does not support merge.  */
+      gaih_result_reset (res);
+
+      if (do_merge)
+	{
+	  __set_h_errno (NETDB_INTERNAL);
+	  __set_errno (EBUSY);
+	  break;
+	}
+
+      no_data = 0;
+      nss_gethostbyname4_r *fct4 = NULL;
+
+      /* gethostbyname4_r sends out parallel A and AAAA queries and
+	 is thus only suitable for PF_UNSPEC.  */
+      if (req->ai_family == PF_UNSPEC)
+	fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
+
+      if (fct4 != NULL)
+	{
+	  while (1)
+	    {
+	      status = DL_CALL_FCT (fct4, (name, &res->at,
+					   tmpbuf->data, tmpbuf->length,
+					   &errno, &h_errno,
+					   NULL));
+	      if (status == NSS_STATUS_SUCCESS)
+		break;
+	      /* gethostbyname4_r may write into AT, so reset it.  */
+	      res->at = NULL;
+	      if (status != NSS_STATUS_TRYAGAIN
+		  || errno != ERANGE || h_errno != NETDB_INTERNAL)
+		{
+		  if (h_errno == TRY_AGAIN)
+		    no_data = EAI_AGAIN;
+		  else
+		    no_data = h_errno == NO_DATA;
+		  break;
+		}
+
+	      if (!scratch_buffer_grow (tmpbuf))
+		{
+		  __resolv_context_put (res_ctx);
+		  result = -EAI_MEMORY;
+		  goto out;
+		}
+	    }
+
+	  if (status == NSS_STATUS_SUCCESS)
+	    {
+	      assert (!no_data);
+	      no_data = 1;
+
+	      if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL)
+		{
+		  char *canonbuf = __strdup (res->at->name);
+		  if (canonbuf == NULL)
+		    {
+		      __resolv_context_put (res_ctx);
+		      result = -EAI_MEMORY;
+		      goto out;
+		    }
+		  res->canon = canonbuf;
+		}
+
+	      struct gaih_addrtuple **pat = &res->at;
+
+	      while (*pat != NULL)
+		{
+		  if ((*pat)->family == AF_INET
+		      && req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED) != 0)
+		    {
+		      uint32_t *pataddr = (*pat)->addr;
+		      (*pat)->family = AF_INET6;
+		      pataddr[3] = pataddr[0];
+		      pataddr[2] = htonl (0xffff);
+		      pataddr[1] = 0;
+		      pataddr[0] = 0;
+		      pat = &((*pat)->next);
+		      no_data = 0;
+		    }
+		  else if (req->ai_family == AF_UNSPEC
+			   || (*pat)->family == req->ai_family)
+		    {
+		      pat = &((*pat)->next);
+
+		      no_data = 0;
+		      if (req->ai_family == AF_INET6)
+			res->got_ipv6 = true;
+		    }
+		  else
+		    *pat = ((*pat)->next);
+		}
+	    }
+
+	  no_inet6_data = no_data;
+	}
+      else
+	{
+	  nss_gethostbyname3_r *fct = NULL;
+	  if (req->ai_flags & AI_CANONNAME)
+	    /* No need to use this function if we do not look for
+	       the canonical name.  The function does not exist in
+	       all NSS modules and therefore the lookup would
+	       often fail.  */
+	    fct = __nss_lookup_function (nip, "gethostbyname3_r");
+	  if (fct == NULL)
+	    /* We are cheating here.  The gethostbyname2_r
+	       function does not have the same interface as
+	       gethostbyname3_r but the extra arguments the
+	       latter takes are added at the end.  So the
+	       gethostbyname2_r code will just ignore them.  */
+	    fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	  if (fct != NULL)
+	    {
+	      if (req->ai_family == AF_INET6
+		  || req->ai_family == AF_UNSPEC)
+		{
+		  gethosts (AF_INET6);
+		  no_inet6_data = no_data;
+		  inet6_status = status;
+		}
+	      if (req->ai_family == AF_INET
+		  || req->ai_family == AF_UNSPEC
+		  || (req->ai_family == AF_INET6
+		      && (req->ai_flags & AI_V4MAPPED)
+		      /* Avoid generating the mapped addresses if we
+			 know we are not going to need them.  */
+		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
+		{
+		  gethosts (AF_INET);
+
+		  if (req->ai_family == AF_INET)
+		    {
+		      no_inet6_data = no_data;
+		      inet6_status = status;
+		    }
+		}
+
+	      /* If we found one address for AF_INET or AF_INET6,
+		 don't continue the search.  */
+	      if (inet6_status == NSS_STATUS_SUCCESS
+		  || status == NSS_STATUS_SUCCESS)
+		{
+		  if ((req->ai_flags & AI_CANONNAME) != 0
+		      && res->canon == NULL)
+		    {
+		      char *canonbuf = getcanonname (nip, res->at, name);
+		      if (canonbuf == NULL)
+			{
+			  __resolv_context_put (res_ctx);
+			  result = -EAI_MEMORY;
+			  goto out;
+			}
+		      res->canon = canonbuf;
+		    }
+		  status = NSS_STATUS_SUCCESS;
+		}
+	      else
+		{
+		  /* We can have different states for AF_INET and
+		     AF_INET6.  Try to find a useful one for both.  */
+		  if (inet6_status == NSS_STATUS_TRYAGAIN)
+		    status = NSS_STATUS_TRYAGAIN;
+		  else if (status == NSS_STATUS_UNAVAIL
+			   && inet6_status != NSS_STATUS_UNAVAIL)
+		    status = inet6_status;
+		}
+	    }
+	  else
+	    {
+	      /* Could not locate any of the lookup functions.
+		 The NSS lookup code does not consistently set
+		 errno, so we need to supply our own error
+		 code here.  The root cause could either be a
+		 resource allocation failure, or a missing
+		 service function in the DSO (so it should not
+		 be listed in /etc/nsswitch.conf).  Assume the
+		 former, and return EBUSY.  */
+	      status = NSS_STATUS_UNAVAIL;
+	      __set_h_errno (NETDB_INTERNAL);
+	      __set_errno (EBUSY);
+	    }
+	}
+
+      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	break;
+
+      /* The hosts database does not support MERGE.  */
+      if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
+	do_merge = true;
+
+      nip++;
+      if (nip->module == NULL)
+	no_more = -1;
+    }
+
+  __resolv_context_put (res_ctx);
+
+  /* If we have a failure which sets errno, report it using
+     EAI_SYSTEM.  */
+  if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+      && h_errno == NETDB_INTERNAL)
+    {
+      result = -EAI_SYSTEM;
+      goto out;
+    }
+
+  if (no_data != 0 && no_inet6_data != 0)
+    {
+      /* If both requests timed out report this.  */
+      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+	result = -EAI_AGAIN;
+      else
+	/* We made requests but they turned out no data.  The name
+	   is known, though.  */
+	result = -EAI_NODATA;
+    }
+
+out:
+  if (result != 0)
+    gaih_result_reset (res);
+  return result;
+}
+
 /* Convert numeric addresses to binary into RES.  On failure, RES->AT is set to
    NULL and an error code is returned.  If AI_NUMERIC_HOST is not requested and
    the function cannot determine a result, RES->AT is set to NULL and 0
@@ -723,7 +981,7 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
 	  /* We found data, convert it.  RES->AT from the conversion will
 	     either be an allocated block or NULL, both of which are safe to
 	     pass to free ().  */
-	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
+	  if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res))
 	    return -EAI_MEMORY;
 
 	  res->free_at = true;
@@ -801,264 +1059,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	goto process_list;
 #endif
 
-      int no_data = 0;
-      int no_inet6_data = 0;
-      nss_action_list nip;
-      enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
-      enum nss_status status = NSS_STATUS_UNAVAIL;
-      int no_more;
-      struct resolv_context *res_ctx = NULL;
-      bool do_merge = false;
-
-      no_more = !__nss_database_get (nss_database_hosts, &nip);
-
-      /* If we are looking for both IPv4 and IPv6 address we don't
-	 want the lookup functions to automatically promote IPv4
-	 addresses to IPv6 addresses, so we use the no_inet6
-	 function variant.  */
-      res_ctx = __resolv_context_get ();
-      if (res_ctx == NULL)
-	no_more = 1;
-
-      while (!no_more)
-	{
-	  /* Always start afresh; continue should discard previous results
-	     and the hosts database does not support merge.  */
-	  res.at = NULL;
-	  free (res.canon);
-	  free (addrmem);
-	  res.canon = NULL;
-	  addrmem = NULL;
-	  got_ipv6 = false;
-
-	  if (do_merge)
-	    {
-	      __set_h_errno (NETDB_INTERNAL);
-	      __set_errno (EBUSY);
-	      break;
-	    }
-
-	  no_data = 0;
-	  nss_gethostbyname4_r *fct4 = NULL;
-
-	  /* gethostbyname4_r sends out parallel A and AAAA queries and
-	     is thus only suitable for PF_UNSPEC.  */
-	  if (req->ai_family == PF_UNSPEC)
-	    fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
-
-	  if (fct4 != NULL)
-	    {
-	      while (1)
-		{
-		  status = DL_CALL_FCT (fct4, (name, &res.at,
-					       tmpbuf->data, tmpbuf->length,
-					       &errno, &h_errno,
-					       NULL));
-		  if (status == NSS_STATUS_SUCCESS)
-		    break;
-		  /* gethostbyname4_r may write into AT, so reset it.  */
-		  res.at = NULL;
-		  if (status != NSS_STATUS_TRYAGAIN
-		      || errno != ERANGE || h_errno != NETDB_INTERNAL)
-		    {
-		      if (h_errno == TRY_AGAIN)
-			no_data = EAI_AGAIN;
-		      else
-			no_data = h_errno == NO_DATA;
-		      break;
-		    }
-
-		  if (!scratch_buffer_grow (tmpbuf))
-		    {
-		      __resolv_context_put (res_ctx);
-		      result = -EAI_MEMORY;
-		      goto free_and_return;
-		    }
-		}
-
-	      if (status == NSS_STATUS_SUCCESS)
-		{
-		  assert (!no_data);
-		  no_data = 1;
-
-		  if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
-		    {
-		      char *canonbuf = __strdup (res.at->name);
-		      if (canonbuf == NULL)
-			{
-			  __resolv_context_put (res_ctx);
-			  result = -EAI_MEMORY;
-			  goto free_and_return;
-			}
-		      res.canon = canonbuf;
-		    }
-
-		  struct gaih_addrtuple **pat = &res.at;
-
-		  while (*pat != NULL)
-		    {
-		      if ((*pat)->family == AF_INET
-			  && req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED) != 0)
-			{
-			  uint32_t *pataddr = (*pat)->addr;
-			  (*pat)->family = AF_INET6;
-			  pataddr[3] = pataddr[0];
-			  pataddr[2] = htonl (0xffff);
-			  pataddr[1] = 0;
-			  pataddr[0] = 0;
-			  pat = &((*pat)->next);
-			  no_data = 0;
-			}
-		      else if (req->ai_family == AF_UNSPEC
-			       || (*pat)->family == req->ai_family)
-			{
-			  pat = &((*pat)->next);
-
-			  no_data = 0;
-			  if (req->ai_family == AF_INET6)
-			    res.got_ipv6 = true;
-			}
-		      else
-			*pat = ((*pat)->next);
-		    }
-		}
-
-	      no_inet6_data = no_data;
-	    }
-	  else
-	    {
-	      nss_gethostbyname3_r *fct = NULL;
-	      if (req->ai_flags & AI_CANONNAME)
-		/* No need to use this function if we do not look for
-		   the canonical name.  The function does not exist in
-		   all NSS modules and therefore the lookup would
-		   often fail.  */
-		fct = __nss_lookup_function (nip, "gethostbyname3_r");
-	      if (fct == NULL)
-		/* We are cheating here.  The gethostbyname2_r
-		   function does not have the same interface as
-		   gethostbyname3_r but the extra arguments the
-		   latter takes are added at the end.  So the
-		   gethostbyname2_r code will just ignore them.  */
-		fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-	      if (fct != NULL)
-		{
-		  struct gaih_addrtuple **pat = &res.at;
-
-		  if (req->ai_family == AF_INET6
-		      || req->ai_family == AF_UNSPEC)
-		    {
-		      gethosts (AF_INET6);
-		      no_inet6_data = no_data;
-		      inet6_status = status;
-		    }
-		  if (req->ai_family == AF_INET
-		      || req->ai_family == AF_UNSPEC
-		      || (req->ai_family == AF_INET6
-			  && (req->ai_flags & AI_V4MAPPED)
-			  /* Avoid generating the mapped addresses if we
-			     know we are not going to need them.  */
-			  && ((req->ai_flags & AI_ALL) || !res.got_ipv6)))
-		    {
-		      gethosts (AF_INET);
-
-		      if (req->ai_family == AF_INET)
-			{
-			  no_inet6_data = no_data;
-			  inet6_status = status;
-			}
-		    }
-
-		  /* If we found one address for AF_INET or AF_INET6,
-		     don't continue the search.  */
-		  if (inet6_status == NSS_STATUS_SUCCESS
-		      || status == NSS_STATUS_SUCCESS)
-		    {
-		      if ((req->ai_flags & AI_CANONNAME) != 0
-			  && res.canon == NULL)
-			{
-			  char *canonbuf = getcanonname (nip, res.at, name);
-			  if (canonbuf == NULL)
-			    {
-			      __resolv_context_put (res_ctx);
-			      result = -EAI_MEMORY;
-			      goto free_and_return;
-			    }
-			  res.canon = canonbuf;
-			}
-		      status = NSS_STATUS_SUCCESS;
-		    }
-		  else
-		    {
-		      /* We can have different states for AF_INET and
-			 AF_INET6.  Try to find a useful one for both.  */
-		      if (inet6_status == NSS_STATUS_TRYAGAIN)
-			status = NSS_STATUS_TRYAGAIN;
-		      else if (status == NSS_STATUS_UNAVAIL
-			       && inet6_status != NSS_STATUS_UNAVAIL)
-			status = inet6_status;
-		    }
-		}
-	      else
-		{
-		  /* Could not locate any of the lookup functions.
-		     The NSS lookup code does not consistently set
-		     errno, so we need to supply our own error
-		     code here.  The root cause could either be a
-		     resource allocation failure, or a missing
-		     service function in the DSO (so it should not
-		     be listed in /etc/nsswitch.conf).  Assume the
-		     former, and return EBUSY.  */
-		  status = NSS_STATUS_UNAVAIL;
-		  __set_h_errno (NETDB_INTERNAL);
-		  __set_errno (EBUSY);
-		}
-	    }
-
-	  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-	    break;
-
-	  /* The hosts database does not support MERGE.  */
-	  if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
-	    do_merge = true;
-
-	  nip++;
-	  if (nip->module == NULL)
-	    no_more = -1;
-	}
-
-      __resolv_context_put (res_ctx);
-
-      /* If we have a failure which sets errno, report it using
-	 EAI_SYSTEM.  */
-      if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
-	  && h_errno == NETDB_INTERNAL)
-	{
-	  result = -EAI_SYSTEM;
-	  goto free_and_return;
-	}
-
-      if (no_data != 0 && no_inet6_data != 0)
-	{
-	  /* If both requests timed out report this.  */
-	  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-	    result = -EAI_AGAIN;
-	  else
-	    /* We made requests but they turned out no data.  The name
-	       is known, though.  */
-	    result = -EAI_NODATA;
-
-	  goto free_and_return;
-	}
+      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
+	goto free_and_return;
+      else if (res.at != NULL)
+	goto process_list;
 
-    process_list:
-      if (res.at == NULL)
-	{
-	  result = -EAI_NONAME;
-	  goto free_and_return;
-	}
+      /* None of the lookups worked, so name not found.  */
+      result = -EAI_NONAME;
+      goto free_and_return;
     }
   else
     {
@@ -1089,6 +1097,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	}
     }
 
+process_list:
   {
     /* Set up the canonical name if we need it.  */
     if ((result = process_canonname (req, orig_name, &res)) != 0)
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 09/12] gaih_inet: make gethosts into a function
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (7 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

The macro is quite a pain to debug, so make gethosts into a function to
make it easier to maintain.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 117 ++++++++++++++++++------------------
 1 file changed, 59 insertions(+), 58 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index f70ce2c76b..bc385dd322 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -268,63 +268,54 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
   return true;
 }
 
-#define gethosts(_family) \
- {									      \
-  struct hostent th;							      \
-  char *localcanon = NULL;						      \
-  no_data = 0;								      \
-  while (1)								      \
-    {									      \
-      status = DL_CALL_FCT (fct, (name, _family, &th,			      \
-				  tmpbuf->data, tmpbuf->length,		      \
-				  &errno, &h_errno, NULL, &localcanon));      \
-      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL	      \
-	  || errno != ERANGE)						      \
-	break;								      \
-      if (!scratch_buffer_grow (tmpbuf))				      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_MEMORY;						      \
-	  goto out;							      \
-	}								      \
-    }									      \
-  if (status == NSS_STATUS_NOTFOUND					      \
-      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)	      \
-    {									      \
-      if (h_errno == NETDB_INTERNAL)					      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_SYSTEM;						      \
-	  goto out;							      \
-	}								      \
-      if (h_errno == TRY_AGAIN)						      \
-	no_data = EAI_AGAIN;						      \
-      else								      \
-	no_data = h_errno == NO_DATA;					      \
-    }									      \
-  else if (status == NSS_STATUS_SUCCESS)				      \
-    {									      \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res))	      \
-	{								      \
-	  __resolv_context_put (res_ctx);				      \
-	  result = -EAI_SYSTEM;						      \
-	  goto out;							      \
-	}								      \
-									      \
-      if (localcanon != NULL && res->canon == NULL)			      \
-	{								      \
-	  char *canonbuf = __strdup (localcanon);			      \
-	  if (canonbuf == NULL)						      \
-	    {								      \
-	      __resolv_context_put (res_ctx);				      \
-	      result = -EAI_SYSTEM;					      \
-	      goto out;							      \
-	    }								      \
-	  res->canon = canonbuf;					      \
-	}								      \
-    }									      \
- }
+static int
+gethosts (nss_gethostbyname3_r fct, int family, const char *name,
+	  const struct addrinfo *req, struct scratch_buffer *tmpbuf,
+	  struct gaih_result *res, enum nss_status *statusp, int *no_datap)
+{
+  struct hostent th;
+  char *localcanon = NULL;
+  enum nss_status status;
+
+  *no_datap = 0;
+  while (1)
+    {
+      *statusp = status = DL_CALL_FCT (fct, (name, family, &th,
+					     tmpbuf->data, tmpbuf->length,
+					     &errno, &h_errno, NULL,
+					     &localcanon));
+      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL
+	  || errno != ERANGE)
+	break;
+      if (!scratch_buffer_grow (tmpbuf))
+	return -EAI_MEMORY;
+    }
+  if (status == NSS_STATUS_NOTFOUND
+      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+    {
+      if (h_errno == NETDB_INTERNAL)
+	return -EAI_SYSTEM;
+      if (h_errno == TRY_AGAIN)
+	*no_datap = EAI_AGAIN;
+      else
+	*no_datap = h_errno == NO_DATA;
+    }
+  else if (status == NSS_STATUS_SUCCESS)
+    {
+      if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
+	return -EAI_SYSTEM;
+
+      if (localcanon != NULL && res->canon == NULL)
+	{
+	  char *canonbuf = __strdup (localcanon);
+	  if (canonbuf == NULL)
+	    return  -EAI_SYSTEM;
+	  res->canon = canonbuf;
+	}
+    }
 
+  return 0;
+}
 
 /* This function is called if a canonical name is requested, but if
    the service function did not provide it.  It tries to obtain the
@@ -741,7 +732,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
 	      if (req->ai_family == AF_INET6
 		  || req->ai_family == AF_UNSPEC)
 		{
-		  gethosts (AF_INET6);
+		  if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf,
+					  res, &status, &no_data)) != 0)
+		    {
+		      __resolv_context_put (res_ctx);
+		      goto out;
+		    }
 		  no_inet6_data = no_data;
 		  inet6_status = status;
 		}
@@ -753,7 +749,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
 			 know we are not going to need them.  */
 		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
 		{
-		  gethosts (AF_INET);
+		  if ((result = gethosts (fct, AF_INET, name, req, tmpbuf,
+					  res, &status, &no_data)) != 0)
+		    {
+		      __resolv_context_put (res_ctx);
+		      goto out;
+		    }
 
 		  if (req->ai_family == AF_INET)
 		    {
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 10/12] gaih_inet: split loopback lookup into its own function
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (8 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Flatten the condition nesting and replace the alloca for RET.AT/ATR with
a single array LOCAL_AT[2].  This gets rid of alloca and alloca
accounting.

`git diff -b` is probably the best way to view this change since much of
the diff is whitespace changes.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 127 ++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 65 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index bc385dd322..47c41d332d 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1004,6 +1004,32 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
   return -EAI_NODATA;
 }
 
+/* Add local address information into RES.  RES->AT is assumed to have enough
+   space for two tuples and is zeroed out.  */
+
+static void
+get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
+{
+  struct gaih_addrtuple *atr = res->at;
+  if (req->ai_family == AF_UNSPEC)
+    res->at->next = res->at + 1;
+
+  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
+    {
+      res->at->family = AF_INET6;
+      if ((req->ai_flags & AI_PASSIVE) == 0)
+	memcpy (res->at->addr, &in6addr_loopback, sizeof (struct in6_addr));
+      atr = res->at->next;
+    }
+
+  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
+    {
+      atr->family = AF_INET;
+      if ((req->ai_flags & AI_PASSIVE) == 0)
+	atr->addr[0] = htonl (INADDR_LOOPBACK);
+    }
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -1014,10 +1040,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
   const char *orig_name = name;
 
-  /* Reserve stack memory for the scratch buffer in the getaddrinfo
-     function.  */
-  size_t alloca_used = sizeof (struct scratch_buffer);
-
   int rc;
   if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0)
     return rc;
@@ -1027,76 +1049,51 @@ gaih_inet (const char *name, const struct gaih_service *service,
   int result = 0;
 
   struct gaih_result res = {0};
-  if (name != NULL)
+  struct gaih_addrtuple local_at[2] = {0};
+
+  res.at = local_at;
+
+  if (__glibc_unlikely (name == NULL))
     {
-      if (req->ai_flags & AI_IDN)
-	{
-	  char *out;
-	  result = __idna_to_dns_encoding (name, &out);
-	  if (result != 0)
-	    return -result;
-	  name = out;
-	  malloc_name = true;
-	}
+      get_local_addresses (req, &res);
+      goto process_list;
+    }
 
-      res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
-      res.at->scopeid = 0;
-      res.at->next = NULL;
+  if (req->ai_flags & AI_IDN)
+    {
+      char *out;
+      result = __idna_to_dns_encoding (name, &out);
+      if (result != 0)
+	return -result;
+      name = out;
+      malloc_name = true;
+    }
 
-      if ((result = text_to_binary_address (name, req, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = text_to_binary_address (name, req, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
-      if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
 #ifdef USE_NSCD
-      if ((result = get_nscd_addresses (name, req, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
+  if ((result = get_nscd_addresses (name, req, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 #endif
 
-      if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
-	goto free_and_return;
-      else if (res.at != NULL)
-	goto process_list;
-
-      /* None of the lookups worked, so name not found.  */
-      result = -EAI_NONAME;
-      goto free_and_return;
-    }
-  else
-    {
-      struct gaih_addrtuple *atr;
-      atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
-				     alloca_used);
-      memset (res.at, '\0', sizeof (struct gaih_addrtuple));
-
-      if (req->ai_family == AF_UNSPEC)
-	{
-	  res.at->next = __alloca (sizeof (struct gaih_addrtuple));
-	  memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
-	}
-
-      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	{
-	  res.at->family = AF_INET6;
-	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
-	  atr = res.at->next;
-	}
+  if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
+    goto free_and_return;
+  else if (res.at != NULL)
+    goto process_list;
 
-      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	{
-	  atr->family = AF_INET;
-	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    atr->addr[0] = htonl (INADDR_LOOPBACK);
-	}
-    }
+  /* None of the lookups worked, so name not found.  */
+  result = -EAI_NONAME;
+  goto free_and_return;
 
 process_list:
   {
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 11/12] gaih_inet: Split result generation into its own function
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (9 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  2022-03-17  8:11   ` [PATCH v3 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

Simplify the loop a wee bit and clean up variable names too.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 176 ++++++++++++++++++------------------
 1 file changed, 86 insertions(+), 90 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 47c41d332d..f5d4a5cfd9 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1030,6 +1030,87 @@ get_local_addresses (const struct addrinfo *req, struct gaih_result *res)
     }
 }
 
+/* Generate results in PAI and its count in NADDRS.  Return 0 on success or an
+   error code on failure.  */
+
+static int
+generate_addrinfo (const struct addrinfo *req, struct gaih_result *res,
+		   const struct gaih_servtuple *st, struct addrinfo **pai,
+		   unsigned int *naddrs)
+{
+  size_t socklen;
+  sa_family_t family;
+
+  /* Buffer is the size of an unformatted IPv6 address in printable format.  */
+  for (struct gaih_addrtuple *at = res->at; at != NULL; at = at->next)
+    {
+      family = at->family;
+      if (family == AF_INET6)
+	{
+	  socklen = sizeof (struct sockaddr_in6);
+
+	  /* If we looked up IPv4 mapped address discard them here if
+	     the caller isn't interested in all address and we have
+	     found at least one IPv6 address.  */
+	  if (res->got_ipv6
+	      && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
+	      && IN6_IS_ADDR_V4MAPPED (at->addr))
+	    continue;
+	}
+      else
+	socklen = sizeof (struct sockaddr_in);
+
+      for (int i = 0; st[i].set; i++)
+	{
+	  struct addrinfo *ai;
+	  ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
+	  if (ai == NULL)
+	    return -EAI_MEMORY;
+
+	  ai->ai_flags = req->ai_flags;
+	  ai->ai_family = family;
+	  ai->ai_socktype = st[i].socktype;
+	  ai->ai_protocol = st[i].protocol;
+	  ai->ai_addrlen = socklen;
+	  ai->ai_addr = (void *) (ai + 1);
+
+	  /* We only add the canonical name once.  */
+	  ai->ai_canonname = res->canon;
+	  res->canon = NULL;
+
+#ifdef _HAVE_SA_LEN
+	  ai->ai_addr->sa_len = socklen;
+#endif /* _HAVE_SA_LEN */
+	  ai->ai_addr->sa_family = family;
+
+	  /* In case of an allocation error the list must be NULL
+	     terminated.  */
+	  ai->ai_next = NULL;
+
+	  if (family == AF_INET6)
+	    {
+	      struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) ai->ai_addr;
+	      sin6p->sin6_port = st[i].port;
+	      sin6p->sin6_flowinfo = 0;
+	      memcpy (&sin6p->sin6_addr, at->addr, sizeof (struct in6_addr));
+	      sin6p->sin6_scope_id = at->scopeid;
+	    }
+	  else
+	    {
+	      struct sockaddr_in *sinp = (struct sockaddr_in *) ai->ai_addr;
+	      sinp->sin_port = st[i].port;
+	      memcpy (&sinp->sin_addr, at->addr, sizeof (struct in_addr));
+	      memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
+	    }
+
+	  pai = &(ai->ai_next);
+	}
+
+      ++*naddrs;
+    }
+  return 0;
+}
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai,
@@ -1096,98 +1177,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
   goto free_and_return;
 
 process_list:
-  {
-    /* Set up the canonical name if we need it.  */
-    if ((result = process_canonname (req, orig_name, &res)) != 0)
-      goto free_and_return;
-
-    struct gaih_addrtuple *at2 = res.at;
-    size_t socklen;
-    sa_family_t family;
-
-    /*
-      buffer is the size of an unformatted IPv6 address in printable format.
-     */
-    while (at2 != NULL)
-      {
-	family = at2->family;
-	if (family == AF_INET6)
-	  {
-	    socklen = sizeof (struct sockaddr_in6);
-
-	    /* If we looked up IPv4 mapped address discard them here if
-	       the caller isn't interested in all address and we have
-	       found at least one IPv6 address.  */
-	    if (res.got_ipv6
-		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
-		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
-	      goto ignore;
-	  }
-	else
-	  socklen = sizeof (struct sockaddr_in);
-
-	for (int i = 0; st[i].set; i++)
-	  {
-	    struct addrinfo *ai;
-	    ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
-	    if (ai == NULL)
-	      {
-		result = -EAI_MEMORY;
-		goto free_and_return;
-	      }
-
-	    ai->ai_flags = req->ai_flags;
-	    ai->ai_family = family;
-	    ai->ai_socktype = st[i].socktype;
-	    ai->ai_protocol = st[i].protocol;
-	    ai->ai_addrlen = socklen;
-	    ai->ai_addr = (void *) (ai + 1);
-
-	    /* We only add the canonical name once.  */
-	    ai->ai_canonname = res.canon;
-	    res.canon = NULL;
-
-#ifdef _HAVE_SA_LEN
-	    ai->ai_addr->sa_len = socklen;
-#endif /* _HAVE_SA_LEN */
-	    ai->ai_addr->sa_family = family;
-
-	    /* In case of an allocation error the list must be NULL
-	       terminated.  */
-	    ai->ai_next = NULL;
-
-	    if (family == AF_INET6)
-	      {
-		struct sockaddr_in6 *sin6p =
-		  (struct sockaddr_in6 *) ai->ai_addr;
-
-		sin6p->sin6_port = st[i].port;
-		sin6p->sin6_flowinfo = 0;
-		memcpy (&sin6p->sin6_addr,
-			at2->addr, sizeof (struct in6_addr));
-		sin6p->sin6_scope_id = at2->scopeid;
-	      }
-	    else
-	      {
-		struct sockaddr_in *sinp =
-		  (struct sockaddr_in *) ai->ai_addr;
-		sinp->sin_port = st[i].port;
-		memcpy (&sinp->sin_addr,
-			at2->addr, sizeof (struct in_addr));
-		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
-	      }
-
-	    pai = &(ai->ai_next);
-	  }
-
-	++*naddrs;
+  /* Set up the canonical name if we need it.  */
+  if ((result = process_canonname (req, orig_name, &res)) != 0)
+    goto free_and_return;
 
-      ignore:
-	at2 = at2->next;
-      }
-  }
+  result = generate_addrinfo (req, &res, st, pai, naddrs);
 
- free_and_return:
+free_and_return:
   if (malloc_name)
     free ((char *) name);
   free (addrmem);
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 12/12] gethosts: Return EAI_MEMORY on allocation failure
  2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
                     ` (10 preceding siblings ...)
  2022-03-17  8:11   ` [PATCH v3 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
@ 2022-03-17  8:11   ` Siddhesh Poyarekar
  11 siblings, 0 replies; 68+ messages in thread
From: Siddhesh Poyarekar @ 2022-03-17  8:11 UTC (permalink / raw)
  To: libc-alpha

All other cases of failures due to lack of memory return EAI_MEMORY, so
it seems wrong to return EAI_SYSTEM here.  The only reason
convert_hostent_to_gaih_addrtuple could fail is on calloc failure.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
---
 sysdeps/posix/getaddrinfo.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index f5d4a5cfd9..0ece3b46b7 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -303,13 +303,13 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name,
   else if (status == NSS_STATUS_SUCCESS)
     {
       if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
-	return -EAI_SYSTEM;
+	return -EAI_MEMORY;
 
       if (localcanon != NULL && res->canon == NULL)
 	{
 	  char *canonbuf = __strdup (localcanon);
 	  if (canonbuf == NULL)
-	    return  -EAI_SYSTEM;
+	    return  -EAI_MEMORY;
 	  res->canon = canonbuf;
 	}
     }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931]
  2022-03-17  8:11   ` [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
@ 2022-03-17 21:47     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17 21:47 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


LGTM.  I expected multiple test cases (one per nsswitch) but forgot
about the hot reloading, so we're testing that too :-)

Reviewed-by: DJ Delorie <dj@redhat.com>

Siddhesh Poyarekar <siddhesh@sourceware.org> writes:
> diff --git a/nss/Makefile b/nss/Makefile
> +  tst-nss-gai-actions \

Ok.

> diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c
> +/* Test continue and merge NSS actions for getaddrinfo.
> +   Copyright The GNU Toolchain Authors.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <dlfcn.h>
> +#include <gnu/lib-names.h>
> +#include <nss.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <support/check.h>
> +#include <support/format_nss.h>
> +#include <support/support.h>
> +#include <support/xstdio.h>
> +#include <support/xunistd.h>

Ok.

> +enum
> +{
> +  ACTION_MERGE = 0,
> +  ACTION_CONTINUE,
> +};
> +
> +static const char *
> +family_str (int family)
> +{
> +  switch (family)
> +    {
> +    case AF_UNSPEC:
> +      return "AF_UNSPEC";
> +    case AF_INET:
> +      return "AF_INET";
> +    default:
> +      __builtin_unreachable ();
> +    }
> +}
> +
> +static const char *
> +action_str (int action)
> +{
> +  switch (action)
> +    {
> +    case ACTION_MERGE:
> +      return "merge";
> +    case ACTION_CONTINUE:
> +      return "continue";
> +    default:
> +      __builtin_unreachable ();
> +    }
> +}

Ok.

> +static void
> +do_one_test (int action, int family, bool canon)
> +{
> +  struct addrinfo hints =
> +    {
> +      .ai_family = family,
> +    };
> +
> +  struct addrinfo *ai;
> +
> +  if (canon)
> +    hints.ai_flags = AI_CANONNAME;
> +
> +  printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n",
> +	  action_str (action), family_str (family),
> +	  canon ? "AI_CANONNAME" : "");

Ok.

> +  int ret = getaddrinfo ("example.org", "80", &hints, &ai);

Ok.  Do we need an /etc/services?  Probably not.

> +  switch (action)
> +    {
> +    case ACTION_MERGE:
> +      if (ret == 0)
> +	{
> +	  char *formatted = support_format_addrinfo (ai, ret);
> +
> +	  printf ("merge unexpectedly succeeded:\n %s\n", formatted);
> +	  support_record_failure ();
> +	  free (formatted);
> +	}
> +      else
> +	return;

Ok.

> +    case ACTION_CONTINUE:
> +	{
> +	  char *formatted = support_format_addrinfo (ai, ret);
> +
> +	  /* Verify that the result appears exactly once.  */
> +	  const char *expected = "address: STREAM/TCP 192.0.0.1 80\n"
> +	    "address: DGRAM/UDP 192.0.0.1 80\n"
> +	    "address: RAW/IP 192.0.0.1 80\n";
> +
> +	  const char *contains = strstr (formatted, expected);
> +	  const char *contains2 = NULL;
> +
> +	  if (contains != NULL)
> +	    contains2 = strstr (contains + strlen (expected), expected);
> +
> +	  if (contains == NULL || contains2 != NULL)
> +	    {
> +	      printf ("continue failed:\n%s\n", formatted);
> +	      support_record_failure ();
> +	    }
> +
> +	  free (formatted);
> +	  break;

Ok.

> +	}
> +    default:
> +      __builtin_unreachable ();
> +    }
> +}

Ok.

> +static void
> +do_one_test_set (int action)
> +{
> +  char buf[32];
> +
> +  snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files",
> +	    action_str (action));
> +  __nss_configure_lookup ("hosts", buf);
> +
> +  do_one_test (action, AF_UNSPEC, false);
> +  do_one_test (action, AF_INET, false);
> +  do_one_test (action, AF_INET, true);
> +}
> +
> +static int
> +do_test (void)
> +{
> +  do_one_test_set (ACTION_CONTINUE);
> +  do_one_test_set (ACTION_MERGE);
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/nss/tst-nss-gai-actions.root/etc/host.conf b/nss/tst-nss-gai-actions.root/etc/host.conf
> +multi on

Ok.

> diff --git a/nss/tst-nss-gai-actions.root/etc/hosts b/nss/tst-nss-gai-actions.root/etc/hosts
> +192.0.0.1	example.org
> +192.0.0.2	example.org
> + . . .
> +192.0.1.252	example.org
> +192.0.1.253	example.org
> +192.0.1.254	example.org

Ok.

> diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c

Unchanged from v2, ok.


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 07/12] gaih_inet: Split nscd lookup code into its own function.
  2022-03-17  8:11   ` [PATCH v3 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
@ 2022-03-17 22:02     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17 22:02 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


Siddhesh Poyarekar <siddhesh@sourceware.org> writes:
> Add a new member got_ipv6 to indicate if the results have an IPv6
> result and use it instead of the local got_ipv6.

No change from v2 but got_ipv6 was initialized in 01/12 (and more so in
08/12) so OK now

LGTM
Reviewed-by: DJ Delorie <dj@redhat.com>


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 08/12] gaih_inet: separate nss lookup loop into its own function
  2022-03-17  8:11   ` [PATCH v3 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
@ 2022-03-17 22:05     ` DJ Delorie
  0 siblings, 0 replies; 68+ messages in thread
From: DJ Delorie @ 2022-03-17 22:05 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha


Siddhesh Poyarekar <siddhesh@sourceware.org> writes:
> @@ -224,12 +229,14 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
>        array = array->next;
>      }
>  
> -  array = realloc (*result, (old + count) * sizeof (*array));
> +  array = realloc (res->at, (old + count) * sizeof (*array));
>  
>    if (array == NULL)
>      return false;
>  
> -  *result = array;
> +  res->got_ipv6 = family == AF_INET6;
> +  res->at = array;
> +  res->free_at = true;

Ok.

Rest essentially unchanged from v2


LGTM

Reviewed-by: DJ Delorie <dj@redhat.com>


^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2022-03-17 22:05 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-08 10:07 [PATCH 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
2022-03-08 13:52   ` Siddhesh Poyarekar
2022-03-08 21:12     ` Carlos O'Donell
2022-03-09 17:13       ` Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
2022-03-08 11:00   ` Andreas Schwab
2022-03-08 13:45     ` Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
2022-03-08 10:07 ` [PATCH 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
2022-03-14  9:48 ` [PATCH v2 00/12] getaddrinfo facelift and fixes Siddhesh Poyarekar
2022-03-14  9:48   ` [PATCH v2 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
2022-03-14 10:30     ` Andreas Schwab
2022-03-14 14:15       ` Siddhesh Poyarekar
2022-03-16 20:47     ` DJ Delorie
2022-03-17  1:39       ` Siddhesh Poyarekar
2022-03-14  9:48   ` [PATCH v2 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
2022-03-16 21:12     ` DJ Delorie
2022-03-14  9:48   ` [PATCH v2 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
2022-03-16 23:42     ` DJ Delorie
2022-03-17  2:30       ` Siddhesh Poyarekar
2022-03-14  9:48   ` [PATCH v2 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
2022-03-17  0:48     ` DJ Delorie
2022-03-14  9:48   ` [PATCH v2 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
2022-03-17  4:10     ` DJ Delorie
2022-03-14  9:48   ` [PATCH v2 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
2022-03-17  4:20     ` DJ Delorie
2022-03-14  9:48   ` [PATCH v2 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
2022-03-17  4:31     ` DJ Delorie
2022-03-17  6:22       ` Siddhesh Poyarekar
2022-03-14  9:48   ` [PATCH v2 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
2022-03-17  4:42     ` DJ Delorie
2022-03-17  4:59       ` Siddhesh Poyarekar
2022-03-14  9:48   ` [PATCH v2 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
2022-03-17  4:44     ` DJ Delorie
2022-03-14  9:48   ` [PATCH v2 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
2022-03-17  4:51     ` DJ Delorie
2022-03-14  9:48   ` [PATCH v2 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
2022-03-17  5:05     ` DJ Delorie
2022-03-17  5:11       ` Siddhesh Poyarekar
2022-03-14  9:48   ` [PATCH v2 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar
2022-03-17  5:06     ` DJ Delorie
2022-03-14 13:21 ` [PATCH 00/12] getaddrinfo facelift and fixes Cristian Rodríguez
2022-03-14 14:16   ` Siddhesh Poyarekar
2022-03-17  8:11 ` [PATCH v3 " Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 01/12] Simplify allocations and fix merge and continue actions [BZ #28931] Siddhesh Poyarekar
2022-03-17 21:47     ` DJ Delorie
2022-03-17  8:11   ` [PATCH v3 02/12] gaih_inet: Simplify canon name resolution Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 03/12] getaddrinfo: Fix leak with AI_ALL [BZ #28852] Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 04/12] gaih_inet: Simplify service resolution Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 05/12] gaih_inet: make numeric lookup a separate routine Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 06/12] gaih_inet: Split simple gethostbyname into its own function Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 07/12] gaih_inet: Split nscd lookup code " Siddhesh Poyarekar
2022-03-17 22:02     ` DJ Delorie
2022-03-17  8:11   ` [PATCH v3 08/12] gaih_inet: separate nss lookup loop " Siddhesh Poyarekar
2022-03-17 22:05     ` DJ Delorie
2022-03-17  8:11   ` [PATCH v3 09/12] gaih_inet: make gethosts into a function Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 10/12] gaih_inet: split loopback lookup into its own function Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 11/12] gaih_inet: Split result generation " Siddhesh Poyarekar
2022-03-17  8:11   ` [PATCH v3 12/12] gethosts: Return EAI_MEMORY on allocation failure Siddhesh Poyarekar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).