]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - nss/nss_files/files-hosts.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / nss / nss_files / files-hosts.c
index c96a39c38e03b7f956840e1bb8a9894ae957a1e6..4c51c90b4a99cd42e8d2fb449d0b028c82a4bcb4 100644 (file)
@@ -1,21 +1,20 @@
 /* Hosts file parser in nss_files module.
-   Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1996-2015 Free Software Foundation, Inc.
    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 Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
 #include <netinet/in.h>
@@ -55,232 +54,442 @@ LINE_PARSER
    STRING_FIELD (addr, isspace, 1);
 
    /* Parse address.  */
-   if (inet_pton (af, addr, entdata->host_addr) <= 0
-       && (af != AF_INET6 || (flags & AI_V4MAPPED) == 0
-          || inet_pton (AF_INET, addr, entdata->host_addr) <= 0
-          || (map_v4v6_address ((char *) entdata->host_addr,
-                                (char *) entdata->host_addr),
-              0)))
-     /* Illegal address: ignore line.  */
-     return 0;
+   if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
+       > 0)
+     af = af == AF_UNSPEC ? AF_INET : af;
+   else
+     {
+       if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
+          && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
+        map_v4v6_address ((char *) entdata->host_addr,
+                          (char *) entdata->host_addr);
+       else if (af == AF_INET
+               && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
+        {
+          if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
+            memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
+          else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
+            {
+              in_addr_t localhost = htonl (INADDR_LOOPBACK);
+              memcpy (entdata->host_addr, &localhost, sizeof (localhost));
+            }
+          else
+            /* Illegal address: ignore line.  */
+            return 0;
+        }
+       else if (af == AF_UNSPEC
+               && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
+        af = AF_INET6;
+       else
+        /* Illegal address: ignore line.  */
+        return 0;
+     }
 
    /* We always return entries of the requested form.  */
    result->h_addrtype = af;
    result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
 
    /* Store a pointer to the address in the expected form.  */
-   entdata->h_addr_ptrs[0] = entdata->host_addr;
+   entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
    entdata->h_addr_ptrs[1] = NULL;
    result->h_addr_list = entdata->h_addr_ptrs;
 
    STRING_FIELD (result->h_name, isspace, 1);
  })
 
-
-
-#define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
-enum nss_status                                                                      \
-_nss_files_get##name##_r (proto,                                             \
-                         struct STRUCTURE *result, char *buffer,             \
-                         size_t buflen, int *errnop H_ERRNO_PROTO)           \
-{                                                                            \
-  enum nss_status status;                                                    \
-                                                                             \
-  __libc_lock_lock (lock);                                                   \
-                                                                             \
-  /* Reset file pointer to beginning or open file.  */                       \
-  status = internal_setent (keep_stream);                                    \
-                                                                             \
-  if (status == NSS_STATUS_SUCCESS)                                          \
-    {                                                                        \
-      /* Tell getent function that we have repositioned the file pointer.  */ \
-      last_use = getby;                                                              \
-                                                                             \
-      while ((status = internal_getent (result, buffer, buflen, errnop       \
-                                       H_ERRNO_ARG EXTRA_ARGS_VALUE))        \
-            == NSS_STATUS_SUCCESS)                                           \
-       { break_if_match }                                                    \
-                                                                             \
-      if (status == NSS_STATUS_SUCCESS                                       \
-         && _res_hconf.flags & HCONF_FLAG_MULTI)                             \
-       {                                                                     \
-         /* We have to get all host entries from the file.  */               \
-         const size_t tmp_buflen = MIN (buflen, 4096);                       \
-         char tmp_buffer[tmp_buflen];                                        \
-         struct hostent tmp_result_buf;                                      \
-         int naddrs = 1;                                                     \
-         int naliases = 0;                                                   \
-         char *bufferend;                                                    \
-                                                                             \
-         while (result->h_aliases[naliases] != NULL)                         \
-           ++naliases;                                                       \
-                                                                             \
-         bufferend = (char *) &result->h_aliases[naliases + 1];              \
-                                                                             \
-         while ((status = internal_getent (&tmp_result_buf, tmp_buffer,      \
-                                           tmp_buflen, errnop H_ERRNO_ARG    \
-                                           EXTRA_ARGS_VALUE))                \
-                == NSS_STATUS_SUCCESS)                                       \
-           {                                                                 \
-             int matches = 1;                                                \
-             struct hostent *old_result = result;                            \
-             result = &tmp_result_buf;                                       \
-             /* The following piece is a bit clumsy but we want to use the   \
-                `break_if_match' value.  The optimizer should do its         \
-                job.  */                                                     \
-             do                                                              \
-               {                                                             \
-                 break_if_match                                              \
-                 result = old_result;                                        \
-               }                                                             \
-             while ((matches = 0));                                          \
-                                                                             \
-             if (matches)                                                    \
-               {                                                             \
-                 /* We could be very clever and try to recycle a few bytes   \
-                    in the buffer instead of generating new arrays.  But     \
-                    we are not doing this here since it's more work than     \
-                    it's worth.  Simply let the user provide a bit bigger    \
-                    buffer.  */                                              \
-                 char **new_h_addr_list;                                     \
-                 char **new_h_aliases;                                       \
-                 int newaliases = 0;                                         \
-                 size_t newstrlen = 0;                                       \
-                 int cnt;                                                    \
-                                                                             \
-                 /* Count the new aliases and the length of the strings.  */ \
-                 while (tmp_result_buf.h_aliases[newaliases] != NULL)        \
-                   {                                                         \
-                     char *cp = tmp_result_buf.h_aliases[newaliases];        \
-                     ++newaliases;                                           \
-                     newstrlen += strlen (cp) + 1;                           \
-                   }                                                         \
-                 /* If the real name is different add it also to the         \
-                    aliases.  This means that there is a duplication         \
-                    in the alias list but this is really the users           \
-                    problem.  */                                             \
-                 if (strcmp (old_result->h_name,                             \
-                             tmp_result_buf.h_name) != 0)                    \
-                   {                                                         \
-                     ++newaliases;                                           \
-                     newstrlen += strlen (tmp_result_buf.h_name) + 1;        \
-                   }                                                         \
-                                                                             \
-                 /* Now we can check whether the buffer is large enough.  */ \
-                 if (bufferend + 16 + (naddrs + 2) * sizeof (char *)         \
-                     + roundup (newstrlen, sizeof (char *))                  \
-                     + (naliases + newaliases + 1) * sizeof (char *)         \
-                     >= buffer + buflen)                                     \
-                   {                                                         \
-                     *errnop = ERANGE;                                       \
-                     status = NSS_STATUS_TRYAGAIN;                           \
-                     break;                                                  \
-                   }                                                         \
-                                                                             \
-                 new_h_addr_list =                                           \
-                   (char **) (bufferend                                      \
-                              + roundup (newstrlen, sizeof (char *))         \
-                              + 16);                                         \
-                 new_h_aliases =                                             \
-                   (char **) ((char *) new_h_addr_list                       \
-                              + (naddrs + 2) * sizeof (char *));             \
-                                                                             \
-                 /* Copy the old data in the new arrays.  */                 \
-                 for (cnt = 0; cnt < naddrs; ++cnt)                          \
-                   new_h_addr_list[cnt] = old_result->h_addr_list[cnt];      \
-                                                                             \
-                 for (cnt = 0; cnt < naliases; ++cnt)                        \
-                   new_h_aliases[cnt] = old_result->h_aliases[cnt];          \
-                                                                             \
-                 /* Store the new strings.  */                               \
-                 cnt = 0;                                                    \
-                 while (tmp_result_buf.h_aliases[cnt] != NULL)               \
-                   {                                                         \
-                     new_h_aliases[naliases++] = bufferend;                  \
-                     bufferend = (__stpcpy (bufferend,                       \
-                                            tmp_result_buf.h_aliases[cnt])   \
-                                  + 1);                                      \
-                   }                                                         \
-                                                                             \
-                 if (cnt < newaliases)                                       \
-                   {                                                         \
-                     new_h_aliases[naliases++] = bufferend;                  \
-                     bufferend = __stpcpy (bufferend,                        \
-                                           tmp_result_buf.h_name) + 1;       \
-                   }                                                         \
-                                                                             \
-                 /* Final NULL pointer.  */                                  \
-                 new_h_aliases[naliases] = NULL;                             \
-                                                                             \
-                 /* Round up the buffer end address.  */                     \
-                 bufferend += (sizeof (char *)                               \
-                               - ((bufferend - (char *) 0)                   \
-                                  % sizeof (char *)));                       \
-                                                                             \
-                 /* Now the new address.  */                                 \
-                 new_h_addr_list[naddrs++] =                                 \
-                   memcpy (bufferend, tmp_result_buf.h_addr,                 \
-                           tmp_result_buf.h_length);                         \
-                                                                             \
-                 /* Also here a final NULL pointer.  */                      \
-                 new_h_addr_list[naddrs] = NULL;                             \
-                                                                             \
-                 /* Store the new array pointers.  */                        \
-                 old_result->h_aliases = new_h_aliases;                      \
-                 old_result->h_addr_list = new_h_addr_list;                  \
-                                                                             \
-                 /* Compute the new buffer end.  */                          \
-                 bufferend = (char *) &new_h_aliases[naliases + 1];          \
-                 assert (bufferend <= buffer + buflen);                      \
-               }                                                             \
-           }                                                                 \
-                                                                             \
-         if (status != NSS_STATUS_TRYAGAIN)                                  \
-           status = NSS_STATUS_SUCCESS;                                      \
-       }                                                                     \
-                                                                             \
-                                                                             \
-      if (! keep_stream)                                                     \
-       internal_endent ();                                                   \
-    }                                                                        \
-                                                                             \
-  __libc_lock_unlock (lock);                                                 \
-                                                                             \
-  return status;                                                             \
-}
-
-
 #define EXTRA_ARGS_VALUE \
   , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),                   \
   ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
 #include "files-XXX.c"
-HOST_DB_LOOKUP (hostbyname, ,,
-               {
-                 LOOKUP_NAME_CASE (h_name, h_aliases)
-               }, const char *name)
-
-
 #undef EXTRA_ARGS_VALUE
-/* XXX Is using _res to determine whether we want to convert IPv4 addresses
-   to IPv6 addresses really the right thing to do?  */
-#define EXTRA_ARGS_VALUE \
-  , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
-HOST_DB_LOOKUP (hostbyname2, ,,
-               {
-                 LOOKUP_NAME_CASE (h_name, h_aliases)
-               }, const char *name, int af)
 
-DB_LOOKUP (hostbyaddr, ,,
+/* We only need to consider IPv4 mapped addresses if the input to the
+   gethostbyaddr() function is an IPv6 address.  */
+#define EXTRA_ARGS_VALUE \
+  , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
+DB_LOOKUP (hostbyaddr, ,,,
           {
-            if (result->h_length == len
+            if (result->h_length == (int) len
                 && ! memcmp (addr, result->h_addr_list[0], len))
               break;
           }, const void *addr, socklen_t len, int af)
-
 #undef EXTRA_ARGS_VALUE
-#define EXTRA_ARGS_VALUE \
-  , af, flags
-HOST_DB_LOOKUP (ipnodebyname, ,,
+
+enum nss_status
+_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
+                            char *buffer, size_t buflen, int *errnop,
+                            int *herrnop, int32_t *ttlp, char **canonp)
+{
+  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
+  buffer += pad;
+  buflen = buflen > pad ? buflen - pad : 0;
+
+  __libc_lock_lock (lock);
+
+  /* Reset file pointer to beginning or open file.  */
+  enum nss_status status = internal_setent (keep_stream);
+
+  if (status == NSS_STATUS_SUCCESS)
+    {
+      /* XXX Is using _res to determine whether we want to convert IPv4
+         addresses to IPv6 addresses really the right thing to do?  */
+      int flags = ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0);
+
+      /* Tell getent function that we have repositioned the file pointer.  */
+      last_use = getby;
+
+      while ((status = internal_getent (result, buffer, buflen, errnop,
+                                       herrnop, af, flags))
+            == NSS_STATUS_SUCCESS)
+       {
+         LOOKUP_NAME_CASE (h_name, h_aliases)
+       }
+
+      if (status == NSS_STATUS_SUCCESS
+         && _res_hconf.flags & HCONF_FLAG_MULTI)
+       {
+         /* We have to get all host entries from the file.  */
+         size_t tmp_buflen = MIN (buflen, 4096);
+         char tmp_buffer_stack[tmp_buflen]
+           __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
+         char *tmp_buffer = tmp_buffer_stack;
+         struct hostent tmp_result_buf;
+         int naddrs = 1;
+         int naliases = 0;
+         char *bufferend;
+         bool tmp_buffer_malloced = false;
+
+         while (result->h_aliases[naliases] != NULL)
+           ++naliases;
+
+         bufferend = (char *) &result->h_aliases[naliases + 1];
+
+       again:
+         while ((status = internal_getent (&tmp_result_buf, tmp_buffer,
+                                           tmp_buflen, errnop, herrnop, af,
+                                           flags))
+                == NSS_STATUS_SUCCESS)
+           {
+             int matches = 1;
+             struct hostent *old_result = result;
+             result = &tmp_result_buf;
+             /* The following piece is a bit clumsy but we want to use the
+                `LOOKUP_NAME_CASE' value.  The optimizer should do its
+                job.  */
+             do
                {
                  LOOKUP_NAME_CASE (h_name, h_aliases)
-               }, const char *name, int af, int flags)
+                 result = old_result;
+               }
+             while ((matches = 0));
+
+             if (matches)
+               {
+                 /* We could be very clever and try to recycle a few bytes
+                    in the buffer instead of generating new arrays.  But
+                    we are not doing this here since it's more work than
+                    it's worth.  Simply let the user provide a bit bigger
+                    buffer.  */
+                 char **new_h_addr_list;
+                 char **new_h_aliases;
+                 int newaliases = 0;
+                 size_t newstrlen = 0;
+                 int cnt;
+
+                 /* Count the new aliases and the length of the strings.  */
+                 while (tmp_result_buf.h_aliases[newaliases] != NULL)
+                   {
+                     char *cp = tmp_result_buf.h_aliases[newaliases];
+                     ++newaliases;
+                     newstrlen += strlen (cp) + 1;
+                   }
+                 /* If the real name is different add it also to the
+                    aliases.  This means that there is a duplication
+                    in the alias list but this is really the user's
+                    problem.  */
+                 if (strcmp (old_result->h_name,
+                             tmp_result_buf.h_name) != 0)
+                   {
+                     ++newaliases;
+                     newstrlen += strlen (tmp_result_buf.h_name) + 1;
+                   }
+
+                 /* Make sure bufferend is aligned.  */
+                 assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
+
+                 /* Now we can check whether the buffer is large enough.
+                    16 is the maximal size of the IP address.  */
+                 if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
+                     + roundup (newstrlen, sizeof (char *))
+                     + (naliases + newaliases + 1) * sizeof (char *)
+                     >= buffer + buflen)
+                   {
+                     *errnop = ERANGE;
+                     *herrnop = NETDB_INTERNAL;
+                     status = NSS_STATUS_TRYAGAIN;
+                     goto out;
+                   }
+
+                 new_h_addr_list =
+                   (char **) (bufferend
+                              + roundup (newstrlen, sizeof (char *))
+                              + 16);
+                 new_h_aliases =
+                   (char **) ((char *) new_h_addr_list
+                              + (naddrs + 2) * sizeof (char *));
+
+                 /* Copy the old data in the new arrays.  */
+                 for (cnt = 0; cnt < naddrs; ++cnt)
+                   new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
+
+                 for (cnt = 0; cnt < naliases; ++cnt)
+                   new_h_aliases[cnt] = old_result->h_aliases[cnt];
+
+                 /* Store the new strings.  */
+                 cnt = 0;
+                 while (tmp_result_buf.h_aliases[cnt] != NULL)
+                   {
+                     new_h_aliases[naliases++] = bufferend;
+                     bufferend = (__stpcpy (bufferend,
+                                            tmp_result_buf.h_aliases[cnt])
+                                  + 1);
+                     ++cnt;
+                   }
+
+                 if (cnt < newaliases)
+                   {
+                     new_h_aliases[naliases++] = bufferend;
+                     bufferend = __stpcpy (bufferend,
+                                           tmp_result_buf.h_name) + 1;
+                   }
+
+                 /* Final NULL pointer.  */
+                 new_h_aliases[naliases] = NULL;
+
+                 /* Round up the buffer end address.  */
+                 bufferend += (sizeof (char *)
+                               - ((bufferend - (char *) 0)
+                                  % sizeof (char *))) % sizeof (char *);
+
+                 /* Now the new address.  */
+                 new_h_addr_list[naddrs++] =
+                   memcpy (bufferend, tmp_result_buf.h_addr,
+                           tmp_result_buf.h_length);
+
+                 /* Also here a final NULL pointer.  */
+                 new_h_addr_list[naddrs] = NULL;
+
+                 /* Store the new array pointers.  */
+                 old_result->h_aliases = new_h_aliases;
+                 old_result->h_addr_list = new_h_addr_list;
+
+                 /* Compute the new buffer end.  */
+                 bufferend = (char *) &new_h_aliases[naliases + 1];
+                 assert (bufferend <= buffer + buflen);
+
+                 result = old_result;
+               }
+           }
+
+         if (status == NSS_STATUS_TRYAGAIN)
+           {
+             size_t newsize = 2 * tmp_buflen;
+             if (tmp_buffer_malloced)
+               {
+                 char *newp = realloc (tmp_buffer, newsize);
+                 if (newp != NULL)
+                   {
+                     assert ((((uintptr_t) newp)
+                              & (__alignof__ (struct hostent_data) - 1))
+                             == 0);
+                     tmp_buffer = newp;
+                     tmp_buflen = newsize;
+                     goto again;
+                   }
+               }
+             else if (!__libc_use_alloca (buflen + newsize))
+               {
+                 tmp_buffer = malloc (newsize);
+                 if (tmp_buffer != NULL)
+                   {
+                     assert ((((uintptr_t) tmp_buffer)
+                              & (__alignof__ (struct hostent_data) - 1))
+                             == 0);
+                     tmp_buffer_malloced = true;
+                     tmp_buflen = newsize;
+                     goto again;
+                   }
+               }
+             else
+               {
+                 tmp_buffer
+                   = extend_alloca (tmp_buffer, tmp_buflen,
+                                    newsize
+                                    + __alignof__ (struct hostent_data));
+                 tmp_buffer = (char *) (((uintptr_t) tmp_buffer
+                                         + __alignof__ (struct hostent_data)
+                                         - 1)
+                                        & ~(__alignof__ (struct hostent_data)
+                                            - 1));
+                 goto again;
+               }
+           }
+         else
+           status = NSS_STATUS_SUCCESS;
+       out:
+         if (tmp_buffer_malloced)
+           free (tmp_buffer);
+       }
+
+      if (! keep_stream)
+       internal_endent ();
+    }
+
+  if (canonp && status == NSS_STATUS_SUCCESS)
+    *canonp = result->h_name;
+
+  __libc_lock_unlock (lock);
+
+  return status;
+}
+
+enum nss_status
+_nss_files_gethostbyname_r (const char *name, struct hostent *result,
+                           char *buffer, size_t buflen, int *errnop,
+                           int *herrnop)
+{
+  int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
+
+  return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
+                                     errnop, herrnop, NULL, NULL);
+}
+
+enum nss_status
+_nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
+                            char *buffer, size_t buflen, int *errnop,
+                            int *herrnop)
+{
+  return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
+                                     errnop, herrnop, NULL, NULL);
+}
+
+enum nss_status
+_nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+                            char *buffer, size_t buflen, int *errnop,
+                            int *herrnop, int32_t *ttlp)
+{
+  __libc_lock_lock (lock);
+
+  /* Reset file pointer to beginning or open file.  */
+  enum nss_status status = internal_setent (keep_stream);
+
+  if (status == NSS_STATUS_SUCCESS)
+    {
+      /* Tell getent function that we have repositioned the file pointer.  */
+      last_use = getby;
+
+      bool any = false;
+      bool got_canon = false;
+      while (1)
+       {
+         /* Align the buffer for the next record.  */
+         uintptr_t pad = (-(uintptr_t) buffer
+                          % __alignof__ (struct hostent_data));
+         buffer += pad;
+         buflen = buflen > pad ? buflen - pad : 0;
+
+         struct hostent result;
+         status = internal_getent (&result, buffer, buflen, errnop,
+                                   herrnop, AF_UNSPEC, 0);
+         if (status != NSS_STATUS_SUCCESS)
+           break;
+
+         int naliases = 0;
+         if (__strcasecmp (name, result.h_name) != 0)
+           {
+             for (; result.h_aliases[naliases] != NULL; ++naliases)
+               if (! __strcasecmp (name, result.h_aliases[naliases]))
+                 break;
+             if (result.h_aliases[naliases] == NULL)
+               continue;
+
+             /* We know this alias exist.  Count it.  */
+             ++naliases;
+           }
+
+         /* Determine how much memory has been used so far.  */
+         // XXX It is not necessary to preserve the aliases array
+         while (result.h_aliases[naliases] != NULL)
+           ++naliases;
+         char *bufferend = (char *) &result.h_aliases[naliases + 1];
+         assert (buflen >= bufferend - buffer);
+         buflen -= bufferend - buffer;
+         buffer = bufferend;
+
+         /* We found something.  */
+         any = true;
+
+         /* Create the record the caller expects.  There is only one
+            address.  */
+         assert (result.h_addr_list[1] == NULL);
+         if (*pat == NULL)
+           {
+             uintptr_t pad = (-(uintptr_t) buffer
+                              % __alignof__ (struct gaih_addrtuple));
+             buffer += pad;
+             buflen = buflen > pad ? buflen - pad : 0;
+
+             if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
+                                   0))
+               {
+                 *errnop = ERANGE;
+                 *herrnop = NETDB_INTERNAL;
+                 status = NSS_STATUS_TRYAGAIN;
+                 break;
+               }
+
+             *pat = (struct gaih_addrtuple *) buffer;
+             buffer += sizeof (struct gaih_addrtuple);
+             buflen -= sizeof (struct gaih_addrtuple);
+           }
+
+         (*pat)->next = NULL;
+         (*pat)->name = got_canon ? NULL : result.h_name;
+         got_canon = true;
+         (*pat)->family = result.h_addrtype;
+         memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
+         (*pat)->scopeid = 0;
+
+         pat = &((*pat)->next);
+
+         /* If we only look for the first matching entry we are done.  */
+         if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
+           break;
+       }
+
+      /* If we have to look for multiple records and found one, this
+        is a success.  */
+      if (status == NSS_STATUS_NOTFOUND && any)
+       {
+         assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
+         status = NSS_STATUS_SUCCESS;
+       }
+
+      if (! keep_stream)
+       internal_endent ();
+    }
+  else if (status == NSS_STATUS_TRYAGAIN)
+    {
+      *errnop = errno;
+      *herrnop = TRY_AGAIN;
+    }
+  else
+    {
+      *errnop = errno;
+      *herrnop = NO_DATA;
+    }
+
+  __libc_lock_unlock (lock);
+
+  return status;
+}