]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
gaih_inet: make numeric lookup a separate routine
authorSiddhesh Poyarekar <siddhesh@sourceware.org>
Mon, 7 Mar 2022 08:38:51 +0000 (14:08 +0530)
committerSiddhesh Poyarekar <siddhesh@sourceware.org>
Tue, 22 Mar 2022 14:09:17 +0000 (19:39 +0530)
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

index dae5e9f55fd1b5a57a842c52bbf45cb70be15a9d..19bb13db599e833b13707306e1651f70c8e9e8f4 100644 (file)
@@ -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;
 }