summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--elf/Makefile13
-rw-r--r--elf/tst-dlopenrpath.c74
-rw-r--r--elf/tst-dlopenrpathmod.c36
-rw-r--r--resolv/Makefile2
-rw-r--r--resolv/Versions2
-rw-r--r--resolv/nss_dns/dns-canon.c137
-rw-r--r--sysdeps/posix/getaddrinfo.c404
8 files changed, 446 insertions, 242 deletions
diff --git a/ChangeLog b/ChangeLog
index 9bf99bd41e..ef90c576fa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,24 @@
 2004-08-15  Ulrich Drepper  <drepper@redhat.com>
 
-	* intl/tst-gettext.sh: Adjust for change for de.po file to UTF-8.
+	* sysdeps/posix/getaddrinfo.c (gaih_addrtuple): Change type of
+	addr to avoid casts.
+	(gethosts): Removed.
+	(gethosts2): Renamed to gethosts.  Make it usable for family !=
+	AF_UNSPEC.  Fix AI_V4MAPPED.
+	(gaih_inet): Remove use of old gethosts.  Always use what used to be
+	gethosts2.  If entry is found, try to use the same NSS module's
+	getcanonname_r function.  Use gethostbyaddr for AI_CANONNAME only
+	if getcanonname_r was not available.  Fix filtering of AI_V4MAPPED
+	addresses.  Numerous cleanups.
+	* resolv/nss_dns/dns-canon.c: New file.
+	* resolv/Makefile (libnss_dns-routines): Add dns-canon.
+	* resolv/Versions (libnss_dns): Add _nss_dns_getcanonname_r.
+
+	* elf/Makefile: Add rules to build and run tst-dlopenrpath.
+	* elf/tst-dlopenrpath.c: New file.
+	* elf/tst-dlopenrpathmod.c: New file.
+
+	* intl/tst-gettext.sh: Adjust for change of de.po file to UTF-8.
 	* intl/tst-gettext.c: Likewise.
 
 	* nss/getent.c (ahosts_keys_int): Correctly print IPv6 addresses.
diff --git a/elf/Makefile b/elf/Makefile
index 9bbc304455..ac8319bc5f 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -82,7 +82,7 @@ distribute	:= rtld-Rules \
 		   tst-array1.exp tst-array2.exp tst-array4.exp \
 		   tst-array2dep.c tst-piemod1.c \
 		   tst-execstack-mod.c tst-dlmodcount.c \
-		   check-textrel.c dl-sysdep.h
+		   check-textrel.c dl-sysdep.h test-dlopenrpathmod.c
 
 CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables
 CFLAGS-dl-lookup.c = -fexceptions -fasynchronous-unwind-tables
@@ -152,7 +152,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
 	 restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
 	 circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \
 	 tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-align \
-	 $(tests-execstack-$(have-z-execstack)) tst-dlmodcount
+	 $(tests-execstack-$(have-z-execstack)) tst-dlmodcount tst-dlopenrpath
 #	 reldep9
 test-srcs = tst-pathopt
 tests-vis-yes = vismain
@@ -184,7 +184,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		circlemod3 circlemod3a \
 		reldep8mod1 reldep8mod2 reldep8mod3 \
 		reldep9mod1 reldep9mod2 reldep9mod3 \
-		tst-alignmod $(modules-execstack-$(have-z-execstack))
+		tst-alignmod $(modules-execstack-$(have-z-execstack)) \
+		tst-dlopenrpathmod
 ifeq (yes,$(have-initfini-array))
 modules-names += tst-array2dep
 endif
@@ -747,3 +748,9 @@ $(objpfx)tst-dlmodcount: $(libdl)
 $(objpfx)tst-dlmodcount.out: $(test-modules)
 
 endif
+
+$(objpfx)tst-dlopenrpathmod.so: $(libdl)
+$(objpfx)tst-dlopenrpath: $(objpfx)tst-dlopenrpathmod.so $(libdl)
+CFLAGS-tst-dlopenrpath.c += -DPFX=\"$(objpfx)\"
+LDFLAGS-tst-dlopenrpathmod.so += -Wl,-rpath,\$$ORIGIN/test-subdir
+$(objpfx)tst-dlopenrpath.out: $(objpfx)firstobj.so
diff --git a/elf/tst-dlopenrpath.c b/elf/tst-dlopenrpath.c
new file mode 100644
index 0000000000..964f103b8c
--- /dev/null
+++ b/elf/tst-dlopenrpath.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+extern int foo (void);
+
+static const char testsubdir[] = PFX "test-subdir";
+
+
+static int
+do_test (void)
+{
+  struct stat64 st;
+  int result = 1;
+
+  if (mkdir (testsubdir, 0777) != 0
+      && (errno != EEXIST
+	  || stat64 (testsubdir, &st) != 0
+	  || !S_ISDIR (st.st_mode)))
+    {
+      printf ("cannot create directory %s\n", testsubdir);
+      return 1;
+    }
+
+  if (system ("cp " PFX "firstobj.so " PFX "test-subdir/in-subdir.so") != 0)
+    {
+      puts ("cannot copy DSO");
+      return 1;
+    }
+
+  void *p = dlopen ("in-subdir.so", RTLD_LAZY|RTLD_LOCAL);
+  if (p != NULL)
+    {
+      puts ("succeeded in opening in-subdir.so from do_test");
+      dlclose (p);
+      goto out;
+    }
+
+  result = foo ();
+
+ out:
+#if 0
+  unlink (PFX "test-subdir/in-subdir.so");
+  rmdir (testsubdir);
+#endif
+
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/elf/tst-dlopenrpathmod.c b/elf/tst-dlopenrpathmod.c
new file mode 100644
index 0000000000..8fe7873c9d
--- /dev/null
+++ b/elf/tst-dlopenrpathmod.c
@@ -0,0 +1,36 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+
+int
+foo (void)
+{
+  void *p = dlopen ("in-subdir.so", RTLD_LAZY|RTLD_LOCAL);
+  if (p != NULL)
+    {
+      dlclose (p);
+      return 0;
+    }
+
+  puts ("couldn't open in-subdir.so from foo");
+  return 1;
+}
diff --git a/resolv/Makefile b/resolv/Makefile
index a91e8a6f44..f6230da8fb 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -57,7 +57,7 @@ libanl-routines := gai_cancel gai_error gai_misc gai_notify gai_suspend \
 subdir-dirs = nss_dns
 vpath %.c nss_dns
 
-libnss_dns-routines	:= dns-host dns-network
+libnss_dns-routines	:= dns-host dns-network dns-canon
 ifneq ($(build-static-nss),yes)
 libnss_dns-inhibit-o	= $(filter-out .os,$(object-suffixes))
 endif
diff --git a/resolv/Versions b/resolv/Versions
index a809508aa0..0e4fea5e19 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -86,7 +86,7 @@ libnss_dns {
   GLIBC_PRIVATE {
     _nss_dns_gethostbyaddr_r; _nss_dns_gethostbyname2_r;
     _nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r;
-    _nss_dns_getnetbyname_r;
+    _nss_dns_getnetbyname_r; _nss_dns_getcanonname_r;
   }
 }
 
diff --git a/resolv/nss_dns/dns-canon.c b/resolv/nss_dns/dns-canon.c
new file mode 100644
index 0000000000..e5b38f5e7c
--- /dev/null
+++ b/resolv/nss_dns/dns-canon.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <arpa/nameser.h>
+#include <nsswitch.h>
+
+
+#if PACKETSZ > 65536
+# define MAXPACKET	PACKETSZ
+#else
+# define MAXPACKET	65536
+#endif
+
+
+/* We need this time later.  */
+typedef union querybuf
+{
+  HEADER hdr;
+  unsigned char buf[MAXPACKET];
+} querybuf;
+
+
+enum nss_status
+_nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
+			 char **result,int *errnop, int *h_errnop)
+{
+  /* Just an alibi buffer, res_nquery will allocate a real buffer for
+     us.  */
+  unsigned char buf[20];
+  union
+  {
+    querybuf *buf;
+    unsigned char *ptr;
+  } ansp = { .ptr = buf };
+  enum nss_status status;
+
+  int r = __libc_res_nquery (&_res, name, ns_c_in, ns_t_cname,
+			     buf, sizeof (buf), &ansp.ptr);
+  if (r > 0)
+    {
+      /* We need to decode the response.  Just one question record.
+	 And if we got no answers we bail out, too.  */
+      if (ansp.buf->hdr.qdcount != htons (1)
+	  || ansp.buf->hdr.ancount == 0)
+	goto unavail;
+
+      /* Beginning and end of the buffer with query, answer, and the
+	 rest.  */
+      unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
+      unsigned char *endptr = ansp.ptr + r;
+
+      /* Skip over the query.  This is the name, type, and class.  */
+      int s = __dn_skipname (ptr, endptr);
+      if (s < 0)
+	goto unavail;
+
+      /* Skip over the name and the two 16-bit values containing type
+	 and class.  */
+      ptr += s + 2 * sizeof (uint16_t);
+
+      /* Now the reply.  First again the name from the query, then
+	 type, class, TTL, and the length of the RDATA.  */
+      s = __dn_skipname (ptr, endptr);
+      if (s < 0)
+	goto unavail;
+
+      ptr += s;
+
+      /* Check whether type and class match.  */
+      if (*(uint16_t *) ptr != htons (ns_t_cname))
+	goto unavail;
+
+      ptr += sizeof (uint16_t);
+      if (*(uint16_t *) ptr != htons (ns_c_in))
+	goto unavail;
+
+      /* Also skip over the TTL and rdata length.  */
+      ptr += sizeof (uint16_t) + sizeof (uint32_t) + sizeof (int16_t);
+
+      /* Now the name we are looking for.  */
+      s = __dn_expand (ansp.buf->buf, endptr, ptr, buffer, buflen);
+      if (s < 0)
+	{
+	  if (errno != EMSGSIZE)
+	    goto unavail;
+
+	  /* The buffer is too small.  */
+	  *errnop = ERANGE;
+	  status = NSS_STATUS_TRYAGAIN;
+	  h_errno = NETDB_INTERNAL;
+	}
+      else
+	{
+	  /* Success.  */
+	  *result = buffer;
+	  status = NSS_STATUS_SUCCESS;
+	}
+    }
+  else if (h_errno == TRY_AGAIN)
+    {
+    again:
+      status = NSS_STATUS_TRYAGAIN;
+      *errnop = errno;
+    }
+  else
+    {
+    unavail:
+      status = NSS_STATUS_UNAVAIL;
+      *errnop = errno;
+    }
+  *h_errnop = h_errno;
+
+  if (ansp.ptr != buf)
+    free (ansp.ptr);
+
+  return status;
+}
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index bdfdcfbcad..23b74296c1 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -89,7 +89,7 @@ struct gaih_addrtuple
   {
     struct gaih_addrtuple *next;
     int family;
-    char addr[16];
+    uint32_t addr[4];
     uint32_t scopeid;
   };
 
@@ -290,92 +290,6 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
   no_data = 0;								      \
   do {									      \
     tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);		      \
-    rc = __gethostbyname2_r (name, _family, &th, tmpbuf,		      \
-         tmpbuflen, &h, &herrno);					      \
-  } while (rc == ERANGE && herrno == NETDB_INTERNAL);			      \
-  if (rc != 0)								      \
-    {									      \
-      if (herrno == NETDB_INTERNAL)					      \
-	{								      \
-	  __set_h_errno (herrno);					      \
-	  return -EAI_SYSTEM;						      \
-	}								      \
-      if (herrno == TRY_AGAIN)						      \
-	no_data = EAI_AGAIN;						      \
-      else								      \
-	no_data = herrno == NO_DATA;					      \
-    }									      \
-  else if (h != NULL)							      \
-    {									      \
-      for (i = 0; h->h_addr_list[i]; i++)				      \
-	{								      \
-	  if (*pat == NULL) {						      \
-	    *pat = __alloca (sizeof (struct gaih_addrtuple));		      \
-	    (*pat)->scopeid = 0;					      \
-	  }								      \
-	  (*pat)->next = NULL;						      \
-	  (*pat)->family = _family;					      \
-	  memcpy ((*pat)->addr, h->h_addr_list[i],			      \
-		 sizeof(_type));					      \
-	  pat = &((*pat)->next);					      \
-	}								      \
-      if (_family == AF_INET6)						      \
-	got_ipv6 = true;						      \
-    }									      \
-  else if (_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))	      \
-    {									      \
-      /* We have to add V4 mapped addresses.  Maybe we discard them	      \
-         later again but we get them anyhow for now.  */		      \
-      while ((rc = __gethostbyname2_r (name, AF_INET6, &th, tmpbuf,	      \
-				       tmpbuflen, &h, &herrno)) == ERANGE     \
-	     && herrno == NETDB_INTERNAL)				      \
-	tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);	      \
-									      \
-      if (rc != 0)							      \
-	{								      \
-	  if (herrno == NETDB_INTERNAL)					      \
-	    {								      \
-	      __set_h_errno (herrno);					      \
-	      return -EAI_SYSTEM;					      \
-	    }								      \
-	  if (herrno == TRY_AGAIN)					      \
-	    no_data = EAI_AGAIN;					      \
-	  else								      \
-	    no_data = herrno == NO_DATA;				      \
-	}								      \
-      else if (h != NULL)						      \
-	{								      \
-	  for (i = 0; h->h_addr_list[i]; ++i)				      \
-	    {								      \
-	      if (*pat == NULL)						      \
-		{							      \
-		  *pat = __alloca (sizeof (struct gaih_addrtuple));	      \
-		  (*pat)->scopeid = 0;					      \
-		}							      \
-	      uint32_t *addr = (uint32_t *) (*pat)->addr;		      \
-	      (*pat)->next = NULL;					      \
-	      (*pat)->family = _family;					      \
-	      addr[3] = *(uint32_t *) h->h_addr_list[i];		      \
-	      addr[2] = htonl (0xffff);					      \
-	      addr[1] = 0;						      \
-	      addr[0] = 0;						      \
-	      pat = &((*pat)->next);					      \
-	    }								      \
-	}								      \
-    }									      \
- }
-
-
-#define gethosts2(_family, _type) \
- {									      \
-  int i, herrno;							      \
-  size_t tmpbuflen;							      \
-  struct hostent th;							      \
-  char *tmpbuf = NULL;							      \
-  tmpbuflen = 512;							      \
-  no_data = 0;								      \
-  do {									      \
-    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);		      \
     rc = 0;								      \
     status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf,		      \
            tmpbuflen, &rc, &herrno));					      \
@@ -400,23 +314,42 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
     {									      \
       for (i = 0; h->h_addr_list[i]; i++)				      \
 	{								      \
-	  if (*pat == NULL) {						      \
-	    *pat = __alloca (sizeof (struct gaih_addrtuple));		      \
-	    (*pat)->scopeid = 0;					      \
-	  }								      \
+	  if (*pat == NULL)						      \
+	    {								      \
+	      *pat = __alloca (sizeof (struct gaih_addrtuple));		      \
+	      (*pat)->scopeid = 0;					      \
+	    }								      \
+	  uint32_t *addr = (*pat)->addr;				      \
 	  (*pat)->next = NULL;						      \
-	  (*pat)->family = _family;					      \
-	  memcpy ((*pat)->addr, h->h_addr_list[i],			      \
-		 sizeof(_type));					      \
+	  if (_family == AF_INET && req->ai_family == AF_INET6)		      \
+	    {								      \
+	      (*pat)->family = AF_INET6;				      \
+	      addr[3] = *(uint32_t *) h->h_addr_list[i];		      \
+	      addr[2] = htonl (0xffff);					      \
+	      addr[1] = 0;						      \
+	      addr[0] = 0;						      \
+	    }								      \
+	  else								      \
+	    {								      \
+	      (*pat)->family = _family;					      \
+	      memcpy (addr, h->h_addr_list[i], sizeof(_type));		      \
+	    }								      \
 	  pat = &((*pat)->next);					      \
 	}								      \
+									      \
+      if (_family == AF_INET6 && i > 0)					      \
+	got_ipv6 = true;						      \
     }									      \
  }
 
+
 typedef enum nss_status (*nss_gethostbyname2_r)
   (const char *name, int af, struct hostent *host,
    char *buffer, size_t buflen, int *errnop,
    int *h_errnop);
+typedef enum nss_status (*nss_getcanonname_r)
+  (const char *name, char *buffer, size_t buflen, char **result,
+   int *errnop, int *h_errnop);
 extern service_user *__nss_hosts_database attribute_hidden;
 
 static int
@@ -428,6 +361,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
   struct gaih_addrtuple *at = NULL;
   int rc;
   bool got_ipv6 = false;
+  const char *canon = NULL;
 
   if (req->ai_protocol || req->ai_socktype)
     {
@@ -581,10 +515,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    at->family = AF_INET;
 	  else if (req->ai_family == AF_INET6 && req->ai_flags & AI_V4MAPPED)
 	    {
-	      ((uint32_t *) at->addr)[3] = *(uint32_t *) at->addr;
-	      ((uint32_t *) at->addr)[2] = htonl (0xffff);
-	      ((uint32_t *) at->addr)[1] = 0;
-	      ((uint32_t *) at->addr)[0] = 0;
+	      at->addr[3] = at->addr[0];
+	      at->addr[2] = htonl (0xffff);
+	      at->addr[1] = 0;
+	      at->addr[0] = 0;
 	      at->family = AF_INET6;
 	    }
 	  else
@@ -607,7 +541,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	      else if (req->ai_family == AF_INET
 		       && IN6_IS_ADDR_V4MAPPED (at->addr))
 		{
-		  *(uint32_t *) at->addr = ((uint32_t *) at->addr)[3];
+		  at->addr[0] = at->addr[3];
 		  at->family = AF_INET;
 		}
 	      else
@@ -645,82 +579,110 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  struct gaih_addrtuple **pat = &at;
 	  int no_data = 0;
 	  int no_inet6_data = 0;
+	  service_user *nip = NULL;
+	  enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+	  enum nss_status status = NSS_STATUS_UNAVAIL;
+	  int no_more;
+	  nss_gethostbyname2_r fct;
+	  int old_res_options;
+
+	  if (__nss_hosts_database != NULL)
+	    {
+	      no_more = 0;
+	      nip = __nss_hosts_database;
+	    }
+	  else
+	    no_more = __nss_database_lookup ("hosts", NULL,
+					     "dns [!UNAVAIL=return] files",
+					     &nip);
+
+	  if (__res_maybe_init (&_res, 0) == -1)
+	    no_more = 1;
+
 	  /* If we are looking for both IPv4 and IPv6 address we don't
 	     want the lookup functions to automatically promote IPv4
 	     addresses to IPv6 addresses.  Currently this is decided
 	     by setting the RES_USE_INET6 bit in _res.options.  */
-	  if (req->ai_family == AF_UNSPEC)
-	    {
-	      service_user *nip = NULL;
-	      enum nss_status inet6_status, status = NSS_STATUS_UNAVAIL;
-	      int no_more;
-	      nss_gethostbyname2_r fct;
-	      int old_res_options;
-
-	      if (__nss_hosts_database != NULL)
-		{
-		  no_more = 0;
-		  nip = __nss_hosts_database;
-		}
-	      else
-		no_more = __nss_database_lookup ("hosts", NULL,
-						 "dns [!UNAVAIL=return] files",
-						 &nip);
+	  old_res_options = _res.options;
+	  _res.options &= ~RES_USE_INET6;
 
-	      if (__res_maybe_init (&_res, 0) == -1)
-		no_more = 1;
-	      old_res_options = _res.options;
-	      _res.options &= ~RES_USE_INET6;
+	  while (!no_more)
+	    {
+	      fct = __nss_lookup_function (nip, "gethostbyname2_r");
 
-	      while (!no_more)
+	      if (fct != NULL)
 		{
-		  fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-		  if (fct != NULL)
+		  if (req->ai_family == AF_INET6
+		      || req->ai_family == AF_UNSPEC)
 		    {
-		      gethosts2 (AF_INET6, struct in6_addr);
+		      gethosts (AF_INET6, struct in6_addr);
 		      no_inet6_data = no_data;
 		      inet6_status = status;
-		      gethosts2 (AF_INET, struct in_addr);
-
-		      /* 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)
-			break;
-
-		      /* We can have different states for AF_INET
-			 and AF_INET6. Try to find a usefull 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 (req->ai_family == AF_INET
+		      || req->ai_family == AF_UNSPEC
+		      || (req->ai_family == AF_INET6
+			  && (req->ai_flags & AI_V4MAPPED)))
+		    {
+		      gethosts (AF_INET, struct in_addr);
 
-		  if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
-		    break;
+		      if (req->ai_family == AF_INET)
+			{
+			  no_inet6_data = no_data;
+			  inet6_status = status;
+			}
+		    }
 
-		  if (nip->next == NULL)
-		    no_more = -1;
-		  else
-		    nip = nip->next;
+		  /* 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 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, (name, buf, max_fqdn_len,
+						  &s, &rc, &herrno))
+			      == NSS_STATUS_SUCCESS)
+			    canon = s;
+			  else
+			    /* Set to name now to avoid using
+			       gethostbyaddr.  */
+			    canon = name;
+			}
+
+		      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;
 		}
 
-	      _res.options = old_res_options;
-	    }
-	  else if (req->ai_family == AF_INET6)
-	    {
-	      gethosts (AF_INET6, struct in6_addr);
-	      no_inet6_data = no_data;
-	    }
-	  else if (req->ai_family == AF_INET)
-	    {
-	      gethosts (AF_INET, struct in_addr);
-	      no_inet6_data = no_data;
+	      if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
+		break;
+
+	      if (nip->next == NULL)
+		no_more = -1;
+	      else
+		nip = nip->next;
 	    }
 
+	  _res.options = old_res_options;
+
 	  if (no_data != 0 && no_inet6_data != 0)
 	    {
 	      /* If both requests timed out report this.  */
@@ -742,13 +704,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
       atr = at = __alloca (sizeof (struct gaih_addrtuple));
       memset (at, '\0', sizeof (struct gaih_addrtuple));
 
-      if (req->ai_family == 0)
+      if (req->ai_family == AF_UNSPEC)
 	{
 	  at->next = __alloca (sizeof (struct gaih_addrtuple));
 	  memset (at->next, '\0', sizeof (struct gaih_addrtuple));
 	}
 
-      if (req->ai_family == 0 || req->ai_family == AF_INET6)
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
 	{
 	  at->family = AF_INET6;
 	  if ((req->ai_flags & AI_PASSIVE) == 0)
@@ -756,11 +718,11 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  atr = at->next;
 	}
 
-      if (req->ai_family == 0 || req->ai_family == AF_INET)
+      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
 	{
 	  atr->family = AF_INET;
 	  if ((req->ai_flags & AI_PASSIVE) == 0)
-	    *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
+	    atr->addr[0] = htonl (INADDR_LOOPBACK);
 	}
     }
 
@@ -770,7 +732,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
   {
     struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
-    size_t socklen, namelen;
+    size_t socklen;
+    size_t canonlen;
     sa_family_t family;
 
     /*
@@ -778,77 +741,46 @@ gaih_inet (const char *name, const struct gaih_service *service,
      */
     while (at2 != NULL)
       {
-	const char *c = NULL;
-
 	/* Only the first entry gets the canonical name.  */
 	if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
 	  {
-	    struct hostent *h = NULL;
-
-	    int herrno;
-	    struct hostent th;
-	    size_t tmpbuflen = 512;
-	    char *tmpbuf = NULL;
-
-	    do
+	    if (canon == NULL)
 	      {
-		tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
-		rc = __gethostbyaddr_r (at2->addr,
-					((at2->family == AF_INET6)
-					 ? sizeof(struct in6_addr)
-					 : sizeof(struct in_addr)),
-					at2->family, &th, tmpbuf, tmpbuflen,
-					&h, &herrno);
+		struct hostent *h = NULL;
+		int herrno;
+		struct hostent th;
+		size_t tmpbuflen = 512;
+		char *tmpbuf = NULL;
 
-	      }
-	    while (rc == ERANGE && herrno == NETDB_INTERNAL);
-
-	    if (rc != 0 && herrno == NETDB_INTERNAL)
-	      {
-		__set_h_errno (herrno);
-		return -EAI_SYSTEM;
-	      }
+		do
+		  {
+		    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
+		    rc = __gethostbyaddr_r (at2->addr,
+					    ((at2->family == AF_INET6)
+					     ? sizeof (struct in6_addr)
+					     : sizeof (struct in_addr)),
+					    at2->family, &th, tmpbuf,
+					    tmpbuflen, &h, &herrno);
+		  }
+		while (rc == ERANGE && herrno == NETDB_INTERNAL);
 
-	    if (h != NULL)
-	      c = h->h_name;
-	    else
-	      {
-		/* We have to try to get the canonical in some other
-		   way.  If we are looking for either AF_INET or
-		   AF_INET6 try the other line.  */
-		if (req->ai_family == AF_UNSPEC)
+		if (rc != 0 && herrno == NETDB_INTERNAL)
 		  {
-		    struct addrinfo *p = NULL;
-		    struct addrinfo **end = &p;
-		    struct addrinfo localreq = *req;
-		    struct addrinfo *runp;
-
-		    localreq.ai_family = AF_INET + AF_INET6 - at2->family;
-		    (void) gaih_inet (name, service, &localreq, end);
-
-		    runp = p;
-		    while (runp != NULL)
-		      {
-			if (p->ai_canonname != name)
-			  {
-			    c = strdupa (p->ai_canonname);
-			    break;
-			  }
-			runp = runp->ai_next;
-		      }
-
-		    freeaddrinfo (p);
+		    __set_h_errno (herrno);
+		    return -EAI_SYSTEM;
 		  }
 
-		/* If this code is used the missing canonical name is
-		   substituted with the name passed in by the user.  */
-		if (c == NULL)
-		  c = name;
+		if (h != NULL)
+		  canon = h->h_name;
+		else
+		  {
+		    assert (name != NULL);
+		    /* If the canonical name cannot be determined, use
+		       the passed in string.  */
+		    canon = name;
+		  }
 	      }
 
-	    if (c == NULL)
-	      return GAIH_OKIFUNSPEC | -EAI_NONAME;
-
 #ifdef HAVE_LIBIDN
 	    if (req->ai_flags & AI_CANONIDN)
 	      {
@@ -859,7 +791,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		  idn_flags |= IDNA_USE_STD3_ASCII_RULES;
 
 		char *out;
-		int rc = __idna_to_unicode_lzlz (c, &out, idn_flags);
+		int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
 		if (rc != IDNA_SUCCESS)
 		  {
 		    if (rc == IDNA_MALLOC_ERROR)
@@ -870,18 +802,18 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		  }
 		/* In case the output string is the same as the input
 		   string no new string has been allocated.  */
-		if (out != c)
+		if (out != canon)
 		  {
-		    c = strdupa (out);
+		    canon = strdupa (out);
 		    free (out);
 		  }
 	      }
 #endif
 
-	    namelen = strlen (c) + 1;
+	    canonlen = strlen (canon) + 1;
 	  }
 	else
-	  namelen = 0;
+	  canonlen = 0;
 
 	if (at2->family == AF_INET6)
 	  {
@@ -891,7 +823,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    /* If we looked up IPv4 mapped address discard them here if
 	       the caller isn't interested in all address and we have
 	       found at least one IPv6 address.  */
-	    if (! got_ipv6
+	    if (got_ipv6
 		&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
 		&& IN6_IS_ADDR_V4MAPPED (at2->addr))
 	      goto ignore;
@@ -904,7 +836,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
 	for (st2 = st; st2 != NULL; st2 = st2->next)
 	  {
-	    *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
+	    *pai = malloc (sizeof (struct addrinfo) + socklen + canonlen);
 	    if (*pai == NULL)
 	      return -EAI_MEMORY;
 
@@ -913,7 +845,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	    (*pai)->ai_socktype = st2->socktype;
 	    (*pai)->ai_protocol = st2->protocol;
 	    (*pai)->ai_addrlen = socklen;
-	    (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
+	    (*pai)->ai_addr = (void *) (*pai + 1);
 #if SALEN
 	    (*pai)->ai_addr->sa_len = socklen;
 #endif /* SALEN */
@@ -924,30 +856,30 @@ gaih_inet (const char *name, const struct gaih_service *service,
 		struct sockaddr_in6 *sin6p =
 		  (struct sockaddr_in6 *) (*pai)->ai_addr;
 
+		sin6p->sin6_port = st2->port;
 		sin6p->sin6_flowinfo = 0;
 		memcpy (&sin6p->sin6_addr,
 			at2->addr, sizeof (struct in6_addr));
-		sin6p->sin6_port = st2->port;
 		sin6p->sin6_scope_id = at2->scopeid;
 	      }
 	    else
 	      {
 		struct sockaddr_in *sinp =
 		  (struct sockaddr_in *) (*pai)->ai_addr;
+		sinp->sin_port = st2->port;
 		memcpy (&sinp->sin_addr,
 			at2->addr, sizeof (struct in_addr));
-		sinp->sin_port = st2->port;
 		memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
 	      }
 
-	    if (namelen != 0)
+	    if (canonlen != 0)
 	      {
 		(*pai)->ai_canonname = ((void *) (*pai) +
 					sizeof (struct addrinfo) + socklen);
-		strcpy ((*pai)->ai_canonname, c);
+		strcpy ((*pai)->ai_canonname, canon);
 
 		/* We do not need to allocate the canonical name anymore.  */
-		namelen = 0;
+		canonlen = 0;
 	      }
 	    else
 	      (*pai)->ai_canonname = NULL;