* include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend
authorUlrich Drepper <drepper@redhat.com>
Sat, 10 May 2008 23:27:39 +0000 (23:27 +0000)
committerUlrich Drepper <drepper@redhat.com>
Sat, 10 May 2008 23:27:39 +0000 (23:27 +0000)
prototypes.
* include/arpa/nameser_compat.h: Define T_UNSPEC.
* nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r.
(libnss_nisplus): Export _nss_nisplus_gethostbyname4_r.
* nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle
af==AF_UNSPEC.
(_nss_nis_gethostbyname4_r): New function.
* nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent):
Change to also handle af==AF_UNSPEC.
(get_tablename): New function.  Use it to avoid duplication.
(_nss_nisplus_gethostbyname4_r): New function.
* nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is
available.
* nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r.
* nss/nss.h: Define struct gaih_addrtuple.
* nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle
af==AF_UNSPEC.
(_nss_files_gethostbyname4_r): New function.
* resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r.
* resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery
calls.
* resolv/res_query.c (__libc_res_nquery): Take two additional
parameters for second answer buffer.  Handle type=T_UNSPEC to mean
look up IPv4 and IPv6.
Change all callers.
* resolv/res_send.c (__libc_res_nsend): Take five aditional parameters
for an additional query and answer buffer.  Pass to send_vc and
send_dg.
(send_vc): Send possibly two requests and receive two answers.
(send_dg): Likewise.
* resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and
__libc_res_nquery.
(_nss_dns_gethostbyname4_r): New function.
(gaih_getanswer_slice): Likewise.
(gaih_getanswer): Likewise.
* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust
__libc_res_nquery call.
* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise.
(_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call.
* sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is
available.

17 files changed:
ChangeLog
include/arpa/nameser_compat.h
nis/Versions
nis/nss_nis/nis-hosts.c
nis/nss_nisplus/nisplus-hosts.c
nscd/aicache.c
nss/Versions
nss/nss.h
nss/nss_files/files-hosts.c
resolv/Versions
resolv/gethnamaddr.c
resolv/nss_dns/dns-canon.c
resolv/nss_dns/dns-host.c
resolv/nss_dns/dns-network.c
resolv/res_query.c
resolv/res_send.c
sysdeps/posix/getaddrinfo.c

index cbeb159..c20a02d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,48 @@
+2008-05-10  Ulrich Drepper  <drepper@redhat.com>
+
+       * include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend
+       prototypes.
+       * include/arpa/nameser_compat.h: Define T_UNSPEC.
+       * nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r.
+       (libnss_nisplus): Export _nss_nisplus_gethostbyname4_r.
+       * nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle
+       af==AF_UNSPEC.
+       (_nss_nis_gethostbyname4_r): New function.
+       * nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent):
+       Change to also handle af==AF_UNSPEC.
+       (get_tablename): New function.  Use it to avoid duplication.
+       (_nss_nisplus_gethostbyname4_r): New function.
+       * nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is
+       available.
+       * nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r.
+       * nss/nss.h: Define struct gaih_addrtuple.
+       * nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle
+       af==AF_UNSPEC.
+       (_nss_files_gethostbyname4_r): New function.
+       * resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r.
+       * resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery
+       calls.
+       * resolv/res_query.c (__libc_res_nquery): Take two additional
+       parameters for second answer buffer.  Handle type=T_UNSPEC to mean
+       look up IPv4 and IPv6.
+       Change all callers.
+       * resolv/res_send.c (__libc_res_nsend): Take five aditional parameters
+       for an additional query and answer buffer.  Pass to send_vc and
+       send_dg.
+       (send_vc): Send possibly two requests and receive two answers.
+       (send_dg): Likewise.
+       * resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and
+       __libc_res_nquery.
+       (_nss_dns_gethostbyname4_r): New function.
+       (gaih_getanswer_slice): Likewise.
+       (gaih_getanswer): Likewise.
+       * resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust
+       __libc_res_nquery call.
+       * resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise.
+       (_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call.
+       * sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is
+       available.
+
 2008-05-05  David S. Miller  <davem@davemloft.net>
 
        * sysdeps/sparc/sparc32/Makefile: Use -mcpu=v7 for initfini.s build.
index bfbb2de..2e735ed 100644 (file)
@@ -1 +1,8 @@
+#ifndef _ARPA_NAMESER_COMPAT_
 #include <resolv/arpa/nameser_compat.h>
+
+/* Picksome unused number to represent lookups of IPv4 and IPv6 (i.e.,
+   T_A and T_AAAA).  */
+#define T_UNSPEC 62321
+
+#endif
index be4453e..ef9a512 100644 (file)
@@ -95,7 +95,7 @@ libnss_nis {
     _nss_nis_setgrent; _nss_nis_sethostent; _nss_nis_setnetent;
     _nss_nis_setnetgrent; _nss_nis_setprotoent; _nss_nis_setpwent;
     _nss_nis_setrpcent; _nss_nis_setservent; _nss_nis_setspent;
-    _nss_nis_initgroups_dyn;
+    _nss_nis_initgroups_dyn; _nss_nis_gethostbyname4_r;
   }
 }
 
@@ -126,5 +126,6 @@ libnss_nisplus {
     _nss_nisplus_setnetent; _nss_nisplus_setnetgrent; _nss_nisplus_setprotoent;
     _nss_nisplus_setpwent; _nss_nisplus_setrpcent; _nss_nisplus_setservent;
     _nss_nisplus_setspent; _nss_nisplus_initgroups_dyn;
+    _nss_nisplus_gethostbyname4_r;
   }
 }
index 7bf4af7..24d1363 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright (C) 1996-2000, 2002, 2003, 2006, 2007 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2000, 2002, 2003, 2006, 2007, 2008
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
@@ -17,6 +18,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <nss.h>
 #include <ctype.h>
 /* The following is an ugly trick to avoid a prototype declaration for
@@ -61,9 +63,12 @@ LINE_PARSER
 
    STRING_FIELD (addr, isspace, 1);
 
+   assert (af == AF_INET || af == AF_INET6 || af == AF_UNSPEC);
+
    /* Parse address.  */
-   if (af == AF_INET && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
+   if (af != AF_INET6 && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
      {
+       assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
        if (flags & AI_V4MAPPED)
          {
            map_v4v6_address ((char *) entdata->host_addr,
@@ -77,7 +82,7 @@ LINE_PARSER
            result->h_length = INADDRSZ;
          }
      }
-   else if (af == AF_INET6
+   else if (af != AF_INET
             && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
      {
        result->h_addrtype = AF_INET6;
@@ -102,6 +107,7 @@ static bool_t new_start = 1;
 static char *oldkey = NULL;
 static int oldkeylen = 0;
 
+
 enum nss_status
 _nss_nis_sethostent (int stayopen)
 {
@@ -124,6 +130,7 @@ _nss_nis_sethostent (int stayopen)
    is used so this makes no difference.  */
 strong_alias (_nss_nis_sethostent, _nss_nis_endhostent)
 
+
 /* The calling function always need to get a lock first. */
 static enum nss_status
 internal_nis_gethostent_r (struct hostent *host, char *buffer,
@@ -216,6 +223,7 @@ internal_nis_gethostent_r (struct hostent *host, char *buffer,
   return NSS_STATUS_SUCCESS;
 }
 
+
 enum nss_status
 _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
                       int *errnop, int *h_errnop)
@@ -233,6 +241,7 @@ _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
   return status;
 }
 
+
 static enum nss_status
 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
                           char *buffer, size_t buflen, int *errnop,
@@ -323,16 +332,24 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
   return NSS_STATUS_SUCCESS;
 }
 
+
 enum nss_status
 _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host,
                           char *buffer, size_t buflen, int *errnop,
                           int *h_errnop)
 {
+  if (af != AF_INET && af != AF_INET6)
+    {
+      *h_errnop = HOST_NOT_FOUND;
+      return NSS_STATUS_NOTFOUND;
+    }
+
   return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
                                    h_errnop,
                        ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
 }
 
+
 enum nss_status
 _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
                          size_t buflen, int *errnop, int *h_errnop)
@@ -351,6 +368,7 @@ _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
                                    errnop, h_errnop, 0);
 }
 
+
 enum nss_status
 _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
                          struct hostent *host, char *buffer, size_t buflen,
@@ -430,13 +448,93 @@ _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
   return NSS_STATUS_SUCCESS;
 }
 
-#if 0
+
 enum nss_status
-_nss_nis_getipnodebyname_r (const char *name, int af, int flags,
-                           struct hostent *result, char *buffer,
-                           size_t buflen, int *errnop, int *herrnop)
+_nss_nis_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+                          char *buffer, size_t buflen, int *errnop,
+                          int *herrnop, int32_t *ttlp)
 {
-  return internal_gethostbyname2_r (name, af, result, buffer, buflen,
-                                   errnop, herrnop, flags);
+  char *domain;
+  if (yp_get_default_domain (&domain))
+    return NSS_STATUS_UNAVAIL;
+
+  /* Convert name to lowercase.  */
+  size_t namlen = strlen (name);
+  char name2[namlen + 1];
+  size_t i;
+
+  for (i = 0; i < namlen; ++i)
+    name2[i] = tolower (name[i]);
+  name2[i] = '\0';
+
+  char *result;
+  int len;
+  int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
+
+  if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
+    {
+      enum nss_status retval = yperr2nss (yperr);
+
+      if (retval == NSS_STATUS_TRYAGAIN)
+       {
+         *herrnop = TRY_AGAIN;
+         *errnop = errno;
+       }
+      if (retval == NSS_STATUS_NOTFOUND)
+       *herrnop = HOST_NOT_FOUND;
+      return retval;
+    }
+
+  struct parser_data data;
+  struct hostent host;
+  int parse_res = parse_line (result, &host, &data, buflen, errnop, AF_UNSPEC,
+                             0);
+  if (__builtin_expect (parse_res < 1, 0))
+    {
+      if (parse_res == -1)
+       {
+         *herrnop = NETDB_INTERNAL;
+         return NSS_STATUS_TRYAGAIN;
+       }
+      else
+       {
+         *herrnop = HOST_NOT_FOUND;
+         return NSS_STATUS_NOTFOUND;
+       }
+    }
+
+  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))
+       {
+       erange:
+         free (result);
+         *errnop = ERANGE;
+         *herrnop = NETDB_INTERNAL;
+         return NSS_STATUS_TRYAGAIN;
+       }
+
+      *pat = (struct gaih_addrtuple *) buffer;
+      buffer += sizeof (struct gaih_addrtuple);
+      buflen -= sizeof (struct gaih_addrtuple);
+    }
+
+  (*pat)->next = NULL;
+  size_t h_name_len = strlen (host.h_name);
+  if (h_name_len >= buflen)
+    goto erange;
+  (*pat)->name = memcpy (buffer, host.h_name, h_name_len + 1);
+  (*pat)->family = host.h_addrtype;
+  memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
+  (*pat)->scopeid = 0;
+  assert (host.h_addr_list[1] == NULL);
+
+  free (result);
+
+  return NSS_STATUS_SUCCESS;
 }
-#endif
index f5f0ac9..37d4477 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1997-2002, 2003, 2005, 2006 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2003, 2005, 2006, 2008 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
 
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <atomic.h>
 #include <ctype.h>
 #include <errno.h>
@@ -58,15 +59,15 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
   if (result == NULL)
     return 0;
 
-  if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) ||
-      __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ ||
-      strcmp(NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0 ||
-      NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4)
+  if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
+      || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
+      || strcmp (NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0
+      || NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4)
     return 0;
 
   char *data = first_unused;
 
-  if (room_left < (af == AF_INET6 || (flags & AI_V4MAPPED) != 0
+  if (room_left < (af != AF_INET || (flags & AI_V4MAPPED) != 0
                   ? IN6ADDRSZ : INADDRSZ))
     {
     no_more_room:
@@ -75,8 +76,10 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
     }
 
   /* Parse address.  */
-  if (af == AF_INET && inet_pton (af, NISENTRYVAL (0, 2, result), data) > 0)
+  if (af != AF_INET6
+      && inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
     {
+      assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
       if (flags & AI_V4MAPPED)
        {
          map_v4v6_address (data, data);
@@ -89,7 +92,7 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
          host->h_length = INADDRSZ;
        }
     }
-  else if (af == AF_INET6
+  else if (af != AF_INET
           && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
     {
       host->h_addrtype = AF_INET6;
@@ -109,27 +112,33 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
   first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
                            NISENTRYLEN (0, 0, result));
   *first_unused++ = '\0';
-  room_left -= NISENTRYLEN (0, 0, result) + 1;
 
-  /* XXX Rewrite at some point to allocate the array first and then
-     copy the strings.  It wasteful to first concatenate the strings
-     to just split them again later.  */
+  room_left -= NISENTRYLEN (0, 0, result) + 1;
   char *line = first_unused;
-  for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
+
+  /* When this is a call to gethostbyname4_r we do not need the aliases.  */
+  if (af != AF_UNSPEC)
     {
-      if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
+      /* XXX Rewrite at some point to allocate the array first and then
+        copy the strings.  It is wasteful to first concatenate the strings
+        to just split them again later.  */
+      for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
        {
-         if (NISENTRYLEN (i, 1, result) + 2 > room_left)
-           goto no_more_room;
-
-         *first_unused++ = ' ';
-         first_unused = __stpncpy (first_unused, NISENTRYVAL (i, 1, result),
-                                   NISENTRYLEN (i, 1, result));
-         *first_unused = '\0';
-         room_left -= NISENTRYLEN (i, 1, result) + 1;
+         if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
+           {
+             if (NISENTRYLEN (i, 1, result) + 2 > room_left)
+               goto no_more_room;
+
+             *first_unused++ = ' ';
+             first_unused = __stpncpy (first_unused,
+                                       NISENTRYVAL (i, 1, result),
+                                       NISENTRYLEN (i, 1, result));
+             *first_unused = '\0';
+             room_left -= NISENTRYLEN (i, 1, result) + 1;
+           }
        }
+      *first_unused++ = '\0';
     }
-  *first_unused++ = '\0';
 
   /* Adjust the pointer so it is aligned for
      storing pointers.  */
@@ -147,30 +156,34 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
   host->h_addr_list[1] = NULL;
   host->h_aliases = &host->h_addr_list[2];
 
-  i = 0;
-  while (*line != '\0')
+  /* When this is a call to gethostbyname4_r we do not need the aliases.  */
+  if (af != AF_UNSPEC)
     {
-      /* Skip leading blanks.  */
-      while (isspace (*line))
-       ++line;
+      i = 0;
+      while (*line != '\0')
+       {
+         /* Skip leading blanks.  */
+         while (isspace (*line))
+           ++line;
 
-      if (*line == '\0')
-       break;
+         if (*line == '\0')
+           break;
 
-      if (room_left < sizeof (char *))
-       goto no_more_room;
+         if (room_left < sizeof (char *))
+           goto no_more_room;
 
-      room_left -= sizeof (char *);
-      host->h_aliases[i++] = line;
+         room_left -= sizeof (char *);
+         host->h_aliases[i++] = line;
 
-      while (*line != '\0' && *line != ' ')
-       ++line;
+         while (*line != '\0' && *line != ' ')
+           ++line;
 
-      if (*line == ' ')
-       *line++ = '\0';
-    }
+         if (*line == ' ')
+           *line++ = '\0';
+       }
 
-  host->h_aliases[i] = NULL;
+      host->h_aliases[i] = NULL;
+    }
 
   return 1;
 }
@@ -204,6 +217,7 @@ _nss_create_tablename (int *errnop)
   return NSS_STATUS_SUCCESS;
 }
 
+
 enum nss_status
 _nss_nisplus_sethostent (int stayopen)
 {
@@ -226,6 +240,7 @@ _nss_nisplus_sethostent (int stayopen)
   return status;
 }
 
+
 enum nss_status
 _nss_nisplus_endhostent (void)
 {
@@ -242,6 +257,7 @@ _nss_nisplus_endhostent (void)
   return NSS_STATUS_SUCCESS;
 }
 
+
 static enum nss_status
 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
                               size_t buflen, int *errnop, int *herrnop)
@@ -329,6 +345,7 @@ internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
   return NSS_STATUS_SUCCESS;
 }
 
+
 enum nss_status
 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
                           size_t buflen, int *errnop, int *herrnop)
@@ -345,26 +362,33 @@ _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
   return status;
 }
 
+
+static enum nss_status
+get_tablename (int *herrnop)
+{
+  __libc_lock_lock (lock);
+
+  enum nss_status status = _nss_create_tablename (herrnop);
+
+  __libc_lock_unlock (lock);
+
+  if (status != NSS_STATUS_SUCCESS)
+    *herrnop = NETDB_INTERNAL;
+
+  return status;
+}
+
+
 static enum nss_status
 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
                           char *buffer, size_t buflen, int *errnop,
                           int *herrnop, int flags)
 {
-  int parse_res, retval;
-
   if (tablename_val == NULL)
     {
-      __libc_lock_lock (lock);
-
-      enum nss_status status = _nss_create_tablename (errnop);
-
-      __libc_lock_unlock (lock);
-
+      enum nss_status status = get_tablename (herrnop);
       if (status != NSS_STATUS_SUCCESS)
-       {
-         *herrnop = NETDB_INTERNAL;
-         return NSS_STATUS_UNAVAIL;
-       }
+       return status;
     }
 
   if (name == NULL)
@@ -374,39 +398,36 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
       return NSS_STATUS_NOTFOUND;
     }
 
-  nis_result *result;
   char buf[strlen (name) + 10 + tablename_len];
   int olderr = errno;
 
   /* Search at first in the alias list, and use the correct name
      for the next search.  */
   snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
-  result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
+  nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
 
   if (result != NULL)
     {
-      char *bufptr = buf;
-
       /* If we did not find it, try it as original name. But if the
         database is correct, we should find it in the first case, too.  */
-      if ((result->status != NIS_SUCCESS
-          && result->status != NIS_S_SUCCESS)
-         || __type_of (result->objects.objects_val) != NIS_ENTRY_OBJ
-         || strcmp (result->objects.objects_val->EN_data.en_type,
-                    "hosts_tbl") != 0
-         || result->objects.objects_val->EN_data.en_cols.en_cols_len < 3)
-       snprintf (buf, sizeof (buf), "[cname=%s],%s", name, tablename_val);
-      else
+      char *bufptr = buf;
+      size_t buflen = sizeof (buf);
+
+      if ((result->status == NIS_SUCCESS || result->status == NIS_S_SUCCESS)
+         && __type_of (result->objects.objects_val) == NIS_ENTRY_OBJ
+         && strcmp (result->objects.objects_val->EN_data.en_type,
+                    "hosts_tbl") == 0
+         && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3)
        {
          /* We need to allocate a new buffer since there is no
-            guarantee the returned name has a length limit.  */
-         const char *entryval = NISENTRYVAL(0, 0, result);
-         size_t buflen = strlen (entryval) + 10 + tablename_len;
+            guarantee the returned alias name has a length limit.  */
+         name = NISENTRYVAL(0, 0, result);
+         size_t buflen = strlen (name) + 10 + tablename_len;
          bufptr = alloca (buflen);
-         snprintf (bufptr, buflen, "[cname=%s],%s",
-                   entryval, tablename_val);
        }
 
+      snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
+
       nis_freeresult (result);
       result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
     }
@@ -417,7 +438,7 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
       return NSS_STATUS_TRYAGAIN;
     }
 
-  retval = niserr2nss (result->status);
+  int retval = niserr2nss (result->status);
   if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
     {
       if (retval == NSS_STATUS_TRYAGAIN)
@@ -431,8 +452,8 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
       return retval;
     }
 
-  parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
-                                         buflen, errnop, flags);
+  int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
+                                             buflen, errnop, flags);
 
   nis_freeresult (result);
 
@@ -450,16 +471,24 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
   return NSS_STATUS_NOTFOUND;
 }
 
+
 enum nss_status
 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
                               char *buffer, size_t buflen, int *errnop,
                               int *herrnop)
 {
+  if (af != AF_INET && af != AF_INET6)
+    {
+      *herrnop = HOST_NOT_FOUND;
+      return NSS_STATUS_NOTFOUND;
+    }
+
   return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
                                    herrnop,
                         ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
 }
 
+
 enum nss_status
 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
                              char *buffer, size_t buflen, int *errnop,
@@ -480,6 +509,7 @@ _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
                                   buflen, errnop, h_errnop, 0);
 }
 
+
 enum nss_status
 _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
                              struct hostent *host, char *buffer,
@@ -487,12 +517,7 @@ _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
 {
   if (tablename_val == NULL)
     {
-      __libc_lock_lock (lock);
-
-      enum nss_status status = _nss_create_tablename (errnop);
-
-      __libc_lock_unlock (lock);
-
+      enum nss_status status = get_tablename (herrnop);
       if (status != NSS_STATUS_SUCCESS)
        return status;
     }
@@ -547,3 +572,44 @@ _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
   __set_errno (olderr);
   return NSS_STATUS_NOTFOUND;
 }
+
+
+enum nss_status
+_nss_nisplus_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+                              char *buffer, size_t buflen, int *errnop,
+                              int *herrnop, int32_t *ttlp)
+{
+  struct hostent host;
+
+  enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
+                                                     buffer, buflen,
+                                                     errnop, herrnop, 0);
+  if (__builtin_expect (status == NSS_STATUS_SUCCESS, 1))
+    {
+      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))
+           {
+             free (result);
+             *errnop = ERANGE;
+             *herrnop = NETDB_INTERNAL;
+             return NSS_STATUS_TRYAGAIN;
+           }
+       }
+
+      (*pat)->next = NULL;
+      (*pat)->name = host.h_name;
+      (*pat)->family = host.h_addrtype;
+
+      memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
+      (*pat)->scopeid = 0;
+      assert (host.h_addr_list[1] == NULL);
+    }
+
+  return status;
+}
index 2518f80..4db3e65 100644 (file)
@@ -21,6 +21,7 @@
 #include <errno.h>
 #include <libintl.h>
 #include <netdb.h>
+#include <nss.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 #endif
 
 
+typedef enum nss_status (*nss_gethostbyname4_r)
+  (const char *name, struct gaih_addrtuple **pat,
+   char *buffer, size_t buflen, int *errnop,
+   int *h_errnop, int32_t *ttlp);
 typedef enum nss_status (*nss_gethostbyname3_r)
   (const char *name, int af, struct hostent *host,
    char *buffer, size_t buflen, int *errnop,
@@ -117,16 +122,104 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
 
   while (!no_more)
     {
+      void *cp;
       int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
+      int naddrs = 0;
+      size_t addrslen = 0;
+      size_t canonlen;
+
+      nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
+                                                        "gethostbyname4_r");
+      if (fct4 != NULL)
+       {
+         struct gaih_addrtuple *at = NULL;
+         while (1)
+           {
+             rc6 = 0;
+             status[0] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len,
+                                             &rc6, &herrno, &ttl));
+             if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
+               break;
+             tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
+           }
+
+         if (rc6 != 0 && herrno == NETDB_INTERNAL)
+           goto out;
 
-      /* Prefer the function which also returns the TTL and canonical name.  */
-      nss_gethostbyname3_r fct = __nss_lookup_function (nip,
-                                                       "gethostbyname3_r");
-      if (fct == NULL)
-       fct = __nss_lookup_function (nip, "gethostbyname2_r");
+         if (status[0] != NSS_STATUS_SUCCESS)
+           goto next_nip;
+
+         /* We found the data.  Count the addresses and the size.  */
+         for (struct gaih_addrtuple *at2 = at; at2 != NULL; at2 = at2->next)
+           {
+             ++naddrs;
+             /* We handle unknown types here the best we can: assume
+                the maximum size for the address.  */
+             if (at2->family == AF_INET)
+               addrslen += INADDRSZ;
+             else if (at2->family == AF_INET6
+                      && IN6ADDRSZ != sizeof (at2->addr))
+               addrslen += IN6ADDRSZ;
+             else
+               addrslen += sizeof (at2->addr);
+           }
+         canon = at->name;
+         canonlen = strlen (canon) + 1;
+
+         total = sizeof (*dataset) + naddrs + addrslen + canonlen;
+
+         /* Now we can allocate the data structure.  If the TTL of the
+            entry is reported as zero do not cache the entry at all.  */
+         if (ttl != 0 && he == NULL)
+           {
+             dataset = (struct dataset *) mempool_alloc (db, total
+                                                         + req->key_len,
+                                                         IDX_result_data);
+             if (dataset == NULL)
+               ++db->head->addfailed;
+           }
+
+         if (dataset == NULL)
+           {
+             /* We cannot permanently add the result in the moment.  But
+                we can provide the result as is.  Store the data in some
+                temporary memory.  */
+             dataset = (struct dataset *) alloca (total + req->key_len);
+
+             /* We cannot add this record to the permanent database.  */
+             alloca_used = true;
+           }
 
-      if (fct != NULL)
+         /* Fill in the address and address families.  */
+         char *addrs = (char *) (&dataset->resp + 1);
+         uint8_t *family = (uint8_t *) (addrs + addrslen);
+
+         for (struct gaih_addrtuple *at2 = at; at2 != NULL; at2 = at2->next)
+           {
+             *family++ = at2->family;
+             if (at2->family == AF_INET)
+               addrs = mempcpy (addrs, at2->addr, INADDRSZ);
+             else if (at2->family == AF_INET6
+                      && IN6ADDRSZ != sizeof (at2->addr))
+               addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
+             else
+               addrs = mempcpy (addrs, at2->addr, sizeof (at2->addr));
+           }
+
+         cp = family;
+       }
+      else
        {
+         /* Prefer the function which also returns the TTL and
+            canonical name.  */
+         nss_gethostbyname3_r fct = __nss_lookup_function (nip,
+                                                           "gethostbyname3_r");
+         if (fct == NULL)
+           fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+         if (fct == NULL)
+           goto next_nip;
+
          struct hostent th[2];
 
          /* Collect IPv6 information first.  */
@@ -134,8 +227,8 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
            {
              rc6 = 0;
              status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6,
-                                            tmpbuf6len, &rc6, &herrno,
-                                            &ttl, &canon));
+                                            tmpbuf6len, &rc6, &herrno, &ttl,
+                                            &canon));
              if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
                break;
              tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
@@ -173,231 +266,226 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
          if (rc4 != 0 && herrno == NETDB_INTERNAL)
            goto out;
 
-         if (status[0] == NSS_STATUS_SUCCESS
-             || status[1] == NSS_STATUS_SUCCESS)
+         if (status[0] != NSS_STATUS_SUCCESS
+             && status[1] != NSS_STATUS_SUCCESS)
+           goto next_nip;
+
+         /* We found the data.  Count the addresses and the size.  */
+         for (int j = 0; j < 2; ++j)
+           if (status[j] == NSS_STATUS_SUCCESS)
+             for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
+               {
+                 ++naddrs;
+                 addrslen += th[j].h_length;
+               }
+
+         if (canon == NULL)
            {
-             /* We found the data.  Count the addresses and the size.  */
-             int naddrs = 0;
-             size_t addrslen = 0;
-             for (int j = 0; j < 2; ++j)
-               if (status[j] == NSS_STATUS_SUCCESS)
-                 for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
+             /* Determine the canonical name.  */
+             nss_getcanonname_r cfct;
+             cfct = __nss_lookup_function (nip, "getcanonname_r");
+             if (cfct != NULL)
+               {
+                 const size_t max_fqdn_len = 256;
+                 char *buf = alloca (max_fqdn_len);
+                 char *s;
+                 int rc;
+
+                 if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s,
+                                         &rc, &herrno))
+                     == NSS_STATUS_SUCCESS)
+                   canon = s;
+                 else
+                   /* Set to name now to avoid using gethostbyaddr.  */
+                   canon = key;
+               }
+             else
+               {
+                 struct hostent *he = NULL;
+                 int herrno;
+                 struct hostent he_mem;
+                 void *addr;
+                 size_t addrlen;
+                 int addrfamily;
+
+                 if (status[1] == NSS_STATUS_SUCCESS)
+                   {
+                     addr = th[1].h_addr_list[0];
+                     addrlen = sizeof (struct in_addr);
+                     addrfamily = AF_INET;
+                   }
+                 else
                    {
-                     ++naddrs;
-                     addrslen += th[j].h_length;
+                     addr = th[0].h_addr_list[0];
+                     addrlen = sizeof (struct in6_addr);
+                     addrfamily = AF_INET6;
                    }
 
-             if (canon == NULL)
-               {
-                 /* Determine the canonical name.  */
-                 nss_getcanonname_r cfct;
-                 cfct = __nss_lookup_function (nip, "getcanonname_r");
-                 if (cfct != NULL)
+                 size_t tmpbuflen = 512;
+                 char *tmpbuf = alloca (tmpbuflen);
+                 int rc;
+                 while (1)
                    {
-                     const size_t max_fqdn_len = 256;
-                     char *buf = alloca (max_fqdn_len);
-                     char *s;
-                     int rc;
-
-                     if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s, &rc,
-                                             &herrno)) == NSS_STATUS_SUCCESS)
-                       canon = s;
-                     else
-                       /* Set to name now to avoid using gethostbyaddr.  */
-                       canon = key;
+                     rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
+                                              &he_mem, tmpbuf, tmpbuflen,
+                                              &he, &herrno, NULL);
+                     if (rc != ERANGE || herrno != NETDB_INTERNAL)
+                       break;
+                     tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
+                                             tmpbuflen * 2);
                    }
-                 else
+
+                 if (rc == 0)
                    {
-                     struct hostent *he = NULL;
-                     int herrno;
-                     struct hostent he_mem;
-                     void *addr;
-                     size_t addrlen;
-                     int addrfamily;
-
-                     if (status[1] == NSS_STATUS_SUCCESS)
-                       {
-                         addr = th[1].h_addr_list[0];
-                         addrlen = sizeof (struct in_addr);
-                         addrfamily = AF_INET;
-                       }
+                     if (he != NULL)
+                       canon = he->h_name;
                      else
-                       {
-                         addr = th[0].h_addr_list[0];
-                         addrlen = sizeof (struct in6_addr);
-                         addrfamily = AF_INET6;
-                       }
-
-                     size_t tmpbuflen = 512;
-                     char *tmpbuf = alloca (tmpbuflen);
-                     int rc;
-                     while (1)
-                       {
-                         rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
-                                                  &he_mem, tmpbuf, tmpbuflen,
-                                                  &he, &herrno, NULL);
-                         if (rc != ERANGE || herrno != NETDB_INTERNAL)
-                           break;
-                         tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
-                                                 tmpbuflen * 2);
-                       }
-
-                     if (rc == 0)
-                       {
-                         if (he != NULL)
-                           canon = he->h_name;
-                         else
-                           canon = key;
-                       }
+                       canon = key;
                    }
                }
-             size_t canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
+           }
 
-             total = sizeof (*dataset) + naddrs + addrslen + canonlen;
+         canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
+
+         total = sizeof (*dataset) + naddrs + addrslen + canonlen;
 
-             /* Now we can allocate the data structure.  If the TTL
-                of the entry is reported as zero do not cache the
-                entry at all.  */
-             if (ttl != 0 && he == NULL)
-               {
-                 dataset = (struct dataset *) mempool_alloc (db,
-                                                             total
-                                                             + req->key_len,
-                                                             IDX_result_data);
-                 if (dataset == NULL)
-                   ++db->head->addfailed;
-               }
 
+         /* Now we can allocate the data structure.  If the TTL of the
+            entry is reported as zero do not cache the entry at all.  */
+         if (ttl != 0 && he == NULL)
+           {
+             dataset = (struct dataset *) mempool_alloc (db, total
+                                                         + req->key_len,
+                                                         IDX_result_data);
              if (dataset == NULL)
-               {
-                 /* We cannot permanently add the result in the moment.  But
-                    we can provide the result as is.  Store the data in some
-                    temporary memory.  */
-                 dataset = (struct dataset *) alloca (total + req->key_len);
+               ++db->head->addfailed;
+           }
+
+         if (dataset == NULL)
+           {
+             /* We cannot permanently add the result in the moment.  But
+                we can provide the result as is.  Store the data in some
+                temporary memory.  */
+             dataset = (struct dataset *) alloca (total + req->key_len);
+
+             /* We cannot add this record to the permanent database.  */
+             alloca_used = true;
+           }
 
-                 /* We cannot add this record to the permanent database.  */
-                 alloca_used = true;
+         /* Fill in the address and address families.  */
+         char *addrs = (char *) (&dataset->resp + 1);
+         uint8_t *family = (uint8_t *) (addrs + addrslen);
+
+         for (int j = 0; j < 2; ++j)
+           if (status[j] == NSS_STATUS_SUCCESS)
+             for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
+               {
+                 addrs = mempcpy (addrs, th[j].h_addr_list[i],
+                                  th[j].h_length);
+                 *family++ = th[j].h_addrtype;
                }
 
-             dataset->head.allocsize = total + req->key_len;
-             dataset->head.recsize = total - offsetof (struct dataset, resp);
-             dataset->head.notfound = false;
-             dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
-             dataset->head.usable = true;
-
-             /* Compute the timeout time.  */
-             dataset->head.timeout = time (NULL) + (ttl == INT32_MAX
-                                                    ? db->postimeout : ttl);
-
-             dataset->resp.version = NSCD_VERSION;
-             dataset->resp.found = 1;
-             dataset->resp.naddrs = naddrs;
-             dataset->resp.addrslen = addrslen;
-             dataset->resp.canonlen = canonlen;
-             dataset->resp.error = NETDB_SUCCESS;
-
-             char *addrs = (char *) (&dataset->resp + 1);
-             uint8_t *family = (uint8_t *) (addrs + addrslen);
-
-             for (int j = 0; j < 2; ++j)
-               if (status[j] == NSS_STATUS_SUCCESS)
-                 for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
-                   {
-                     addrs = mempcpy (addrs, th[j].h_addr_list[i],
-                                      th[j].h_length);
-                     *family++ = th[j].h_addrtype;
-                   }
+         cp = family;
+       }
 
-             void *cp = family;
-             if (canon != NULL)
-               cp = mempcpy (cp, canon, canonlen);
+      /* Fill in the rest of the dataset.  */
+      dataset->head.allocsize = total + req->key_len;
+      dataset->head.recsize = total - offsetof (struct dataset, resp);
+      dataset->head.notfound = false;
+      dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
+      dataset->head.usable = true;
 
-             key_copy = memcpy (cp, key, req->key_len);
+      /* Compute the timeout time.  */
+      dataset->head.timeout = time (NULL) + (ttl == INT32_MAX
+                                            ? db->postimeout : ttl);
 
-             /* Now we can determine whether on refill we have to
-                create a new record or not.  */
-             if (he != NULL)
-               {
-                 assert (fd == -1);
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.naddrs = naddrs;
+      dataset->resp.addrslen = addrslen;
+      dataset->resp.canonlen = canonlen;
+      dataset->resp.error = NETDB_SUCCESS;
 
-                 if (total + req->key_len == dh->allocsize
-                     && total - offsetof (struct dataset, resp) == dh->recsize
-                     && memcmp (&dataset->resp, dh->data,
-                                dh->allocsize
-                                - offsetof (struct dataset, resp)) == 0)
-                   {
-                     /* The data has not changed.  We will just bump the
-                        timeout value.  Note that the new record has been
-                        allocated on the stack and need not be freed.  */
-                     dh->timeout = dataset->head.timeout;
-                     ++dh->nreloads;
-                   }
-                 else
-                   {
-                     /* We have to create a new record.  Just allocate
-                        appropriate memory and copy it.  */
-                     struct dataset *newp
-                       = (struct dataset *) mempool_alloc (db,
-                                                           total
-                                                           + req->key_len,
-                                                           IDX_result_data);
-                     if (__builtin_expect (newp != NULL, 1))
-                       {
-                         /* Adjust pointer into the memory block.  */
-                         key_copy = (char *) newp + (key_copy
-                                                     - (char *) dataset);
-
-                         dataset = memcpy (newp, dataset,
-                                           total + req->key_len);
-                         alloca_used = false;
-                       }
-                     else
-                       ++db->head->addfailed;
+      if (canon != NULL)
+       cp = mempcpy (cp, canon, canonlen);
 
-                     /* Mark the old record as obsolete.  */
-                     dh->usable = false;
-                   }
+      key_copy = memcpy (cp, key, req->key_len);
+
+      /* Now we can determine whether on refill we have to create a
+        new record or not.  */
+      if (he != NULL)
+       {
+         assert (fd == -1);
+
+         if (total + req->key_len == dh->allocsize
+             && total - offsetof (struct dataset, resp) == dh->recsize
+             && memcmp (&dataset->resp, dh->data,
+                        dh->allocsize - offsetof (struct dataset,
+                                                  resp)) == 0)
+           {
+             /* The data has not changed.  We will just bump the
+                timeout value.  Note that the new record has been
+                allocated on the stack and need not be freed.  */
+             dh->timeout = dataset->head.timeout;
+             ++dh->nreloads;
+           }
+         else
+           {
+             /* We have to create a new record.  Just allocate
+                appropriate memory and copy it.  */
+             struct dataset *newp
+               = (struct dataset *) mempool_alloc (db, total + req->key_len,
+                                                   IDX_result_data);
+             if (__builtin_expect (newp != NULL, 1))
+               {
+                 /* Adjust pointer into the memory block.  */
+                 key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+                 dataset = memcpy (newp, dataset, total + req->key_len);
+                 alloca_used = false;
                }
              else
-               {
-                 /* We write the dataset before inserting it to the
-                    database since while inserting this thread might
-                    block and so would unnecessarily let the receiver
-                    wait.  */
-                 assert (fd != -1);
+               ++db->head->addfailed;
+
+             /* Mark the old record as obsolete.  */
+             dh->usable = false;
+           }
+       }
+      else
+       {
+         /* We write the dataset before inserting it to the database
+            since while inserting this thread might block and so
+            would unnecessarily let the receiver wait.  */
+         assert (fd != -1);
 
 #ifdef HAVE_SENDFILE
-                 if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
-                   {
-                     assert (db->wr_fd != -1);
-                     assert ((char *) &dataset->resp > (char *) db->data);
-                     assert ((char *) &dataset->resp - (char *) db->head
-                             + total
-                             <= (sizeof (struct database_pers_head)
-                                 + db->head->module * sizeof (ref_t)
-                                 + db->head->data_size));
-                     ssize_t written;
-                     written = sendfileall (fd, db->wr_fd,
-                                            (char *) &dataset->resp
-                                            - (char *) db->head, total);
+         if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+           {
+             assert (db->wr_fd != -1);
+             assert ((char *) &dataset->resp > (char *) db->data);
+             assert ((char *) &dataset->resp - (char *) db->head + total
+                     <= (sizeof (struct database_pers_head)
+                         + db->head->module * sizeof (ref_t)
+                         + db->head->data_size));
+             ssize_t written;
+             written = sendfileall (fd, db->wr_fd, (char *) &dataset->resp
+                                    - (char *) db->head, total);
 # ifndef __ASSUME_SENDFILE
-                     if (written == -1 && errno == ENOSYS)
-                       goto use_write;
+             if (written == -1 && errno == ENOSYS)
+               goto use_write;
 # endif
-                   }
-                 else
+           }
+         else
 # ifndef __ASSUME_SENDFILE
-                 use_write:
+         use_write:
 # endif
 #endif
-                   writeall (fd, &dataset->resp, total);
-               }
-
-             goto out;
-           }
-
+           writeall (fd, &dataset->resp, total);
        }
 
+      goto out;
+
+next_nip:
       if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
        break;
 
index 8f2f0fb..f7f0e56 100644 (file)
@@ -38,6 +38,7 @@ libnss_files {
     _nss_files_endhostent;
     _nss_files_gethostbyaddr_r;
     _nss_files_gethostbyname2_r;
+    _nss_files_gethostbyname4_r;
     _nss_files_gethostbyname_r;
     _nss_files_gethostent_r;
     _nss_files_gethostton_r;
index f5c12af..611b258 100644 (file)
--- a/nss/nss.h
+++ b/nss/nss.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1999, 2008 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
@@ -23,6 +23,7 @@
 #define _NSS_H 1
 
 #include <features.h>
+#include <stdint.h>
 
 
 __BEGIN_DECLS
@@ -38,6 +39,17 @@ enum nss_status
 };
 
 
+/* Data structure used for the 'gethostbyname4_r' function.  */
+struct gaih_addrtuple
+  {
+    struct gaih_addrtuple *next;
+    char *name;
+    int family;
+    uint32_t addr[4];
+    uint32_t scopeid;
+  };
+
+
 /* Overwrite service selection for database DBNAME using specification
    in STRING.
    This function should only be used by system programs which have to
index b064672..7b69d47 100644 (file)
@@ -1,6 +1,5 @@
 /* Hosts file parser in nss_files module.
-   Copyright (C) 1996-2001, 2003, 2004, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1996-2001, 2003-2007, 2008 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
@@ -56,7 +55,10 @@ LINE_PARSER
    STRING_FIELD (addr, isspace, 1);
 
    /* Parse address.  */
-   if (inet_pton (af, addr, entdata->host_addr) <= 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)
@@ -76,6 +78,9 @@ LINE_PARSER
             /* 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;
@@ -101,8 +106,6 @@ _nss_files_get##name##_r (proto,                                          \
                          struct STRUCTURE *result, char *buffer,             \
                          size_t buflen, int *errnop H_ERRNO_PROTO)           \
 {                                                                            \
-  enum nss_status status;                                                    \
-                                                                             \
   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);    \
   buffer += pad;                                                             \
   buflen = buflen > pad ? buflen - pad : 0;                                  \
@@ -110,7 +113,7 @@ _nss_files_get##name##_r (proto,                                          \
   __libc_lock_lock (lock);                                                   \
                                                                              \
   /* Reset file pointer to beginning or open file.  */                       \
-  status = internal_setent (keep_stream);                                    \
+  enum nss_status status = internal_setent (keep_stream);                    \
                                                                              \
   if (status == NSS_STATUS_SUCCESS)                                          \
     {                                                                        \
@@ -288,9 +291,9 @@ HOST_DB_LOOKUP (hostbyname, ,,
                {
                  LOOKUP_NAME_CASE (h_name, h_aliases)
                }, const char *name)
+#undef EXTRA_ARGS_VALUE
 
 
-#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 \
@@ -299,8 +302,9 @@ HOST_DB_LOOKUP (hostbyname2, ,,
                {
                  LOOKUP_NAME_CASE (h_name, h_aliases)
                }, const char *name, int af)
-
 #undef EXTRA_ARGS_VALUE
+
+
 /* We only need to consider IPv4 mapped addresses if the input to the
    gethostbyaddr() function is an IPv6 address.  */
 #define EXTRA_ARGS_VALUE \
@@ -311,3 +315,116 @@ DB_LOOKUP (hostbyaddr, ,,
                 && ! memcmp (addr, result->h_addr_list[0], len))
               break;
           }, const void *addr, socklen_t len, int af)
+#undef EXTRA_ARGS_VALUE
+
+
+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
+                                   H_ERRNO_ARG, 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 ();
+    }
+
+  __libc_lock_unlock (lock);
+
+  return status;
+}
index fc2111a..355d95d 100644 (file)
@@ -89,6 +89,7 @@ libnss_dns {
     _nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r;
     _nss_dns_getnetbyname_r; _nss_dns_getcanonname_r;
     _nss_dns_gethostbyaddr2_r;
+    _nss_dns_gethostbyname4_r;
   }
 }
 
index 7be2315..2a9bd0b 100644 (file)
@@ -621,7 +621,7 @@ gethostbyname2(name, af)
        buf.buf = origbuf = (querybuf *) alloca (1024);
 
        if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024,
-                                   &buf.ptr)) < 0) {
+                                   &buf.ptr, NULL, NULL)) < 0) {
                if (buf.buf != origbuf)
                        free (buf.buf);
                Dprintf("res_nsearch failed (%d)\n", n);
@@ -716,12 +716,12 @@ gethostbyaddr(addr, len, af)
        buf.buf = orig_buf = (querybuf *) alloca (1024);
 
        n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024,
-                             &buf.ptr);
+                             &buf.ptr, NULL, NULL);
        if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) {
                strcpy(qp, "ip6.int");
                n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf,
                                      buf.buf != orig_buf ? MAXPACKET : 1024,
-                                     &buf.ptr);
+                                     &buf.ptr, NULL, NULL);
        }
        if (n < 0) {
                if (buf.buf != orig_buf)
index fca6cd8..47949b8 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+/* Copyright (C) 2004, 2006, 2008 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -61,7 +61,7 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
   for (int i = 0; i < nqtypes; ++i)
     {
       int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
-                                buf, sizeof (buf), &ansp.ptr);
+                                buf, sizeof (buf), &ansp.ptr, NULL, NULL);
       if (r > 0)
        {
          /* We need to decode the response.  Just one question record.
index dfdf9c5..c52f9f7 100644 (file)
@@ -71,6 +71,7 @@
  * --Copyright--
  */
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <netdb.h>
@@ -127,6 +128,14 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen,
                                    size_t buflen, int *errnop, int *h_errnop,
                                    int map, int32_t *ttlp, char **canonp);
 
+static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
+                                      const querybuf *answer2, int anslen2,
+                                      const char *qname,
+                                      struct gaih_addrtuple **pat,
+                                      char *buffer, size_t buflen,
+                                      int *errnop, int *h_errnop,
+                                      int32_t *ttlp);
+
 extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af,
                                                  struct hostent *result,
                                                  char *buffer, size_t buflen,
@@ -186,11 +195,11 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
   host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
 
   n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf,
-                         1024, &host_buffer.ptr);
+                         1024, &host_buffer.ptr, NULL, NULL);
   if (n < 0)
     {
-      enum nss_status status = (errno == ECONNREFUSED
-                               ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
+      status = (errno == ECONNREFUSED
+               ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
       *h_errnop = h_errno;
       if (h_errno == TRY_AGAIN)
        *errnop = EAGAIN;
@@ -203,7 +212,8 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
       if (af == AF_INET6 && (_res.options & RES_USE_INET6))
        n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf,
                                host_buffer.buf != orig_host_buffer
-                               ? MAXPACKET : 1024, &host_buffer.ptr);
+                               ? MAXPACKET : 1024, &host_buffer.ptr,
+                               NULL, NULL);
 
       if (n < 0)
        {
@@ -255,6 +265,70 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
 }
 
 
+enum nss_status
+_nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+                          char *buffer, size_t buflen, int *errnop,
+                          int *herrnop, int32_t *ttlp)
+{
+  if (__res_maybe_init (&_res, 0) == -1)
+    return NSS_STATUS_UNAVAIL;
+
+  char tmp[NS_MAXDNAME];
+
+  /*
+   * if there aren't any dots, it could be a user-level alias.
+   * this is also done in res_query() since we are not the only
+   * function that looks up host names.
+   */
+  if (strchr (name, '.') == NULL)
+    {
+      const char *cp = res_hostalias (&_res, name, tmp, sizeof (tmp));
+      if (cp != NULL)
+       name = cp;
+    }
+
+  union
+  {
+    querybuf *buf;
+    u_char *ptr;
+  } host_buffer;
+  querybuf *orig_host_buffer;
+  host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048);
+  u_char *ans2p = NULL;
+  int nans2p = 0;
+
+  int olderr = errno;
+  enum nss_status status;
+  int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC,
+                             host_buffer.buf->buf, 2048, &host_buffer.ptr,
+                             &ans2p, &nans2p);
+  if (n < 0)
+    {
+      status = (errno == ECONNREFUSED
+               ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
+      *herrnop = h_errno;
+      if (h_errno == TRY_AGAIN)
+       *errnop = EAGAIN;
+      else
+       __set_errno (olderr);
+
+      if (host_buffer.buf != orig_host_buffer)
+       free (host_buffer.buf);
+
+      return status;
+    }
+
+  status = gaih_getanswer(host_buffer.buf, n, (const querybuf *) ans2p,
+                         nans2p, name, pat, buffer, buflen,
+                         errnop, herrnop, ttlp);
+
+  if (host_buffer.buf != orig_host_buffer)
+    free (host_buffer.buf);
+
+  return status;
+}
+
+
 extern enum nss_status _nss_dns_gethostbyaddr2_r (const void *addr,
                                                  socklen_t len, int af,
                                                  struct hostent *result,
@@ -342,7 +416,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
            qp += sprintf (qp, "%02hhx", uaddr[n]);
          strcpy (qp, "].ip6.arpa");
          n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR,
-                                host_buffer.buf->buf, 1024, &host_buffer.ptr);
+                                host_buffer.buf->buf, 1024, &host_buffer.ptr,
+                                NULL, NULL);
          if (n >= 0)
            goto got_it_already;
        }
@@ -363,13 +438,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
     }
 
   n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
-                        1024, &host_buffer.ptr);
+                        1024, &host_buffer.ptr, NULL, NULL);
   if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0)
     {
       strcpy (qp, "ip6.int");
       n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
                             host_buffer.buf != orig_host_buffer
-                            ? MAXPACKET : 1024, &host_buffer.ptr);
+                            ? MAXPACKET : 1024, &host_buffer.ptr,
+                            NULL, NULL);
     }
   if (n < 0)
     {
@@ -555,7 +631,8 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
   if (n > 0 && bp[0] == '.')
     bp[0] = '\0';
 
-  if (n < 0 || (*name_ok) (bp) == 0)
+  if (__builtin_expect (n < 0 || ((*name_ok) (bp) == 0 && (errno = EBADMSG)),
+                       0))
     {
       *errnop = errno;
       *h_errnop = NO_RECOVERY;
@@ -629,7 +706,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
          continue;                     /* XXX - had_error++ ? */
        }
 
-      if ((qtype ==T_A || qtype == T_AAAA) && type == T_CNAME)
+      if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME)
        {
          if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1])
            continue;
@@ -857,3 +934,245 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
   return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
           ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
 }
+
+
+static enum nss_status
+gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
+                     struct gaih_addrtuple ***patp,
+                     char **bufferp, size_t *buflenp,
+                     int *errnop, int *h_errnop, int32_t *ttlp, int *firstp)
+{
+  char *buffer = *bufferp;
+  size_t buflen = *buflenp;
+
+  struct gaih_addrtuple **pat = *patp;
+  const HEADER *hp = &answer->hdr;
+  int ancount = ntohs (hp->ancount);
+  int qdcount = ntohs (hp->qdcount);
+  const u_char *cp = answer->buf + HFIXEDSZ;
+  const u_char *end_of_message = answer->buf + anslen;
+  if (__builtin_expect (qdcount != 1, 0))
+    {
+      *h_errnop = NO_RECOVERY;
+      return NSS_STATUS_UNAVAIL;
+    }
+
+   u_char packtmp[NS_MAXCDNAME];
+  int n = __ns_name_unpack (answer->buf, end_of_message, cp,
+                           packtmp, sizeof packtmp);
+  /* We unpack the name to check it for validity.  But we do not need
+     it later.  */
+  if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1)
+    {
+      if (__builtin_expect (errno, 0) == EMSGSIZE)
+       {
+       too_small:
+         *errnop = ERANGE;
+         *h_errnop = NETDB_INTERNAL;
+         return NSS_STATUS_TRYAGAIN;
+       }
+
+      n = -1;
+    }
+
+  if (__builtin_expect (n < 0 || (res_hnok (buffer) == 0
+                                 && (errno = EBADMSG)), 0))
+    {
+      *errnop = errno;
+      *h_errnop = NO_RECOVERY;
+      return NSS_STATUS_UNAVAIL;
+    }
+  cp += n + QFIXEDSZ;
+
+  int haveanswer = 0;
+  int had_error = 0;
+  char *canon = NULL;
+  char *h_name = NULL;
+  int h_namelen = 0;
+
+  while (ancount-- > 0 && cp < end_of_message && had_error == 0)
+    {
+      n = __ns_name_unpack (answer->buf, end_of_message, cp,
+                           packtmp, sizeof packtmp);
+      if (n != -1 &&
+         (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1)
+       {
+         if (__builtin_expect (errno, 0) == EMSGSIZE)
+           goto too_small;
+
+         n = -1;
+       }
+      if (n < 0 || res_hnok (buffer) == 0)
+       {
+         ++had_error;
+         continue;
+       }
+      if (*firstp)
+       {
+         h_name = buffer;
+         buffer += h_namelen;
+         buflen -= h_namelen;
+       }
+
+      cp += n;                         /* name */
+      int type = ns_get16 (cp);
+      cp += INT16SZ;                   /* type */
+      int class = ns_get16 (cp);
+      cp += INT16SZ;                   /* class */
+      int32_t ttl = ns_get32 (cp);
+      cp += INT32SZ;                   /* TTL */
+      n = ns_get16 (cp);
+      cp += INT16SZ;                   /* len */
+
+      if (class != C_IN)
+       {
+         cp += n;
+         continue;
+       }
+
+      if (type == T_CNAME)
+       {
+         char tbuf[MAXDNAME];
+         n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf);
+         if (n < 0 || res_hnok (tbuf) == 0)
+           {
+             ++had_error;
+             continue;
+           }
+         cp += n;
+
+         if (*firstp)
+           {
+             /* Reclaim buffer space.  */
+             if (h_name + h_namelen == buffer)
+               {
+                 buffer = h_name;
+                 buflen += h_namelen;
+               }
+
+             n = strlen (tbuf) + 1;
+             if (__builtin_expect (n > buflen, 0))
+               goto too_small;
+             if (__builtin_expect (n >= MAXHOSTNAMELEN, 0))
+               {
+                 ++had_error;
+                 continue;
+               }
+
+             canon = buffer;
+             buffer = __mempcpy (buffer, tbuf, n);
+             buflen -= n;
+             h_namelen = 0;
+           }
+         continue;
+       }
+      if (__builtin_expect (type == T_SIG, 0)
+         || __builtin_expect (type == T_KEY, 0)
+         || __builtin_expect (type == T_NXT, 0)
+         || __builtin_expect (type == T_PTR, 0))
+       {
+         /* We don't support DNSSEC yet.  For now, ignore the record
+            and send a low priority message to syslog.
+
+            We also don't expect T_PTR messages.  */
+         syslog (LOG_DEBUG | LOG_AUTH,
+                 "getaddrinfo*.gaih_getanswer: got type \"%s\"",
+                 p_type (type));
+         cp += n;
+         continue;
+       }
+      if (type != T_A && type != T_AAAA)
+       abort ();
+
+      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;
+             *h_errnop = NETDB_INTERNAL;
+             return NSS_STATUS_TRYAGAIN;
+           }
+
+         *pat = (struct gaih_addrtuple *) buffer;
+         buffer += sizeof (struct gaih_addrtuple);
+         buflen -= sizeof (struct gaih_addrtuple);
+       }
+
+      (*pat)->name = NULL;
+      (*pat)->next = NULL;
+
+      if (*firstp)
+       {
+         if (ttl != 0 && ttlp != NULL)
+           *ttlp = ttl;
+
+         if (canon != NULL)
+           {
+             (*pat)->name = canon;
+
+             /* Reclaim buffer space.  */
+             if (h_name + h_namelen == buffer)
+               {
+                 buffer = h_name;
+                 buflen += h_namelen;
+               }
+           }
+         else
+           (*pat)->name = h_name;
+
+         *firstp = 0;
+       }
+
+      (*pat)->family = type == T_A ? AF_INET : AF_INET6;
+      memcpy ((*pat)->addr, cp, n);
+      cp += n;
+      (*pat)->scopeid = 0;
+
+      pat = &((*pat)->next);
+
+      haveanswer = 1;
+    }
+
+  if (haveanswer)
+    {
+      *patp = pat;
+      *bufferp = buffer;
+      *buflenp = buflen;
+
+      *h_errnop = NETDB_SUCCESS;
+      return NSS_STATUS_SUCCESS;
+    }
+
+  /* Special case here: if the resolver sent a result but it only
+     contains a CNAME while we are looking for a T_A or T_AAAA record,
+     we fail with NOTFOUND instead of TRYAGAIN.  */
+  return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
+}
+
+
+static enum nss_status
+gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
+               int anslen2, const char *qname,
+               struct gaih_addrtuple **pat, char *buffer, size_t buflen,
+               int *errnop, int *h_errnop, int32_t *ttlp)
+{
+  int first = 1;
+
+  enum nss_status status = gaih_getanswer_slice(answer1, anslen1, qname,
+                                               &pat, &buffer, &buflen,
+                                               errnop, h_errnop, ttlp,
+                                               &first);
+  if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
+      && answer2 != NULL)
+    status = gaih_getanswer_slice(answer2, anslen2, qname,
+                                 &pat, &buffer, &buflen,
+                                 errnop, h_errnop, ttlp, &first);
+
+  return status;
+}
index 4552b5b..40736fb 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007
+/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007, 2008
    Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
@@ -130,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
 
   anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
-                              1024, &net_buffer.ptr);
+                              1024, &net_buffer.ptr, NULL, NULL);
   if (anslen < 0)
     {
       /* Nothing found.  */
@@ -206,7 +206,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
 
   anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
-                             1024, &net_buffer.ptr);
+                             1024, &net_buffer.ptr, NULL, NULL);
   if (anslen < 0)
     {
       /* Nothing found.  */
index 4371af5..a8e8d7b 100644 (file)
@@ -97,7 +97,7 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
 static int
 __libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
                        int class, int type, u_char *answer, int anslen,
-                       u_char **answerp);
+                       u_char **answerp, u_char **answerp2, int *nanswerp2);
 
 /*
  * Formulate a normal query, send, and await answer.
@@ -115,15 +115,20 @@ __libc_res_nquery(res_state statp,
                  int class, int type,  /* class and type of query */
                  u_char *answer,       /* buffer to put answer */
                  int anslen,           /* size of answer buffer */
-                 u_char **answerp)     /* if buffer needs to be enlarged */
+                 u_char **answerp,     /* if buffer needs to be enlarged */
+                 u_char **answerp2,
+                 int *nanswerp2)
 {
-       u_char *buf;
        HEADER *hp = (HEADER *) answer;
        int n, use_malloc = 0;
         u_int oflags = statp->_flags;
 
-       size_t bufsize = QUERYSIZE;
-       buf = alloca (bufsize);
+       size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
+       u_char *buf = alloca (bufsize);
+       u_char *query1 = buf;
+       int nquery1 = -1;
+       u_char *query2 = NULL;
+       int nquery2 = 0;
 
  again:
        hp->rcode = NOERROR;    /* default */
@@ -133,18 +138,47 @@ __libc_res_nquery(res_state statp,
                printf(";; res_query(%s, %d, %d)\n", name, class, type);
 #endif
 
-       n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
-                        buf, bufsize);
-       if (n > 0
-           && (oflags & RES_F_EDNS0ERR) == 0
-           && (statp->options & RES_USE_EDNS0) != 0)
-               n = __res_nopt(statp, n, buf, bufsize, anslen);
+       if (type == T_UNSPEC)
+         {
+           n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
+                            query1, bufsize);
+           if (n > 0)
+             {
+               if ((oflags & RES_F_EDNS0ERR) == 0
+                   && (statp->options & RES_USE_EDNS0) != 0)
+                 n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
+
+               nquery1 = n;
+               query2 = buf + nquery1;
+               n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
+                                NULL, query2, bufsize - n);
+               if (n > 0
+                   && (oflags & RES_F_EDNS0ERR) == 0
+                   && (statp->options & RES_USE_EDNS0) != 0)
+                 n = __res_nopt(statp, n, query2, bufsize - n, anslen / 2);
+               nquery2 = n;
+             }
+         }
+       else
+         {
+           n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
+                            query1, bufsize);
+
+           if (n > 0
+               && (oflags & RES_F_EDNS0ERR) == 0
+               && (statp->options & RES_USE_EDNS0) != 0)
+             n = __res_nopt(statp, n, query1, bufsize, anslen);
+
+           nquery1 = n;
+         }
+
        if (__builtin_expect (n <= 0, 0) && !use_malloc) {
                /* Retry just in case res_nmkquery failed because of too
                   short buffer.  Shouldn't happen.  */
-               bufsize = MAXPACKET;
+               bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET;
                buf = malloc (bufsize);
                if (buf != NULL) {
+                       query1 = buf;
                        use_malloc = 1;
                        goto again;
                }
@@ -168,7 +202,8 @@ __libc_res_nquery(res_state statp,
                return (n);
        }
        assert (answerp == NULL || (void *) *answerp == (void *) answer);
-       n = __libc_res_nsend(statp, buf, n, answer, anslen, answerp);
+       n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
+                            anslen, answerp, answerp2, nanswerp2);
        if (use_malloc)
                free (buf);
        if (n < 0) {
@@ -184,20 +219,37 @@ __libc_res_nquery(res_state statp,
          /* __libc_res_nsend might have reallocated the buffer.  */
          hp = (HEADER *) *answerp;
 
-       if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+       /* We simplify the following tests by assigning HP to HP2.  It
+          is easy to verify that this is the same as ignoring all
+          tests of HP2.  */
+       HEADER *hp2 = answerp2 ? (HEADER *) *answerp2 : hp;
+
+       if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
+           && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
 #ifdef DEBUG
-               if (statp->options & RES_DEBUG)
+               if (statp->options & RES_DEBUG) {
                        printf(";; rcode = %d, ancount=%d\n", hp->rcode,
                            ntohs(hp->ancount));
+                       if (hp != hp2)
+                         printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode,
+                                ntohs(hp2->ancount));
+               }
 #endif
-               switch (hp->rcode) {
+               switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
                case NXDOMAIN:
+                       if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
+                           || (hp2->rcode == NOERROR
+                               && ntohs (hp2->ancount) != 0))
+                               goto success;
                        RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
                        break;
                case SERVFAIL:
                        RES_SET_H_ERRNO(statp, TRY_AGAIN);
                        break;
                case NOERROR:
+                       if (ntohs (hp->ancount) != 0
+                           || ntohs (hp2->ancount) != 0)
+                               goto success;
                        RES_SET_H_ERRNO(statp, NO_DATA);
                        break;
                case FORMERR:
@@ -209,6 +261,7 @@ __libc_res_nquery(res_state statp,
                }
                return (-1);
        }
+ success:
        return (n);
 }
 libresolv_hidden_def (__libc_res_nquery)
@@ -221,7 +274,7 @@ res_nquery(res_state statp,
           int anslen)          /* size of answer buffer */
 {
        return __libc_res_nquery(statp, name, class, type, answer, anslen,
-                                NULL);
+                                NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nquery)
 
@@ -233,11 +286,13 @@ libresolv_hidden_def (res_nquery)
  */
 int
 __libc_res_nsearch(res_state statp,
-           const char *name,   /* domain name */
-           int class, int type,        /* class and type of query */
-           u_char *answer,     /* buffer to put answer */
-           int anslen,         /* size of answer */
-           u_char **answerp)
+                  const char *name,    /* domain name */
+                  int class, int type, /* class and type of query */
+                  u_char *answer,      /* buffer to put answer */
+                  int anslen,          /* size of answer */
+                  u_char **answerp,
+                  u_char **answerp2,
+                  int *nanswerp2)
 {
        const char *cp, * const *domain;
        HEADER *hp = (HEADER *) answer;
@@ -260,7 +315,8 @@ __libc_res_nsearch(res_state statp,
        /* If there aren't any dots, it could be a user-level alias. */
        if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
                return (__libc_res_nquery(statp, cp, class, type, answer,
-                                         anslen, answerp));
+                                         anslen, answerp, answerp2,
+                                         nanswerp2));
 
 #ifdef DEBUG
        if (statp->options & RES_DEBUG)
@@ -276,7 +332,8 @@ __libc_res_nsearch(res_state statp,
        saved_herrno = -1;
        if (dots >= statp->ndots || trailing_dot) {
                ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
-                                             answer, anslen, answerp);
+                                             answer, anslen, answerp,
+                                             answerp2, nanswerp2);
                if (ret > 0 || trailing_dot)
                        return (ret);
                saved_herrno = h_errno;
@@ -285,6 +342,12 @@ __libc_res_nsearch(res_state statp,
                        answer = *answerp;
                        anslen = MAXPACKET;
                }
+               if (answerp2
+                   && (*answerp2 < answer || *answerp2 >= answer + anslen))
+                 {
+                   free (*answerp2);
+                   *answerp2 = NULL;
+                 }
        }
 
        /*
@@ -307,7 +370,8 @@ __libc_res_nsearch(res_state statp,
 
                        ret = __libc_res_nquerydomain(statp, name, *domain,
                                                      class, type,
-                                                     answer, anslen, answerp);
+                                                     answer, anslen, answerp,
+                                                     answerp2, nanswerp2);
                        if (ret > 0)
                                return (ret);
 
@@ -315,6 +379,13 @@ __libc_res_nsearch(res_state statp,
                                answer = *answerp;
                                anslen = MAXPACKET;
                        }
+                       if (answerp2
+                           && (*answerp2 < answer
+                               || *answerp2 >= answer + anslen))
+                         {
+                           free (*answerp2);
+                           *answerp2 = NULL;
+                         }
 
                        /*
                         * If no server present, give up.
@@ -368,7 +439,8 @@ __libc_res_nsearch(res_state statp,
         */
        if (dots && !(tried_as_is || root_on_list)) {
                ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
-                                             answer, anslen, answerp);
+                                             answer, anslen, answerp,
+                                             answerp2, nanswerp2);
                if (ret > 0)
                        return (ret);
        }
@@ -380,6 +452,11 @@ __libc_res_nsearch(res_state statp,
         * else send back meaningless H_ERRNO, that being the one from
         * the last DNSRCH we did.
         */
+       if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen))
+         {
+           free (*answerp2);
+           *answerp2 = NULL;
+         }
        if (saved_herrno != -1)
                RES_SET_H_ERRNO(statp, saved_herrno);
        else if (got_nodata)
@@ -398,7 +475,7 @@ res_nsearch(res_state statp,
            int anslen)         /* size of answer */
 {
        return __libc_res_nsearch(statp, name, class, type, answer,
-                                 anslen, NULL);
+                                 anslen, NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nsearch)
 
@@ -408,12 +485,14 @@ libresolv_hidden_def (res_nsearch)
  */
 static int
 __libc_res_nquerydomain(res_state statp,
-           const char *name,
-           const char *domain,
-           int class, int type,        /* class and type of query */
-           u_char *answer,             /* buffer to put answer */
-           int anslen,                 /* size of answer */
-           u_char **answerp)
+                       const char *name,
+                       const char *domain,
+                       int class, int type,    /* class and type of query */
+                       u_char *answer,         /* buffer to put answer */
+                       int anslen,                     /* size of answer */
+                       u_char **answerp,
+                       u_char **answerp2,
+                       int *nanswerp2)
 {
        char nbuf[MAXDNAME];
        const char *longname = nbuf;
@@ -450,7 +529,7 @@ __libc_res_nquerydomain(res_state statp,
                sprintf(nbuf, "%s.%s", name, domain);
        }
        return (__libc_res_nquery(statp, longname, class, type, answer,
-                                 anslen, answerp));
+                                 anslen, answerp, answerp2, nanswerp2));
 }
 
 int
@@ -462,7 +541,7 @@ res_nquerydomain(res_state statp,
            int anslen)         /* size of answer */
 {
        return __libc_res_nquerydomain(statp, name, domain, class, type,
-                                      answer, anslen, NULL);
+                                      answer, anslen, NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nquerydomain)
 
index 23306a2..b3dbd70 100644 (file)
@@ -176,10 +176,14 @@ evNowTime(struct timespec *res) {
 /* Forward. */
 
 static int             send_vc(res_state, const u_char *, int,
-                               u_char **, int *, int *, int, u_char **);
+                               const u_char *, int,
+                               u_char **, int *, int *, int, u_char **,
+                               u_char **, int *, int *);
 static int             send_dg(res_state, const u_char *, int,
+                               const u_char *, int,
                                u_char **, int *, int *, int,
-                               int *, int *, u_char **);
+                               int *, int *, u_char **,
+                               u_char **, int *, int *);
 #ifdef DEBUG
 static void            Aerror(const res_state, FILE *, const char *, int,
                               const struct sockaddr *);
@@ -334,33 +338,41 @@ libresolv_hidden_def (res_queriesmatch)
 
 int
 __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
-                u_char *ans, int anssiz, u_char **ansp)
+                const u_char *buf2, int buflen2,
+                u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
+                int *nansp2)
 {
-       int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
+  int gotsomewhere, terrno, try, v_circuit, resplen, resplen2, ns, n;
 
        if (statp->nscount == 0) {
                __set_errno (ESRCH);
                return (-1);
        }
 
-       if (anssiz < HFIXEDSZ) {
+       if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) {
                __set_errno (EINVAL);
                return (-1);
        }
 
-       if ((statp->qhook || statp->rhook) && anssiz < MAXPACKET && ansp) {
-               u_char *buf = malloc (MAXPACKET);
-               if (buf == NULL)
-                       return (-1);
-               memcpy (buf, ans, HFIXEDSZ);
-               *ansp = buf;
-               ans = buf;
-               anssiz = MAXPACKET;
+#ifdef USE_HOOKS
+       if (__builtin_expect (statp->qhook || statp->rhook, 0)) {
+               if (anssiz < MAXPACKET && ansp) {
+                       u_char *buf = malloc (MAXPACKET);
+                       if (buf == NULL)
+                               return (-1);
+                       memcpy (buf, ans, HFIXEDSZ);
+                       *ansp = buf;
+                       ans = buf;
+                       anssiz = MAXPACKET;
+               }
        }
+#endif
 
        DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
                (stdout, ";; res_send()\n"), buf, buflen);
-       v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
+       v_circuit = ((statp->options & RES_USEVC)
+                    || buflen > PACKETSZ
+                    || buflen2 > PACKETSZ);
        gotsomewhere = 0;
        terrno = ETIMEDOUT;
 
@@ -442,7 +454,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
         * Some resolvers want to even out the load on their nameservers.
         * Note that RES_BLAST overrides RES_ROTATE.
         */
-       if ((statp->options & RES_ROTATE) != 0 &&
+       if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) &&
            (statp->options & RES_BLAST) == 0) {
                struct sockaddr_in6 *ina;
                unsigned int map;
@@ -479,8 +491,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
 
                if (nsap == NULL)
                        goto next_ns;
- same_ns:
-               if (statp->qhook) {
+           same_ns:
+#ifdef USE_HOOKS
+               if (__builtin_expect (statp->qhook != NULL, 0)) {
                        int done = 0, loops = 0;
 
                        do {
@@ -512,6 +525,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
                                }
                        } while (!done);
                }
+#endif
 
 #ifdef DEBUG
                char tmpbuf[40];
@@ -521,29 +535,34 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
                        ns + 1, inet_ntop(AF_INET6, &nsap->sin6_addr,
                                          tmpbuf, sizeof (tmpbuf))));
 
-               if (v_circuit) {
+               if (__builtin_expect (v_circuit, 0)) {
                        /* Use VC; at most one attempt per server. */
                        try = statp->retry;
-                       n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno,
-                                   ns, ansp);
+                       n = send_vc(statp, buf, buflen, buf2, buflen2,
+                                   &ans, &anssiz, &terrno,
+                                   ns, ansp, ansp2, nansp2, &resplen2);
                        if (n < 0)
                                return (-1);
                        if (n == 0)
                                goto next_ns;
-                       resplen = n;
                } else {
                        /* Use datagrams. */
-                       n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno,
-                                   ns, &v_circuit, &gotsomewhere, ansp);
+                       n = send_dg(statp, buf, buflen, buf2, buflen2,
+                                   &ans, &anssiz, &terrno,
+                                   ns, &v_circuit, &gotsomewhere, ansp,
+                                   ansp2, nansp2, &resplen2);
                        if (n < 0)
                                return (-1);
                        if (n == 0)
                                goto next_ns;
                        if (v_circuit)
+                         // XXX Check whether both requests failed or
+                         // XXX whether one have been answered successfully
                                goto same_ns;
-                       resplen = n;
                }
 
+               resplen = n;
+
                Dprint((statp->options & RES_DEBUG) ||
                       ((statp->pfcode & RES_PRF_REPLY) &&
                        (statp->pfcode & RES_PRF_HEAD1)),
@@ -553,6 +572,11 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
                        (statp->pfcode & RES_PRF_REPLY),
                        (stdout, "%s", ""),
                        ans, (resplen > anssiz) ? anssiz : resplen);
+               if (buf2 != NULL)
+                 DprintQ((statp->options & RES_DEBUG) ||
+                         (statp->pfcode & RES_PRF_REPLY),
+                         (stdout, "%s", ""),
+                         *ansp2, (resplen2 > *nansp2) ? *nansp2 : resplen2);
 
                /*
                 * If we have temporarily opened a virtual circuit,
@@ -563,7 +587,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
                    (statp->options & RES_STAYOPEN) == 0) {
                        __res_iclose(statp, false);
                }
-               if (statp->rhook) {
+#ifdef USE_HOOKS
+               if (__builtin_expect (statp->rhook, 0)) {
                        int done = 0, loops = 0;
 
                        do {
@@ -593,6 +618,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
                        } while (!done);
 
                }
+#endif
                return (resplen);
  next_ns: ;
           } /*foreach ns*/
@@ -612,7 +638,8 @@ int
 res_nsend(res_state statp,
          const u_char *buf, int buflen, u_char *ans, int anssiz)
 {
-       return __libc_res_nsend(statp, buf, buflen, ans, anssiz, NULL);
+  return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
+                         NULL, NULL, NULL);
 }
 libresolv_hidden_def (res_nsend)
 
@@ -620,17 +647,23 @@ libresolv_hidden_def (res_nsend)
 
 static int
 send_vc(res_state statp,
-       const u_char *buf, int buflen, u_char **ansp, int *anssizp,
-       int *terrno, int ns, u_char **anscp)
+       const u_char *buf, int buflen, const u_char *buf2, int buflen2,
+       u_char **ansp, int *anssizp,
+       int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2,
+       int *resplen2)
 {
        const HEADER *hp = (HEADER *) buf;
+       const HEADER *hp2 = (HEADER *) buf2;
        u_char *ans = *ansp;
-       int anssiz = *anssizp;
+       int orig_anssizp = *anssizp;
+       // XXX REMOVE
+       // int anssiz = *anssizp;
        HEADER *anhp = (HEADER *) ans;
        struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
        int truncating, connreset, resplen, n;
-       struct iovec iov[2];
+       struct iovec iov[4];
        u_short len;
+       u_short len2;
        u_char *cp;
 
        connreset = 0;
@@ -677,11 +710,19 @@ send_vc(res_state statp,
        /*
         * Send length & message
         */
-       ns_put16((u_short)buflen, (u_char*)&len);
+       len = htons ((u_short) buflen);
        evConsIovec(&len, INT16SZ, &iov[0]);
        evConsIovec((void*)buf, buflen, &iov[1]);
-       if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, 2))
-           != (INT16SZ + buflen)) {
+       int niov = 2;
+       ssize_t explen = INT16SZ + buflen;
+       if (buf2 != NULL) {
+               len2 = htons ((u_short) buflen2);
+               evConsIovec(&len2, INT16SZ, &iov[2]);
+               evConsIovec((void*)buf2, buflen2, &iov[3]);
+               niov = 4;
+               explen += INT16SZ + buflen2;
+       }
+       if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, niov)) != explen) {
                *terrno = errno;
                Perror(statp, stderr, "write failed", errno);
                __res_iclose(statp, false);
@@ -690,6 +731,8 @@ send_vc(res_state statp,
        /*
         * Receive length & response
         */
+       int recvresp1 = 0;
+       int recvresp2 = buf2 == NULL;
  read_len:
        cp = ans;
        len = INT16SZ;
@@ -718,30 +761,66 @@ send_vc(res_state statp,
                }
                return (0);
        }
+#ifdef _STRING_ARCH_unaligned
+       resplen = ntohs (*(uint16_t *) ans);
+#else
        resplen = ns_get16(ans);
-       if (resplen > anssiz) {
+#endif
+
+       int *thisanssizp;
+       u_char **thisansp;
+       int *thisresplenp;
+       if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
+               thisanssizp = anssizp;
+               thisansp = anscp ?: ansp;
+               assert (anscp != NULL || ansp2 == NULL);
+               thisresplenp = &resplen;
+       } else {
+               if (*anssizp != MAXPACKET) {
+                       /* No buffer allocated for the first
+                          reply.  We can try to use the rest
+                          of the user-provided buffer.  */
+                       *anssizp2 = orig_anssizp - resplen;
+                       *ansp2 = *ansp + resplen;
+               } else {
+                       /* The first reply did not fit into the
+                          user-provided buffer.  Maybe the second
+                          answer will.  */
+                       *anssizp2 = orig_anssizp;
+                       *ansp2 = *ansp;
+               }
+
+               thisanssizp = anssizp2;
+               thisansp = ansp2;
+               thisresplenp = resplen2;
+       }
+       anhp = (HEADER *) *thisansp;
+
+       *thisresplenp = resplen;
+       if (resplen > *thisanssizp) {
+               /* Yes, we test ANSCP here.  If we have two buffers
+                  both will be allocatable.  */
                if (anscp) {
-                       ans = malloc (MAXPACKET);
-                       if (ans == NULL) {
+                       u_char *newp = malloc (MAXPACKET);
+                       if (newp == NULL) {
                                *terrno = ENOMEM;
                                __res_iclose(statp, false);
                                return (0);
                        }
-                       anssiz = MAXPACKET;
-                       *anssizp = MAXPACKET;
-                       *ansp = ans;
-                       *anscp = ans;
-                       anhp = (HEADER *) ans;
+                       *thisanssizp = MAXPACKET;
+                       *thisansp = newp;
+                       anhp = (HEADER *) newp;
                        len = resplen;
                } else {
                        Dprint(statp->options & RES_DEBUG,
                                (stdout, ";; response truncated\n")
                        );
                        truncating = 1;
-                       len = anssiz;
+                       len = *thisanssizp;
                }
        } else
                len = resplen;
+
        if (len < HFIXEDSZ) {
                /*
                 * Undersized message.
@@ -752,7 +831,8 @@ send_vc(res_state statp,
                __res_iclose(statp, false);
                return (0);
        }
-       cp = ans;
+
+       cp = *thisansp;
        while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
                cp += n;
                len -= n;
@@ -768,7 +848,7 @@ send_vc(res_state statp,
                 * Flush rest of answer so connection stays in synch.
                 */
                anhp->tc = 1;
-               len = resplen - anssiz;
+               len = resplen - *thisanssizp;
                while (len != 0) {
                        char junk[PACKETSZ];
 
@@ -787,14 +867,25 @@ send_vc(res_state statp,
         * itself confused, then drop the packet and
         * wait for the correct one.
         */
-       if (hp->id != anhp->id) {
+       if ((recvresp1 || hp->id != anhp->id)
+           && (recvresp2 || hp2->id != anhp->id)) {
                DprintQ((statp->options & RES_DEBUG) ||
                        (statp->pfcode & RES_PRF_REPLY),
                        (stdout, ";; old answer (unexpected):\n"),
-                       ans, (resplen > anssiz) ? anssiz: resplen);
+                       *thisansp,
+                       (resplen > *thisanssiz) ? *thisanssiz: resplen);
                goto read_len;
        }
 
+       /* Mark which reply we received.  */
+       if (recvresp1 == 0 && hp->id == anhp->id)
+         recvresp1 = 1;
+       else
+         recvresp2 = 1;
+       /* Repeat waiting if we have a second answer to arrive.  */
+       if ((recvresp1 & recvresp2) == 0)
+               goto read_len;
+
        /*
         * All is well, or the error is fatal.  Signal that the
         * next nameserver ought not be tried.
@@ -804,19 +895,20 @@ send_vc(res_state statp,
 
 static int
 send_dg(res_state statp,
-       const u_char *buf, int buflen, u_char **ansp, int *anssizp,
-       int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp)
+       const u_char *buf, int buflen, const u_char *buf2, int buflen2,
+       u_char **ansp, int *anssizp,
+       int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp,
+       u_char **ansp2, int *anssizp2, int *resplen2)
 {
        const HEADER *hp = (HEADER *) buf;
+       const HEADER *hp2 = (HEADER *) buf2;
        u_char *ans = *ansp;
-       int anssiz = *anssizp;
-       HEADER *anhp = (HEADER *) ans;
+       int orig_anssizp = *anssizp;
        struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
        struct timespec now, timeout, finish;
        struct pollfd pfd[1];
         int ptimeout;
        struct sockaddr_in6 from;
-       socklen_t fromlen;
        int resplen, seconds, n;
 
        if (EXT(statp).nssocks[ns] == -1) {
@@ -879,6 +971,8 @@ send_dg(res_state statp,
        evAddTime(&finish, &now, &timeout);
        int need_recompute = 0;
        int nwritten = 0;
+       int recvresp1 = 0;
+       int recvresp2 = buf2 == NULL;
        pfd[0].fd = EXT(statp).nssocks[ns];
        pfd[0].events = POLLOUT;
  wait:
@@ -918,35 +1012,73 @@ send_dg(res_state statp,
        }
        __set_errno (0);
        if (pfd[0].revents & POLLOUT) {
-               if (send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL) != buflen) {
+               ssize_t sr;
+               if (nwritten != 0)
+                 sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
+               else
+                 sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
+
+               if (sr != buflen) {
                        if (errno == EINTR || errno == EAGAIN)
                                goto recompute_resend;
                        Perror(statp, stderr, "send", errno);
                        goto err_out;
                }
-               pfd[0].events = POLLIN;
+               if (nwritten != 0 || buf2 == NULL)
+                 pfd[0].events = POLLIN;
+               else
+                 pfd[0].events = POLLIN | POLLOUT;
                ++nwritten;
                goto wait;
        } else if (pfd[0].revents & POLLIN) {
-               fromlen = sizeof(struct sockaddr_in6);
-               if (anssiz < MAXPACKET
+               int *thisanssizp;
+               u_char **thisansp;
+               int *thisresplenp;
+
+               if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
+                       thisanssizp = anssizp;
+                       thisansp = anscp ?: ansp;
+                       assert (anscp != NULL || ansp2 == NULL);
+                       thisresplenp = &resplen;
+               } else {
+                       if (*anssizp != MAXPACKET) {
+                               /* No buffer allocated for the first
+                                  reply.  We can try to use the rest
+                                  of the user-provided buffer.  */
+                               *anssizp2 = orig_anssizp - resplen;
+                               *ansp2 = *ansp + resplen;
+                       } else {
+                               /* The first reply did not fit into the
+                                  user-provided buffer.  Maybe the second
+                                  answer will.  */
+                               *anssizp2 = orig_anssizp;
+                               *ansp2 = *ansp;
+                       }
+
+                       thisanssizp = anssizp2;
+                       thisansp = ansp2;
+                       thisresplenp = resplen2;
+               }
+
+               if (*thisanssizp < MAXPACKET
+                   /* Yes, we test ANSCP here.  If we have two buffers
+                      both will be allocatable.  */
                    && anscp
-                   && (ioctl (pfd[0].fd, FIONREAD, &resplen) < 0
-               || anssiz < resplen)) {
-                       ans = malloc (MAXPACKET);
-                       if (ans == NULL)
-                               ans = *ansp;
-                       else {
-                               anssiz = MAXPACKET;
+                   && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
+                       || *thisanssizp < *thisresplenp)) {
+                       u_char *newp = malloc (MAXPACKET);
+                       if (newp != NULL) {
                                *anssizp = MAXPACKET;
-                               *ansp = ans;
-                               *anscp = ans;
-                               anhp = (HEADER *) ans;
+                               *thisansp = ans = newp;
                        }
                }
-               resplen = recvfrom(pfd[0].fd, (char*)ans, anssiz,0,
-                                  (struct sockaddr *)&from, &fromlen);
-               if (resplen <= 0) {
+               HEADER *anhp = (HEADER *) *thisansp;
+               socklen_t fromlen = sizeof(struct sockaddr_in6);
+               assert (sizeof(from) <= fromlen);
+               *thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp,
+                                        *thisanssizp, 0,
+                                       (struct sockaddr *)&from, &fromlen);
+               if (*thisresplenp <= 0) {
                        if (errno == EINTR || errno == EAGAIN) {
                                need_recompute = 1;
                                goto wait;
@@ -955,17 +1087,18 @@ send_dg(res_state statp,
                        goto err_out;
                }
                *gotsomewhere = 1;
-               if (resplen < HFIXEDSZ) {
+               if (*thisresplenp < HFIXEDSZ) {
                        /*
                         * Undersized message.
                         */
                        Dprint(statp->options & RES_DEBUG,
                               (stdout, ";; undersized: %d\n",
-                               resplen));
+                               *thisresplen));
                        *terrno = EMSGSIZE;
                        goto err_out;
                }
-               if (hp->id != anhp->id) {
+               if ((recvresp1 || hp->id != anhp->id)
+                   && (recvresp2 || hp2->id != anhp->id)) {
                        /*
                         * response from old query, ignore it.
                         * XXX - potential security hazard could
@@ -974,7 +1107,9 @@ send_dg(res_state statp,
                        DprintQ((statp->options & RES_DEBUG) ||
                                (statp->pfcode & RES_PRF_REPLY),
                                (stdout, ";; old answer:\n"),
-                               ans, (resplen > anssiz) ? anssiz : resplen);
+                               thisansp,
+                               (*thisresplen > *thisanssiz)
+                               ? *thisanssiz : *thisresplen);
                        goto wait;
                }
                if (!(statp->options & RES_INSECURE1) &&
@@ -987,14 +1122,16 @@ send_dg(res_state statp,
                        DprintQ((statp->options & RES_DEBUG) ||
                                (statp->pfcode & RES_PRF_REPLY),
                                (stdout, ";; not our server:\n"),
-                               ans, (resplen > anssiz) ? anssiz : resplen);
+                               thisansp,
+                               (*thisresplen > *thisanssiz)
+                               ? *thisanssiz : *thisresplen);
                        goto wait;
                }
 #ifdef RES_USE_EDNS0
                if (anhp->rcode == FORMERR
                    && (statp->options & RES_USE_EDNS0) != 0U) {
                        /*
-                        * Do not retry if the server do not understand
+                        * Do not retry if the server does not understand
                         * EDNS0.  The case has to be captured here, as
                         * FORMERR packet do not carry query section, hence
                         * res_queriesmatch() returns 0.
@@ -1002,15 +1139,23 @@ send_dg(res_state statp,
                        DprintQ(statp->options & RES_DEBUG,
                                (stdout,
                                 "server rejected query with EDNS0:\n"),
-                               ans, (resplen > anssiz) ? anssiz : resplen);
+                               thisans,
+                               (*thisresplen > *thisanssiz)
+                               ? *thisanssiz : *thisresplen);
                        /* record the error */
                        statp->_flags |= RES_F_EDNS0ERR;
                        goto err_out;
         }
 #endif
-               if (!(statp->options & RES_INSECURE2) &&
-                   !res_queriesmatch(buf, buf + buflen,
-                                     ans, ans + anssiz)) {
+               if (!(statp->options & RES_INSECURE2)
+                   && (recvresp1 || !res_queriesmatch(buf, buf + buflen,
+                                                      *thisansp,
+                                                      *thisansp
+                                                      + *thisanssizp))
+                   && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2,
+                                                      *thisansp,
+                                                      *thisansp
+                                                      + *thisanssizp))) {
                        /*
                         * response contains wrong query? ignore it.
                         * XXX - potential security hazard could
@@ -1019,7 +1164,9 @@ send_dg(res_state statp,
                        DprintQ((statp->options & RES_DEBUG) ||
                                (statp->pfcode & RES_PRF_REPLY),
                                (stdout, ";; wrong query name:\n"),
-                               ans, (resplen > anssiz) ? anssiz : resplen);
+                               thisansp,
+                               (*thisresplen > *thisanssiz)
+                               ? *thisanssiz : *thisresplen);
                        goto wait;
                }
                if (anhp->rcode == SERVFAIL ||
@@ -1027,7 +1174,9 @@ send_dg(res_state statp,
                    anhp->rcode == REFUSED) {
                        DprintQ(statp->options & RES_DEBUG,
                                (stdout, "server rejected query:\n"),
-                               ans, (resplen > anssiz) ? anssiz : resplen);
+                               thisansp,
+                               (*thisresplen > *thisanssiz)
+                               ? *thisanssiz : *thisresplen);
                next_ns:
                        __res_iclose(statp, false);
                        /* don't retry if called from dig */
@@ -1038,7 +1187,9 @@ send_dg(res_state statp,
                    && anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
                        DprintQ(statp->options & RES_DEBUG,
                                (stdout, "referred query:\n"),
-                               ans, (resplen > anssiz) ? anssiz : resplen);
+                               thisansp,
+                               (*thisresplen > *thisanssiz)
+                               ? *thisanssiz : *thisresplen);
                        goto next_ns;
                }
                if (!(statp->options & RES_IGNTC) && anhp->tc) {
@@ -1050,8 +1201,18 @@ send_dg(res_state statp,
                               (stdout, ";; truncated answer\n"));
                        *v_circuit = 1;
                        __res_iclose(statp, false);
+                       // XXX if we have received one reply we could
+                       // XXX use it and not repeat it over TCP...
                        return (1);
                }
+               /* Mark which reply we received.  */
+               if (recvresp1 == 0 && hp->id == anhp->id)
+                       recvresp1 = 1;
+               else
+                       recvresp2 = 1;
+               /* Repeat waiting if we have a second answer to arrive.  */
+               if ((recvresp1 & recvresp2) == 0)
+                       goto wait;
                /*
                 * All is well, or the error is fatal.  Signal that the
                 * next nameserver ought not be tried.
index 9a27efd..3de83e3 100644 (file)
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <errno.h>
 #include <ifaddrs.h>
 #include <netdb.h>
+#include <nss.h>
 #include <resolv.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -91,14 +92,6 @@ struct gaih_servtuple
 
 static const struct gaih_servtuple nullserv;
 
-struct gaih_addrtuple
-  {
-    struct gaih_addrtuple *next;
-    char *name;
-    int family;
-    uint32_t addr[4];
-    uint32_t scopeid;
-  };
 
 struct gaih_typeproto
   {
@@ -202,6 +195,7 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
       if (herrno == NETDB_INTERNAL)                                          \
        {                                                                     \
          __set_h_errno (herrno);                                             \
+         _res.options = old_res_options;                                     \
          return -EAI_SYSTEM;                                                 \
        }                                                                     \
       if (herrno == TRY_AGAIN)                                               \
@@ -246,6 +240,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
  }
 
 
+typedef enum nss_status (*nss_gethostbyname4_r)
+  (const char *name, struct gaih_addrtuple **pat,
+   char *buffer, size_t buflen, int *errnop,
+   int *h_errnop, int32_t *ttlp);
 typedef enum nss_status (*nss_gethostbyname3_r)
   (const char *name, int af, struct hostent *host,
    char *buffer, size_t buflen, int *errnop,
@@ -685,87 +683,132 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
          while (!no_more)
            {
-             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)
+             nss_gethostbyname4_r fct4
+               = __nss_lookup_function (nip, "gethostbyname4_r");
+             if (fct4 != NULL)
                {
-                 if (req->ai_family == AF_INET6
-                     || req->ai_family == AF_UNSPEC)
+                 int herrno;
+
+                 while (1)
                    {
-                     gethosts (AF_INET6, struct in6_addr);
-                     no_inet6_data = no_data;
-                     inet6_status = status;
+                     rc = 0;
+                     status = DL_CALL_FCT (fct4, (name, pat, tmpbuf,
+                                                  tmpbuflen, &rc, &herrno,
+                                                  NULL));
+                     if (status != NSS_STATUS_TRYAGAIN
+                         || rc != ERANGE || herrno != NETDB_INTERNAL)
+                       {
+                         if (herrno == NETDB_INTERNAL)
+                           {
+                             __set_h_errno (herrno);
+                             _res.options = old_res_options;
+                             return -EAI_SYSTEM;
+                           }
+                         if (herrno == TRY_AGAIN)
+                           no_data = EAI_AGAIN;
+                         else
+                           no_data = herrno == NO_DATA;
+                         break;
+                       }
+                     tmpbuf = extend_alloca (tmpbuf,
+                                             tmpbuflen, 2 * tmpbuflen);
                    }
-                 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)))
+
+                 if (status == NSS_STATUS_SUCCESS)
                    {
-                     gethosts (AF_INET, struct in_addr);
+                     canon = (*pat)->name;
 
-                     if (req->ai_family == AF_INET)
+                     while (*pat != NULL)
+                       pat = &((*pat)->next);
+                   }
+               }
+             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, struct in6_addr);
                          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)
+                     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)))
                        {
-                         /* If we need the canonical name, get it
-                            from the same service as the result.  */
-                         nss_getcanonname_r cfct;
-                         int herrno;
+                         gethosts (AF_INET, struct in_addr);
 
-                         cfct = __nss_lookup_function (nip, "getcanonname_r");
-                         if (cfct != NULL)
+                         if (req->ai_family == AF_INET)
                            {
-                             const size_t max_fqdn_len = 256;
-                             char *buf = alloca (max_fqdn_len);
-                             char *s;
-
-                             if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
-                                                     max_fqdn_len, &s, &rc,
-                                                     &herrno))
-                                 == NSS_STATUS_SUCCESS)
-                               canon = s;
-                             else
-                               /* Set to name now to avoid using
-                                  gethostbyaddr.  */
-                               canon = name;
+                             no_inet6_data = no_data;
+                             inet6_status = status;
                            }
                        }
 
-                     break;
-                   }
+                     /* 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)
+                           {
+                             /* If we need the canonical name, get it
+                                from the same service as the result.  */
+                             nss_getcanonname_r cfct;
+                             int herrno;
+
+                             cfct = __nss_lookup_function (nip,
+                                                           "getcanonname_r");
+                             if (cfct != NULL)
+                               {
+                                 const size_t max_fqdn_len = 256;
+                                 char *buf = alloca (max_fqdn_len);
+                                 char *s;
+
+                                 if (DL_CALL_FCT (cfct, (at->name ?: name,
+                                                         buf, max_fqdn_len,
+                                                         &s, &rc, &herrno))
+                                     == NSS_STATUS_SUCCESS)
+                                   canon = s;
+                                 else
+                                   /* Set to name now to avoid using
+                                      gethostbyaddr.  */
+                                   canon = name;
+                               }
+                           }
 
-                 /* 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;
+                         break;
+                       }
+
+                     /* 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;
+                   }
                }
 
              if (nss_next_action (nip, status) == NSS_ACTION_RETURN)