summaryrefslogtreecommitdiff
path: root/sysdeps/posix/getaddrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/posix/getaddrinfo.c')
-rw-r--r--sysdeps/posix/getaddrinfo.c718
1 files changed, 284 insertions, 434 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 1ef3f20d98..553833d1f2 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1,3 +1,21 @@
+/* Host and service name lookups using Name Service Switch modules.
+ Copyright (C) 1996-2018 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU 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, see
+ <http://www.gnu.org/licenses/>. */
+
/* The Inner Net License, Version 2.00
The author(s) grant permission for redistribution and use in source and
@@ -41,7 +59,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <ifaddrs.h>
#include <netdb.h>
#include <nss.h>
-#include <resolv.h>
+#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
+#include <resolv/res_use_inet6.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
@@ -62,13 +82,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <not-cancel.h>
#include <nscd/nscd-client.h>
#include <nscd/nscd_proto.h>
-#include <resolv/res_hconf.h>
+#include <scratch_buffer.h>
+#include <inet/net-internal.h>
+
+/* Former AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES
+ flags, now ignored. */
+#define DEPRECATED_AI_IDN 0x300
-#ifdef HAVE_LIBIDN
-extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
-extern int __idna_to_unicode_lzlz (const char *input, char **output,
- int flags);
-# include <libidn/idna.h>
+#if IS_IN (libc)
+# define feof_unlocked(fp) __feof_unlocked (fp)
#endif
struct gaih_service
@@ -135,24 +157,24 @@ static const struct addrinfo default_hints =
static int
gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
- const struct addrinfo *req, struct gaih_servtuple *st)
+ const struct addrinfo *req, struct gaih_servtuple *st,
+ struct scratch_buffer *tmpbuf)
{
struct servent *s;
- size_t tmpbuflen = 1024;
struct servent ts;
- char *tmpbuf;
int r;
do
{
- tmpbuf = __alloca (tmpbuflen);
-
- r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
- &s);
+ r = __getservbyname_r (servicename, tp->name, &ts,
+ tmpbuf->data, tmpbuf->length, &s);
if (r != 0 || s == NULL)
{
if (r == ERANGE)
- tmpbuflen *= 2;
+ {
+ if (!scratch_buffer_grow (tmpbuf))
+ return -EAI_MEMORY;
+ }
else
return -EAI_SERVICE;
}
@@ -168,87 +190,114 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
return 0;
}
+/* Convert struct hostent to a list of struct gaih_addrtuple objects.
+ h_name is not copied, and the struct hostent object must not be
+ deallocated prematurely. *RESULT must be NULL or a pointer to a
+ linked-list. The new addresses are appended at the end. */
+static bool
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
+ int family,
+ struct hostent *h,
+ struct gaih_addrtuple **result)
+{
+ while (*result)
+ result = &(*result)->next;
+
+ /* Count the number of addresses in h->h_addr_list. */
+ size_t count = 0;
+ for (char **p = h->h_addr_list; *p != NULL; ++p)
+ ++count;
+
+ /* Report no data if no addresses are available, or if the incoming
+ address size is larger than what we can store. */
+ if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
+ return true;
+
+ struct gaih_addrtuple *array = calloc (count, sizeof (*array));
+ if (array == NULL)
+ return false;
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ if (family == AF_INET && req->ai_family == AF_INET6)
+ {
+ /* Perform address mapping. */
+ array[i].family = AF_INET6;
+ memcpy(array[i].addr + 3, h->h_addr_list[i], sizeof (uint32_t));
+ array[i].addr[2] = htonl (0xffff);
+ }
+ else
+ {
+ array[i].family = family;
+ memcpy (array[i].addr, h->h_addr_list[i], h->h_length);
+ }
+ array[i].next = array + i + 1;
+ }
+ array[0].name = h->h_name;
+ array[count - 1].next = NULL;
+
+ *result = array;
+ return true;
+}
+
#define gethosts(_family, _type) \
{ \
- int i; \
- int herrno; \
struct hostent th; \
- struct hostent *h; \
char *localcanon = NULL; \
no_data = 0; \
- while (1) { \
- rc = 0; \
- status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen, \
- &rc, &herrno, NULL, &localcanon)); \
- if (rc != ERANGE || herrno != NETDB_INTERNAL) \
- break; \
- if (!malloc_tmpbuf && __libc_use_alloca (alloca_used + 2 * tmpbuflen)) \
- tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, 2 * tmpbuflen, \
- alloca_used); \
- else \
- { \
- char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL, \
- 2 * tmpbuflen); \
- if (newp == NULL) \
- { \
- result = -EAI_MEMORY; \
- goto free_and_return; \
- } \
- tmpbuf = newp; \
- malloc_tmpbuf = true; \
- tmpbuflen = 2 * tmpbuflen; \
- } \
- } \
- if (status == NSS_STATUS_SUCCESS && rc == 0) \
- h = &th; \
- else \
- h = NULL; \
- if (rc != 0) \
+ while (1) \
+ { \
+ status = DL_CALL_FCT (fct, (name, _family, &th, \
+ tmpbuf->data, tmpbuf->length, \
+ &errno, &h_errno, NULL, &localcanon)); \
+ if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL \
+ || errno != ERANGE) \
+ break; \
+ if (!scratch_buffer_grow (tmpbuf)) \
+ { \
+ __resolv_context_enable_inet6 (res_ctx, res_enable_inet6); \
+ __resolv_context_put (res_ctx); \
+ result = -EAI_MEMORY; \
+ goto free_and_return; \
+ } \
+ } \
+ if (status == NSS_STATUS_NOTFOUND \
+ || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) \
{ \
- if (herrno == NETDB_INTERNAL) \
+ if (h_errno == NETDB_INTERNAL) \
{ \
- __set_h_errno (herrno); \
- _res.options |= old_res_options & RES_USE_INET6; \
+ __resolv_context_enable_inet6 (res_ctx, res_enable_inet6); \
+ __resolv_context_put (res_ctx); \
result = -EAI_SYSTEM; \
goto free_and_return; \
} \
- if (herrno == TRY_AGAIN) \
+ if (h_errno == TRY_AGAIN) \
no_data = EAI_AGAIN; \
else \
- no_data = herrno == NO_DATA; \
+ no_data = h_errno == NO_DATA; \
} \
- else if (h != NULL) \
+ else if (status == NSS_STATUS_SUCCESS) \
{ \
- for (i = 0; h->h_addr_list[i]; i++) \
+ if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem)) \
{ \
- if (*pat == NULL) \
- { \
- *pat = __alloca (sizeof (struct gaih_addrtuple)); \
- (*pat)->scopeid = 0; \
- } \
- uint32_t *addr = (*pat)->addr; \
- (*pat)->next = NULL; \
- (*pat)->name = i == 0 ? strdupa (h->h_name) : NULL; \
- 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 \
+ __resolv_context_enable_inet6 (res_ctx, res_enable_inet6); \
+ __resolv_context_put (res_ctx); \
+ result = -EAI_SYSTEM; \
+ goto free_and_return; \
+ } \
+ *pat = addrmem; \
+ \
+ if (localcanon != NULL && canon == NULL) \
+ { \
+ canonbuf = __strdup (localcanon); \
+ if (canonbuf == NULL) \
{ \
- (*pat)->family = _family; \
- memcpy (addr, h->h_addr_list[i], sizeof(_type)); \
+ result = -EAI_SYSTEM; \
+ goto free_and_return; \
} \
- pat = &((*pat)->next); \
+ canon = canonbuf; \
} \
- \
- if (localcanon != NULL && canon == NULL) \
- canon = strdupa (localcanon); \
- \
- if (_family == AF_INET6 && i > 0) \
+ if (_family == AF_INET6 && *pat != NULL) \
got_ipv6 = true; \
} \
}
@@ -265,22 +314,45 @@ typedef enum nss_status (*nss_gethostbyname3_r)
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;
+/* This function is called if a canonical name is requested, but if
+ the service function did not provide it. It tries to obtain the
+ name using getcanonname_r from the same service NIP. If the name
+ cannot be canonicalized, return a copy of NAME. Return NULL on
+ memory allocation failure. The returned string is allocated on the
+ heap; the caller has to free it. */
+static char *
+getcanonname (service_user *nip, struct gaih_addrtuple *at, const char *name)
+{
+ nss_getcanonname_r cfct = __nss_lookup_function (nip, "getcanonname_r");
+ char *s = (char *) name;
+ if (cfct != NULL)
+ {
+ char buf[256];
+ if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf),
+ &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS)
+ /* If the canonical name cannot be determined, use the passed
+ string. */
+ s = (char *) name;
+ }
+ return __strdup (name);
+}
static int
gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai,
- unsigned int *naddrs)
+ unsigned int *naddrs, struct scratch_buffer *tmpbuf)
{
const struct gaih_typeproto *tp = gaih_inet_typeproto;
struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
struct gaih_addrtuple *at = NULL;
- int rc;
bool got_ipv6 = false;
const char *canon = NULL;
const char *orig_name = name;
- size_t alloca_used = 0;
+
+ /* Reserve stack memory for the scratch buffer in the getaddrinfo
+ function. */
+ size_t alloca_used = sizeof (struct scratch_buffer);
if (req->ai_protocol || req->ai_socktype)
{
@@ -315,7 +387,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
st = (struct gaih_servtuple *)
alloca_account (sizeof (struct gaih_servtuple), alloca_used);
- if ((rc = gaih_inet_serv (service->name, tp, req, st)))
+ int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf);
+ if (__glibc_unlikely (rc != 0))
return rc;
}
else
@@ -340,12 +413,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
alloca_account (sizeof (struct gaih_servtuple),
alloca_used);
- if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
- {
- if (rc)
- continue;
- return rc;
- }
+ if (gaih_inet_serv (service->name,
+ tp, req, newp, tmpbuf) != 0)
+ continue;
*pst = newp;
pst = &(newp->next);
@@ -397,13 +467,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
}
bool malloc_name = false;
- bool malloc_addrmem = false;
struct gaih_addrtuple *addrmem = NULL;
- bool malloc_canonbuf = false;
char *canonbuf = NULL;
- bool malloc_tmpbuf = false;
- char *tmpbuf = NULL;
int result = 0;
+
if (name != NULL)
{
at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
@@ -411,35 +478,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
at->scopeid = 0;
at->next = NULL;
-#ifdef HAVE_LIBIDN
if (req->ai_flags & AI_IDN)
{
- int idn_flags = 0;
- if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
- idn_flags |= IDNA_ALLOW_UNASSIGNED;
- if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
- idn_flags |= IDNA_USE_STD3_ASCII_RULES;
-
- char *p = NULL;
- rc = __idna_to_ascii_lz (name, &p, idn_flags);
- if (rc != IDNA_SUCCESS)
- {
- /* No need to jump to free_and_return here. */
- if (rc == IDNA_MALLOC_ERROR)
- return -EAI_MEMORY;
- if (rc == IDNA_DLOPEN_ERROR)
- return -EAI_SYSTEM;
- return -EAI_IDN_ENCODE;
- }
- /* In case the output string is the same as the input string
- no new string has been allocated. */
- if (p != name)
- {
- name = p;
- malloc_name = true;
- }
+ char *out;
+ result = __idna_to_dns_encoding (name, &out);
+ if (result != 0)
+ return -result;
+ name = out;
+ malloc_name = true;
}
-#endif
if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
{
@@ -466,46 +513,11 @@ gaih_inet (const char *name, const struct gaih_service *service,
{
char *scope_delim = strchr (name, SCOPE_DELIMITER);
int e;
-
- {
- bool malloc_namebuf = false;
- char *namebuf = (char *) name;
-
- if (__glibc_unlikely (scope_delim != NULL))
- {
- if (malloc_name)
- *scope_delim = '\0';
- else
- {
- if (__libc_use_alloca (alloca_used
- + scope_delim - name + 1))
- {
- namebuf = alloca_account (scope_delim - name + 1,
- alloca_used);
- *((char *) __mempcpy (namebuf, name,
- scope_delim - name)) = '\0';
- }
- else
- {
- namebuf = strndup (name, scope_delim - name);
- if (namebuf == NULL)
- {
- assert (!malloc_name);
- return -EAI_MEMORY;
- }
- malloc_namebuf = true;
- }
- }
- }
-
- e = inet_pton (AF_INET6, namebuf, at->addr);
-
- if (malloc_namebuf)
- free (namebuf);
- else if (scope_delim != NULL && malloc_name)
- /* Undo what we did above. */
- *scope_delim = SCOPE_DELIMITER;
- }
+ if (scope_delim == NULL)
+ e = inet_pton (AF_INET6, name, at->addr);
+ else
+ e = __inet_pton_length (AF_INET6, name, scope_delim - name,
+ at->addr);
if (e > 0)
{
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
@@ -522,31 +534,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
goto free_and_return;
}
- if (scope_delim != NULL)
+ if (scope_delim != NULL
+ && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
+ scope_delim + 1,
+ &at->scopeid) != 0)
{
- int try_numericscope = 0;
- if (IN6_IS_ADDR_LINKLOCAL (at->addr)
- || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
- {
- at->scopeid = if_nametoindex (scope_delim + 1);
- if (at->scopeid == 0)
- try_numericscope = 1;
- }
- else
- try_numericscope = 1;
-
- if (try_numericscope != 0)
- {
- char *end;
- assert (sizeof (uint32_t) <= sizeof (unsigned long));
- at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
- 10);
- if (*end != '\0')
- {
- result = -EAI_NONAME;
- goto free_and_return;
- }
- }
+ result = -EAI_NONAME;
+ goto free_and_return;
}
if (req->ai_flags & AI_CANONNAME)
@@ -563,7 +557,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
enum nss_status status = NSS_STATUS_UNAVAIL;
int no_more;
- int old_res_options;
+ struct resolv_context *res_ctx = NULL;
+ bool res_enable_inet6 = false;
/* If we do not have to look for IPv6 addresses or the canonical
name, use the simple, old functions, which do not support
@@ -571,40 +566,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (req->ai_family == AF_INET
&& (req->ai_flags & AI_CANONNAME) == 0)
{
- /* Allocate additional room for struct host_data. */
- size_t tmpbuflen = (512 + MAX_NR_ALIASES * sizeof(char*)
- + 16 * sizeof(char));
- assert (tmpbuf == NULL);
- tmpbuf = alloca_account (tmpbuflen, alloca_used);
int rc;
struct hostent th;
struct hostent *h;
- int herrno;
while (1)
{
- rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf,
- tmpbuflen, &h, &herrno);
- if (rc != ERANGE || herrno != NETDB_INTERNAL)
+ rc = __gethostbyname2_r (name, AF_INET, &th,
+ tmpbuf->data, tmpbuf->length,
+ &h, &h_errno);
+ if (rc != ERANGE || h_errno != NETDB_INTERNAL)
break;
-
- if (!malloc_tmpbuf
- && __libc_use_alloca (alloca_used + 2 * tmpbuflen))
- tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen,
- 2 * tmpbuflen,
- alloca_used);
- else
+ if (!scratch_buffer_grow (tmpbuf))
{
- char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL,
- 2 * tmpbuflen);
- if (newp == NULL)
- {
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- tmpbuf = newp;
- malloc_tmpbuf = true;
- tmpbuflen = 2 * tmpbuflen;
+ result = -EAI_MEMORY;
+ goto free_and_return;
}
}
@@ -612,54 +588,29 @@ gaih_inet (const char *name, const struct gaih_service *service,
{
if (h != NULL)
{
- int i;
- /* We found data, count the number of addresses. */
- for (i = 0; h->h_addr_list[i]; ++i)
- ;
- if (i > 0 && *pat != NULL)
- --i;
-
- if (__libc_use_alloca (alloca_used
- + i * sizeof (struct gaih_addrtuple)))
- addrmem = alloca_account (i * sizeof (struct gaih_addrtuple),
- alloca_used);
- else
- {
- addrmem = malloc (i
- * sizeof (struct gaih_addrtuple));
- if (addrmem == NULL)
- {
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- malloc_addrmem = true;
- }
-
- /* Now convert it into the list. */
- struct gaih_addrtuple *addrfree = addrmem;
- for (i = 0; h->h_addr_list[i]; ++i)
+ /* We found data, convert it. */
+ if (!convert_hostent_to_gaih_addrtuple
+ (req, AF_INET, h, &addrmem))
{
- if (*pat == NULL)
- {
- *pat = addrfree++;
- (*pat)->scopeid = 0;
- }
- (*pat)->next = NULL;
- (*pat)->family = AF_INET;
- memcpy ((*pat)->addr, h->h_addr_list[i],
- h->h_length);
- pat = &((*pat)->next);
+ result = -EAI_MEMORY;
+ goto free_and_return;
}
+ *pat = addrmem;
+ }
+ else
+ {
+ if (h_errno == NO_DATA)
+ result = -EAI_NODATA;
+ else
+ result = -EAI_NONAME;
+ goto free_and_return;
}
}
else
{
- if (herrno == NETDB_INTERNAL)
- {
- __set_h_errno (herrno);
- result = -EAI_SYSTEM;
- }
- else if (herrno == TRY_AGAIN)
+ if (h_errno == NETDB_INTERNAL)
+ result = -EAI_SYSTEM;
+ else if (h_errno == TRY_AGAIN)
result = -EAI_AGAIN;
else
/* We made requests but they turned out no data.
@@ -682,29 +633,18 @@ gaih_inet (const char *name, const struct gaih_service *service,
{
/* Try to use nscd. */
struct nscd_ai_result *air = NULL;
- int herrno;
- int err = __nscd_getai (name, &air, &herrno);
+ int err = __nscd_getai (name, &air, &h_errno);
if (air != NULL)
{
/* Transform into gaih_addrtuple list. */
bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
char *addrs = air->addrs;
- if (__libc_use_alloca (alloca_used
- + air->naddrs * sizeof (struct gaih_addrtuple)))
- addrmem = alloca_account (air->naddrs
- * sizeof (struct gaih_addrtuple),
- alloca_used);
- else
+ addrmem = calloc (air->naddrs, sizeof (*addrmem));
+ if (addrmem == NULL)
{
- addrmem = malloc (air->naddrs
- * sizeof (struct gaih_addrtuple));
- if (addrmem == NULL)
- {
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- malloc_addrmem = true;
+ result = -EAI_MEMORY;
+ goto free_and_return;
}
struct gaih_addrtuple *addrfree = addrmem;
@@ -735,22 +675,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
(*pat)->name = NULL;
else if (canonbuf == NULL)
{
- size_t canonlen = strlen (air->canon) + 1;
- if ((req->ai_flags & AI_CANONIDN) != 0
- && __libc_use_alloca (alloca_used + canonlen))
- canonbuf = alloca_account (canonlen, alloca_used);
- else
+ canonbuf = __strdup (air->canon);
+ if (canonbuf == NULL)
{
- canonbuf = malloc (canonlen);
- if (canonbuf == NULL)
- {
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- malloc_canonbuf = true;
+ result = -EAI_MEMORY;
+ goto free_and_return;
}
- canon = (*pat)->name = memcpy (canonbuf, air->canon,
- canonlen);
+ canon = (*pat)->name = canonbuf;
}
if (air->family[i] == AF_INET
@@ -793,9 +724,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
goto free_and_return;
else if (__nss_not_use_nscd_hosts == 0)
{
- if (herrno == NETDB_INTERNAL && errno == ENOMEM)
+ if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
result = -EAI_MEMORY;
- else if (herrno == TRY_AGAIN)
+ else if (h_errno == TRY_AGAIN)
result = -EAI_AGAIN;
else
result = -EAI_SYSTEM;
@@ -813,34 +744,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
no_more = 0;
nip = __nss_hosts_database;
- /* Initialize configurations. */
- if (__glibc_unlikely (!_res_hconf.initialized))
- _res_hconf_init ();
- 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. */
- old_res_options = _res.options;
- _res.options &= ~RES_USE_INET6;
-
- size_t tmpbuflen = 1024 + sizeof(struct gaih_addrtuple);
- malloc_tmpbuf = !__libc_use_alloca (alloca_used + tmpbuflen);
- assert (tmpbuf == NULL);
- if (!malloc_tmpbuf)
- tmpbuf = alloca_account (tmpbuflen, alloca_used);
- else
- {
- tmpbuf = malloc (tmpbuflen);
- if (tmpbuf == NULL)
- {
- _res.options |= old_res_options & RES_USE_INET6;
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- }
+ addresses to IPv6 addresses, so we use the no_inet6
+ function variant. */
+ res_ctx = __resolv_context_get ();
+ res_enable_inet6 = __resolv_context_disable_inet6 (res_ctx);
+ if (res_ctx == NULL)
+ no_more = 1;
while (!no_more)
{
@@ -854,44 +765,31 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (fct4 != NULL)
{
- int herrno;
-
while (1)
{
- rc = 0;
- status = DL_CALL_FCT (fct4, (name, pat, tmpbuf,
- tmpbuflen, &rc, &herrno,
+ status = DL_CALL_FCT (fct4, (name, pat,
+ tmpbuf->data, tmpbuf->length,
+ &errno, &h_errno,
NULL));
if (status == NSS_STATUS_SUCCESS)
break;
if (status != NSS_STATUS_TRYAGAIN
- || rc != ERANGE || herrno != NETDB_INTERNAL)
+ || errno != ERANGE || h_errno != NETDB_INTERNAL)
{
- if (herrno == TRY_AGAIN)
+ if (h_errno == TRY_AGAIN)
no_data = EAI_AGAIN;
else
- no_data = herrno == NO_DATA;
+ no_data = h_errno == NO_DATA;
break;
}
- if (!malloc_tmpbuf
- && __libc_use_alloca (alloca_used + 2 * tmpbuflen))
- tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen,
- 2 * tmpbuflen,
- alloca_used);
- else
+ if (!scratch_buffer_grow (tmpbuf))
{
- char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL,
- 2 * tmpbuflen);
- if (newp == NULL)
- {
- _res.options |= old_res_options & RES_USE_INET6;
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- tmpbuf = newp;
- malloc_tmpbuf = true;
- tmpbuflen = 2 * tmpbuflen;
+ __resolv_context_enable_inet6
+ (res_ctx, res_enable_inet6);
+ __resolv_context_put (res_ctx);
+ result = -EAI_MEMORY;
+ goto free_and_return;
}
}
@@ -985,54 +883,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
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)
+ canonbuf = getcanonname (nip, at, name);
+ if (canonbuf == NULL)
{
- const size_t max_fqdn_len = 256;
- if ((req->ai_flags & AI_CANONIDN) != 0
- && __libc_use_alloca (alloca_used
- + max_fqdn_len))
- canonbuf = alloca_account (max_fqdn_len,
- alloca_used);
- else
- {
- canonbuf = malloc (max_fqdn_len);
- if (canonbuf == NULL)
- {
- _res.options
- |= old_res_options & RES_USE_INET6;
- result = -EAI_MEMORY;
- goto free_and_return;
- }
- malloc_canonbuf = true;
- }
- char *s;
-
- if (DL_CALL_FCT (cfct, (at->name ?: name,
- canonbuf,
- max_fqdn_len,
- &s, &rc, &herrno))
- == NSS_STATUS_SUCCESS)
- canon = s;
- else
- {
- /* If the canonical name cannot be
- determined, use the passed in
- string. */
- if (malloc_canonbuf)
- {
- free (canonbuf);
- malloc_canonbuf = false;
- }
- canon = name;
- }
+ __resolv_context_enable_inet6
+ (res_ctx, res_enable_inet6);
+ __resolv_context_put (res_ctx);
+ result = -EAI_MEMORY;
+ goto free_and_return;
}
+ canon = canonbuf;
}
status = NSS_STATUS_SUCCESS;
}
@@ -1049,13 +909,17 @@ gaih_inet (const char *name, const struct gaih_service *service,
}
else
{
+ /* Could not locate any of the lookup functions.
+ The NSS lookup code does not consistently set
+ errno, so we need to supply our own error
+ code here. The root cause could either be a
+ resource allocation failure, or a missing
+ service function in the DSO (so it should not
+ be listed in /etc/nsswitch.conf). Assume the
+ former, and return EBUSY. */
status = NSS_STATUS_UNAVAIL;
- /* Could not load any of the lookup functions. Indicate
- an internal error if the failure was due to a system
- error other than the file not being found. We use the
- errno from the last failed callback. */
- if (errno != 0 && errno != ENOENT)
- __set_h_errno (NETDB_INTERNAL);
+ __set_h_errno (NETDB_INTERNAL);
+ __set_errno (EBUSY);
}
}
@@ -1068,9 +932,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
nip = nip->next;
}
- _res.options |= old_res_options & RES_USE_INET6;
+ __resolv_context_enable_inet6 (res_ctx, res_enable_inet6);
+ __resolv_context_put (res_ctx);
- if (h_errno == NETDB_INTERNAL)
+ /* If we have a failure which sets errno, report it using
+ EAI_SYSTEM. */
+ if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+ && h_errno == NETDB_INTERNAL)
{
result = -EAI_SYSTEM;
goto free_and_return;
@@ -1144,46 +1012,31 @@ gaih_inet (const char *name, const struct gaih_service *service,
the passed in string. */
canon = orig_name;
-#ifdef HAVE_LIBIDN
- if (req->ai_flags & AI_CANONIDN)
+ bool do_idn = req->ai_flags & AI_CANONIDN;
+ if (do_idn)
{
- int idn_flags = 0;
- if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
- idn_flags |= IDNA_ALLOW_UNASSIGNED;
- if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
- idn_flags |= IDNA_USE_STD3_ASCII_RULES;
-
char *out;
- int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
- if (rc != IDNA_SUCCESS)
+ int rc = __idna_from_dns_encoding (canon, &out);
+ if (rc == 0)
+ canon = out;
+ else if (rc == EAI_IDN_ENCODE)
+ /* Use the punycode name as a fallback. */
+ do_idn = false;
+ else
{
- if (rc == IDNA_MALLOC_ERROR)
- result = -EAI_MEMORY;
- else if (rc == IDNA_DLOPEN_ERROR)
- result = -EAI_SYSTEM;
- else
- result = -EAI_IDN_ENCODE;
+ result = -rc;
goto free_and_return;
}
- /* In case the output string is the same as the input
- string no new string has been allocated and we
- make a copy. */
- if (out == canon)
- goto make_copy;
- canon = out;
}
- else
-#endif
+ if (!do_idn)
{
-#ifdef HAVE_LIBIDN
- make_copy:
-#endif
- if (malloc_canonbuf)
- /* We already allocated the string using malloc. */
- malloc_canonbuf = false;
+ if (canonbuf != NULL)
+ /* We already allocated the string using malloc, but
+ the buffer is now owned by canon. */
+ canonbuf = NULL;
else
{
- canon = strdup (canon);
+ canon = __strdup (canon);
if (canon == NULL)
{
result = -EAI_MEMORY;
@@ -1274,12 +1127,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
free_and_return:
if (malloc_name)
free ((char *) name);
- if (malloc_addrmem)
- free (addrmem);
- if (malloc_canonbuf)
- free (canonbuf);
- if (malloc_tmpbuf)
- free (tmpbuf);
+ free (addrmem);
+ free (canonbuf);
return result;
}
@@ -2341,10 +2190,7 @@ getaddrinfo (const char *name, const char *service,
if (hints->ai_flags
& ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
-#ifdef HAVE_LIBIDN
- |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED
- |AI_IDN_USE_STD3_ASCII_RULES
-#endif
+ |AI_IDN|AI_CANONIDN|DEPRECATED_AI_IDN
|AI_NUMERICSERV|AI_ALL))
return EAI_BADFLAGS;
@@ -2414,7 +2260,11 @@ getaddrinfo (const char *name, const char *service,
if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET
|| hints->ai_family == AF_INET6)
{
- last_i = gaih_inet (name, pservice, hints, end, &naddrs);
+ struct scratch_buffer tmpbuf;
+ scratch_buffer_init (&tmpbuf);
+ last_i = gaih_inet (name, pservice, hints, end, &naddrs, &tmpbuf);
+ scratch_buffer_free (&tmpbuf);
+
if (last_i != 0)
{
freeaddrinfo (p);
@@ -2510,9 +2360,9 @@ getaddrinfo (const char *name, const char *service,
{
if (fd != -1)
close_retry:
- close_not_cancel_no_status (fd);
+ __close_nocancel_nostatus (fd);
af = q->ai_family;
- fd = __socket (af, SOCK_DGRAM, IPPROTO_IP);
+ fd = __socket (af, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_IP);
}
else
{
@@ -2613,7 +2463,7 @@ getaddrinfo (const char *name, const char *service,
}
if (fd != -1)
- close_not_cancel_no_status (fd);
+ __close_nocancel_nostatus (fd);
/* We got all the source addresses we can get, now sort using
the information. */