aboutsummaryrefslogtreecommitdiff
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;