forked from rpms/glibc
1124 lines
32 KiB
Diff
1124 lines
32 KiB
Diff
commit 922f2614d69dc47922c1a8e8a08f2bd74874587e
|
|
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
|
Date: Mon Mar 7 14:08:51 2022 +0530
|
|
|
|
gaih_inet: make numeric lookup a separate routine
|
|
|
|
Introduce the gaih_result structure and general paradigm for cleanups
|
|
that follow to process the lookup request and return a result. A lookup
|
|
function (like text_to_binary_address), should return an integer error
|
|
code and set members of gaih_result based on what it finds. If the
|
|
function does not have a result and no errors have occurred during the
|
|
lookup, it should return 0 and res.at should be set to NULL, allowing a
|
|
subsequent function to do the lookup until we run out of options.
|
|
|
|
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
|
Reviewed-by: DJ Delorie <dj@redhat.com>
|
|
(cherry picked from commit 26dea461191cca519b498890a9682fe4bc8e4c2f)
|
|
|
|
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
|
|
index 8c78ef9570fe0f58..57b6834c8bb3887c 100644
|
|
--- a/sysdeps/posix/getaddrinfo.c
|
|
+++ b/sysdeps/posix/getaddrinfo.c
|
|
@@ -116,6 +116,12 @@ struct gaih_typeproto
|
|
char name[8];
|
|
};
|
|
|
|
+struct gaih_result
|
|
+{
|
|
+ struct gaih_addrtuple *at;
|
|
+ char *canon;
|
|
+};
|
|
+
|
|
/* Values for `protoflag'. */
|
|
#define GAI_PROTO_NOSERVICE 1
|
|
#define GAI_PROTO_PROTOANY 2
|
|
@@ -297,7 +303,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|
} \
|
|
*pat = addrmem; \
|
|
\
|
|
- if (localcanon != NULL && canon == NULL) \
|
|
+ if (localcanon != NULL && res.canon == NULL) \
|
|
{ \
|
|
char *canonbuf = __strdup (localcanon); \
|
|
if (canonbuf == NULL) \
|
|
@@ -306,7 +312,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|
result = -EAI_SYSTEM; \
|
|
goto free_and_return; \
|
|
} \
|
|
- canon = canonbuf; \
|
|
+ res.canon = canonbuf; \
|
|
} \
|
|
if (_family == AF_INET6 && *pat != NULL) \
|
|
got_ipv6 = true; \
|
|
@@ -342,9 +348,9 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
|
|
|
|
static int
|
|
process_canonname (const struct addrinfo *req, const char *orig_name,
|
|
- char **canonp)
|
|
+ struct gaih_result *res)
|
|
{
|
|
- char *canon = *canonp;
|
|
+ char *canon = res->canon;
|
|
|
|
if ((req->ai_flags & AI_CANONNAME) != 0)
|
|
{
|
|
@@ -368,7 +374,7 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
|
|
return -EAI_MEMORY;
|
|
}
|
|
|
|
- *canonp = canon;
|
|
+ res->canon = canon;
|
|
return 0;
|
|
}
|
|
|
|
@@ -460,6 +466,105 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
|
|
return 0;
|
|
}
|
|
|
|
+/* Convert numeric addresses to binary into RES. On failure, RES->AT is set to
|
|
+ NULL and an error code is returned. If AI_NUMERIC_HOST is not requested and
|
|
+ the function cannot determine a result, RES->AT is set to NULL and 0
|
|
+ returned. */
|
|
+
|
|
+static int
|
|
+text_to_binary_address (const char *name, const struct addrinfo *req,
|
|
+ struct gaih_result *res)
|
|
+{
|
|
+ struct gaih_addrtuple *at = res->at;
|
|
+ int result = 0;
|
|
+
|
|
+ assert (at != NULL);
|
|
+
|
|
+ memset (at->addr, 0, sizeof (at->addr));
|
|
+ if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
|
|
+ {
|
|
+ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
|
|
+ at->family = AF_INET;
|
|
+ else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
|
|
+ {
|
|
+ at->addr[3] = at->addr[0];
|
|
+ at->addr[2] = htonl (0xffff);
|
|
+ at->addr[1] = 0;
|
|
+ at->addr[0] = 0;
|
|
+ at->family = AF_INET6;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ result = -EAI_ADDRFAMILY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (req->ai_flags & AI_CANONNAME)
|
|
+ {
|
|
+ char *canonbuf = __strdup (name);
|
|
+ if (canonbuf == NULL)
|
|
+ {
|
|
+ result = -EAI_MEMORY;
|
|
+ goto out;
|
|
+ }
|
|
+ res->canon = canonbuf;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ char *scope_delim = strchr (name, SCOPE_DELIMITER);
|
|
+ int e;
|
|
+
|
|
+ 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)
|
|
+ at->family = AF_INET6;
|
|
+ else if (req->ai_family == AF_INET
|
|
+ && IN6_IS_ADDR_V4MAPPED (at->addr))
|
|
+ {
|
|
+ at->addr[0] = at->addr[3];
|
|
+ at->family = AF_INET;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ result = -EAI_ADDRFAMILY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (scope_delim != NULL
|
|
+ && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
|
|
+ scope_delim + 1, &at->scopeid) != 0)
|
|
+ {
|
|
+ result = -EAI_NONAME;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (req->ai_flags & AI_CANONNAME)
|
|
+ {
|
|
+ char *canonbuf = __strdup (name);
|
|
+ if (canonbuf == NULL)
|
|
+ {
|
|
+ result = -EAI_MEMORY;
|
|
+ goto out;
|
|
+ }
|
|
+ res->canon = canonbuf;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if ((req->ai_flags & AI_NUMERICHOST))
|
|
+ result = -EAI_NONAME;
|
|
+
|
|
+out:
|
|
+ res->at = NULL;
|
|
+ return result;
|
|
+}
|
|
+
|
|
static int
|
|
gaih_inet (const char *name, const struct gaih_service *service,
|
|
const struct addrinfo *req, struct addrinfo **pai,
|
|
@@ -468,9 +573,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
|
|
/ sizeof (struct gaih_typeproto)] = {0};
|
|
|
|
- struct gaih_addrtuple *at = NULL;
|
|
bool got_ipv6 = false;
|
|
- char *canon = NULL;
|
|
const char *orig_name = name;
|
|
|
|
/* Reserve stack memory for the scratch buffer in the getaddrinfo
|
|
@@ -485,6 +588,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
struct gaih_addrtuple *addrmem = NULL;
|
|
int result = 0;
|
|
|
|
+ struct gaih_result res = {0};
|
|
if (name != NULL)
|
|
{
|
|
if (req->ai_flags & AI_IDN)
|
|
@@ -497,533 +601,441 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
malloc_name = true;
|
|
}
|
|
|
|
- uint32_t addr[4];
|
|
- if (__inet_aton_exact (name, (struct in_addr *) addr) != 0)
|
|
+ res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
|
|
+ res.at->scopeid = 0;
|
|
+ res.at->next = NULL;
|
|
+
|
|
+ if ((result = text_to_binary_address (name, req, &res)) != 0)
|
|
+ goto free_and_return;
|
|
+ else if (res.at != NULL)
|
|
+ goto process_list;
|
|
+
|
|
+ int no_data = 0;
|
|
+ int no_inet6_data = 0;
|
|
+ nss_action_list nip;
|
|
+ enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
|
|
+ enum nss_status status = NSS_STATUS_UNAVAIL;
|
|
+ int no_more;
|
|
+ struct resolv_context *res_ctx = NULL;
|
|
+ bool do_merge = false;
|
|
+
|
|
+ /* If we do not have to look for IPv6 addresses or the canonical
|
|
+ name, use the simple, old functions, which do not support
|
|
+ IPv6 scope ids, nor retrieving the canonical name. */
|
|
+ if (req->ai_family == AF_INET
|
|
+ && (req->ai_flags & AI_CANONNAME) == 0)
|
|
{
|
|
- at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
|
|
- at->scopeid = 0;
|
|
- at->next = NULL;
|
|
-
|
|
- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
|
|
- {
|
|
- memcpy (at->addr, addr, sizeof (at->addr));
|
|
- at->family = AF_INET;
|
|
- }
|
|
- else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
|
|
- {
|
|
- at->addr[3] = addr[0];
|
|
- at->addr[2] = htonl (0xffff);
|
|
- at->addr[1] = 0;
|
|
- at->addr[0] = 0;
|
|
- at->family = AF_INET6;
|
|
- }
|
|
- else
|
|
- {
|
|
- result = -EAI_ADDRFAMILY;
|
|
- goto free_and_return;
|
|
- }
|
|
+ int rc;
|
|
+ struct hostent th;
|
|
+ struct hostent *h;
|
|
|
|
- if (req->ai_flags & AI_CANONNAME)
|
|
+ while (1)
|
|
{
|
|
- char *canonbuf = __strdup (name);
|
|
- if (canonbuf == NULL)
|
|
+ rc = __gethostbyname2_r (name, AF_INET, &th,
|
|
+ tmpbuf->data, tmpbuf->length,
|
|
+ &h, &h_errno);
|
|
+ if (rc != ERANGE || h_errno != NETDB_INTERNAL)
|
|
+ break;
|
|
+ if (!scratch_buffer_grow (tmpbuf))
|
|
{
|
|
result = -EAI_MEMORY;
|
|
goto free_and_return;
|
|
}
|
|
- canon = canonbuf;
|
|
}
|
|
|
|
- goto process_list;
|
|
- }
|
|
-
|
|
- char *scope_delim = strchr (name, SCOPE_DELIMITER);
|
|
- int e;
|
|
-
|
|
- if (scope_delim == NULL)
|
|
- e = inet_pton (AF_INET6, name, addr);
|
|
- else
|
|
- e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr);
|
|
-
|
|
- if (e > 0)
|
|
- {
|
|
- at = alloca_account (sizeof (struct gaih_addrtuple),
|
|
- alloca_used);
|
|
- at->scopeid = 0;
|
|
- at->next = NULL;
|
|
-
|
|
- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
|
|
- {
|
|
- memcpy (at->addr, addr, sizeof (at->addr));
|
|
- at->family = AF_INET6;
|
|
- }
|
|
- else if (req->ai_family == AF_INET
|
|
- && IN6_IS_ADDR_V4MAPPED (addr))
|
|
+ if (rc == 0)
|
|
{
|
|
- at->addr[0] = addr[3];
|
|
- at->addr[1] = addr[1];
|
|
- at->addr[2] = addr[2];
|
|
- at->addr[3] = addr[3];
|
|
- at->family = AF_INET;
|
|
+ if (h != NULL)
|
|
+ {
|
|
+ /* We found data, convert it. */
|
|
+ if (!convert_hostent_to_gaih_addrtuple
|
|
+ (req, AF_INET, h, &addrmem))
|
|
+ {
|
|
+ result = -EAI_MEMORY;
|
|
+ goto free_and_return;
|
|
+ }
|
|
+ res.at = addrmem;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (h_errno == NO_DATA)
|
|
+ result = -EAI_NODATA;
|
|
+ else
|
|
+ result = -EAI_NONAME;
|
|
+ goto free_and_return;
|
|
+ }
|
|
}
|
|
else
|
|
{
|
|
- result = -EAI_ADDRFAMILY;
|
|
- goto free_and_return;
|
|
- }
|
|
+ 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.
|
|
+ The name is known, though. */
|
|
+ result = -EAI_NODATA;
|
|
|
|
- if (scope_delim != NULL
|
|
- && __inet6_scopeid_pton ((struct in6_addr *) at->addr,
|
|
- scope_delim + 1,
|
|
- &at->scopeid) != 0)
|
|
- {
|
|
- result = -EAI_NONAME;
|
|
goto free_and_return;
|
|
}
|
|
|
|
- if (req->ai_flags & AI_CANONNAME)
|
|
+ goto process_list;
|
|
+ }
|
|
+
|
|
+#ifdef USE_NSCD
|
|
+ if (__nss_not_use_nscd_hosts > 0
|
|
+ && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
|
|
+ __nss_not_use_nscd_hosts = 0;
|
|
+
|
|
+ if (!__nss_not_use_nscd_hosts
|
|
+ && !__nss_database_custom[NSS_DBSIDX_hosts])
|
|
+ {
|
|
+ /* Try to use nscd. */
|
|
+ struct nscd_ai_result *air = NULL;
|
|
+ int err = __nscd_getai (name, &air, &h_errno);
|
|
+ if (air != NULL)
|
|
{
|
|
- char *canonbuf = __strdup (name);
|
|
- if (canonbuf == NULL)
|
|
+ /* Transform into gaih_addrtuple list. */
|
|
+ bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
|
|
+ char *addrs = air->addrs;
|
|
+
|
|
+ addrmem = calloc (air->naddrs, sizeof (*addrmem));
|
|
+ if (addrmem == NULL)
|
|
{
|
|
result = -EAI_MEMORY;
|
|
goto free_and_return;
|
|
}
|
|
- canon = canonbuf;
|
|
- }
|
|
|
|
- goto process_list;
|
|
- }
|
|
-
|
|
- if ((req->ai_flags & AI_NUMERICHOST) == 0)
|
|
- {
|
|
- int no_data = 0;
|
|
- int no_inet6_data = 0;
|
|
- nss_action_list nip;
|
|
- enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
|
|
- enum nss_status status = NSS_STATUS_UNAVAIL;
|
|
- int no_more;
|
|
- struct resolv_context *res_ctx = NULL;
|
|
- bool do_merge = false;
|
|
-
|
|
- /* If we do not have to look for IPv6 addresses or the canonical
|
|
- name, use the simple, old functions, which do not support
|
|
- IPv6 scope ids, nor retrieving the canonical name. */
|
|
- if (req->ai_family == AF_INET
|
|
- && (req->ai_flags & AI_CANONNAME) == 0)
|
|
- {
|
|
- int rc;
|
|
- struct hostent th;
|
|
- struct hostent *h;
|
|
+ struct gaih_addrtuple *addrfree = addrmem;
|
|
+ struct gaih_addrtuple **pat = &res.at;
|
|
|
|
- while (1)
|
|
+ for (int i = 0; i < air->naddrs; ++i)
|
|
{
|
|
- rc = __gethostbyname2_r (name, AF_INET, &th,
|
|
- tmpbuf->data, tmpbuf->length,
|
|
- &h, &h_errno);
|
|
- if (rc != ERANGE || h_errno != NETDB_INTERNAL)
|
|
- break;
|
|
- if (!scratch_buffer_grow (tmpbuf))
|
|
+ socklen_t size = (air->family[i] == AF_INET
|
|
+ ? INADDRSZ : IN6ADDRSZ);
|
|
+
|
|
+ if (!((air->family[i] == AF_INET
|
|
+ && req->ai_family == AF_INET6
|
|
+ && (req->ai_flags & AI_V4MAPPED) != 0)
|
|
+ || req->ai_family == AF_UNSPEC
|
|
+ || air->family[i] == req->ai_family))
|
|
{
|
|
- result = -EAI_MEMORY;
|
|
- goto free_and_return;
|
|
+ /* Skip over non-matching result. */
|
|
+ addrs += size;
|
|
+ continue;
|
|
}
|
|
- }
|
|
|
|
- if (rc == 0)
|
|
- {
|
|
- if (h != NULL)
|
|
+ if (*pat == NULL)
|
|
+ {
|
|
+ *pat = addrfree++;
|
|
+ (*pat)->scopeid = 0;
|
|
+ }
|
|
+ uint32_t *pataddr = (*pat)->addr;
|
|
+ (*pat)->next = NULL;
|
|
+ if (added_canon || air->canon == NULL)
|
|
+ (*pat)->name = NULL;
|
|
+ else if (res.canon == NULL)
|
|
{
|
|
- /* We found data, convert it. */
|
|
- if (!convert_hostent_to_gaih_addrtuple
|
|
- (req, AF_INET, h, &addrmem))
|
|
+ char *canonbuf = __strdup (air->canon);
|
|
+ if (canonbuf == NULL)
|
|
{
|
|
result = -EAI_MEMORY;
|
|
goto free_and_return;
|
|
}
|
|
- at = addrmem;
|
|
+ res.canon = (*pat)->name = canonbuf;
|
|
}
|
|
- else
|
|
+
|
|
+ if (air->family[i] == AF_INET
|
|
+ && req->ai_family == AF_INET6
|
|
+ && (req->ai_flags & AI_V4MAPPED))
|
|
{
|
|
- if (h_errno == NO_DATA)
|
|
- result = -EAI_NODATA;
|
|
- else
|
|
- result = -EAI_NONAME;
|
|
- goto free_and_return;
|
|
+ (*pat)->family = AF_INET6;
|
|
+ pataddr[3] = *(uint32_t *) addrs;
|
|
+ pataddr[2] = htonl (0xffff);
|
|
+ pataddr[1] = 0;
|
|
+ pataddr[0] = 0;
|
|
+ pat = &((*pat)->next);
|
|
+ added_canon = true;
|
|
+ }
|
|
+ else if (req->ai_family == AF_UNSPEC
|
|
+ || air->family[i] == req->ai_family)
|
|
+ {
|
|
+ (*pat)->family = air->family[i];
|
|
+ memcpy (pataddr, addrs, size);
|
|
+ pat = &((*pat)->next);
|
|
+ added_canon = true;
|
|
+ if (air->family[i] == AF_INET6)
|
|
+ got_ipv6 = true;
|
|
}
|
|
+ addrs += size;
|
|
}
|
|
- else
|
|
- {
|
|
- 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.
|
|
- The name is known, though. */
|
|
- result = -EAI_NODATA;
|
|
|
|
- goto free_and_return;
|
|
- }
|
|
+ free (air);
|
|
|
|
goto process_list;
|
|
}
|
|
+ else if (err == 0)
|
|
+ /* The database contains a negative entry. */
|
|
+ goto free_and_return;
|
|
+ else if (__nss_not_use_nscd_hosts == 0)
|
|
+ {
|
|
+ if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
|
|
+ result = -EAI_MEMORY;
|
|
+ else if (h_errno == TRY_AGAIN)
|
|
+ result = -EAI_AGAIN;
|
|
+ else
|
|
+ result = -EAI_SYSTEM;
|
|
|
|
-#ifdef USE_NSCD
|
|
- if (__nss_not_use_nscd_hosts > 0
|
|
- && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
|
|
- __nss_not_use_nscd_hosts = 0;
|
|
+ goto free_and_return;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ no_more = !__nss_database_get (nss_database_hosts, &nip);
|
|
|
|
- if (!__nss_not_use_nscd_hosts
|
|
- && !__nss_database_custom[NSS_DBSIDX_hosts])
|
|
+ /* 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, so we use the no_inet6
|
|
+ function variant. */
|
|
+ res_ctx = __resolv_context_get ();
|
|
+ if (res_ctx == NULL)
|
|
+ no_more = 1;
|
|
+
|
|
+ while (!no_more)
|
|
+ {
|
|
+ /* Always start afresh; continue should discard previous results
|
|
+ and the hosts database does not support merge. */
|
|
+ res.at = NULL;
|
|
+ free (res.canon);
|
|
+ free (addrmem);
|
|
+ res.canon = NULL;
|
|
+ addrmem = NULL;
|
|
+ got_ipv6 = false;
|
|
+
|
|
+ if (do_merge)
|
|
{
|
|
- /* Try to use nscd. */
|
|
- struct nscd_ai_result *air = NULL;
|
|
- int err = __nscd_getai (name, &air, &h_errno);
|
|
- if (air != NULL)
|
|
+ __set_h_errno (NETDB_INTERNAL);
|
|
+ __set_errno (EBUSY);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ no_data = 0;
|
|
+ nss_gethostbyname4_r *fct4 = NULL;
|
|
+
|
|
+ /* gethostbyname4_r sends out parallel A and AAAA queries and
|
|
+ is thus only suitable for PF_UNSPEC. */
|
|
+ if (req->ai_family == PF_UNSPEC)
|
|
+ fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
|
|
+
|
|
+ if (fct4 != NULL)
|
|
+ {
|
|
+ while (1)
|
|
{
|
|
- /* Transform into gaih_addrtuple list. */
|
|
- bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
|
|
- char *addrs = air->addrs;
|
|
+ status = DL_CALL_FCT (fct4, (name, &res.at,
|
|
+ tmpbuf->data, tmpbuf->length,
|
|
+ &errno, &h_errno,
|
|
+ NULL));
|
|
+ if (status == NSS_STATUS_SUCCESS)
|
|
+ break;
|
|
+ /* gethostbyname4_r may write into AT, so reset it. */
|
|
+ res.at = NULL;
|
|
+ if (status != NSS_STATUS_TRYAGAIN
|
|
+ || errno != ERANGE || h_errno != NETDB_INTERNAL)
|
|
+ {
|
|
+ if (h_errno == TRY_AGAIN)
|
|
+ no_data = EAI_AGAIN;
|
|
+ else
|
|
+ no_data = h_errno == NO_DATA;
|
|
+ break;
|
|
+ }
|
|
|
|
- addrmem = calloc (air->naddrs, sizeof (*addrmem));
|
|
- if (addrmem == NULL)
|
|
+ if (!scratch_buffer_grow (tmpbuf))
|
|
{
|
|
+ __resolv_context_put (res_ctx);
|
|
result = -EAI_MEMORY;
|
|
goto free_and_return;
|
|
}
|
|
+ }
|
|
|
|
- struct gaih_addrtuple *addrfree = addrmem;
|
|
- struct gaih_addrtuple **pat = &at;
|
|
+ if (status == NSS_STATUS_SUCCESS)
|
|
+ {
|
|
+ assert (!no_data);
|
|
+ no_data = 1;
|
|
|
|
- for (int i = 0; i < air->naddrs; ++i)
|
|
+ if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
|
|
{
|
|
- socklen_t size = (air->family[i] == AF_INET
|
|
- ? INADDRSZ : IN6ADDRSZ);
|
|
-
|
|
- if (!((air->family[i] == AF_INET
|
|
- && req->ai_family == AF_INET6
|
|
- && (req->ai_flags & AI_V4MAPPED) != 0)
|
|
- || req->ai_family == AF_UNSPEC
|
|
- || air->family[i] == req->ai_family))
|
|
+ char *canonbuf = __strdup (res.at->name);
|
|
+ if (canonbuf == NULL)
|
|
{
|
|
- /* Skip over non-matching result. */
|
|
- addrs += size;
|
|
- continue;
|
|
+ __resolv_context_put (res_ctx);
|
|
+ result = -EAI_MEMORY;
|
|
+ goto free_and_return;
|
|
}
|
|
+ res.canon = canonbuf;
|
|
+ }
|
|
|
|
- if (*pat == NULL)
|
|
- {
|
|
- *pat = addrfree++;
|
|
- (*pat)->scopeid = 0;
|
|
- }
|
|
- uint32_t *pataddr = (*pat)->addr;
|
|
- (*pat)->next = NULL;
|
|
- if (added_canon || air->canon == NULL)
|
|
- (*pat)->name = NULL;
|
|
- else if (canon == NULL)
|
|
- {
|
|
- char *canonbuf = __strdup (air->canon);
|
|
- if (canonbuf == NULL)
|
|
- {
|
|
- result = -EAI_MEMORY;
|
|
- goto free_and_return;
|
|
- }
|
|
- canon = (*pat)->name = canonbuf;
|
|
- }
|
|
+ struct gaih_addrtuple **pat = &res.at;
|
|
|
|
- if (air->family[i] == AF_INET
|
|
+ while (*pat != NULL)
|
|
+ {
|
|
+ if ((*pat)->family == AF_INET
|
|
&& req->ai_family == AF_INET6
|
|
- && (req->ai_flags & AI_V4MAPPED))
|
|
+ && (req->ai_flags & AI_V4MAPPED) != 0)
|
|
{
|
|
+ uint32_t *pataddr = (*pat)->addr;
|
|
(*pat)->family = AF_INET6;
|
|
- pataddr[3] = *(uint32_t *) addrs;
|
|
+ pataddr[3] = pataddr[0];
|
|
pataddr[2] = htonl (0xffff);
|
|
pataddr[1] = 0;
|
|
pataddr[0] = 0;
|
|
pat = &((*pat)->next);
|
|
- added_canon = true;
|
|
+ no_data = 0;
|
|
}
|
|
else if (req->ai_family == AF_UNSPEC
|
|
- || air->family[i] == req->ai_family)
|
|
+ || (*pat)->family == req->ai_family)
|
|
{
|
|
- (*pat)->family = air->family[i];
|
|
- memcpy (pataddr, addrs, size);
|
|
pat = &((*pat)->next);
|
|
- added_canon = true;
|
|
- if (air->family[i] == AF_INET6)
|
|
+
|
|
+ no_data = 0;
|
|
+ if (req->ai_family == AF_INET6)
|
|
got_ipv6 = true;
|
|
}
|
|
- addrs += size;
|
|
+ else
|
|
+ *pat = ((*pat)->next);
|
|
}
|
|
-
|
|
- free (air);
|
|
-
|
|
- goto process_list;
|
|
}
|
|
- else if (err == 0)
|
|
- /* The database contains a negative entry. */
|
|
- goto free_and_return;
|
|
- else if (__nss_not_use_nscd_hosts == 0)
|
|
- {
|
|
- if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
|
|
- result = -EAI_MEMORY;
|
|
- else if (h_errno == TRY_AGAIN)
|
|
- result = -EAI_AGAIN;
|
|
- else
|
|
- result = -EAI_SYSTEM;
|
|
|
|
- goto free_and_return;
|
|
- }
|
|
+ no_inet6_data = no_data;
|
|
}
|
|
-#endif
|
|
-
|
|
- no_more = !__nss_database_get (nss_database_hosts, &nip);
|
|
-
|
|
- /* 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, so we use the no_inet6
|
|
- function variant. */
|
|
- res_ctx = __resolv_context_get ();
|
|
- if (res_ctx == NULL)
|
|
- no_more = 1;
|
|
-
|
|
- while (!no_more)
|
|
+ else
|
|
{
|
|
- /* Always start afresh; continue should discard previous results
|
|
- and the hosts database does not support merge. */
|
|
- at = NULL;
|
|
- free (canon);
|
|
- free (addrmem);
|
|
- canon = NULL;
|
|
- addrmem = NULL;
|
|
- got_ipv6 = false;
|
|
-
|
|
- if (do_merge)
|
|
+ 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)
|
|
{
|
|
- __set_h_errno (NETDB_INTERNAL);
|
|
- __set_errno (EBUSY);
|
|
- break;
|
|
- }
|
|
-
|
|
- no_data = 0;
|
|
- nss_gethostbyname4_r *fct4 = NULL;
|
|
-
|
|
- /* gethostbyname4_r sends out parallel A and AAAA queries and
|
|
- is thus only suitable for PF_UNSPEC. */
|
|
- if (req->ai_family == PF_UNSPEC)
|
|
- fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
|
|
+ struct gaih_addrtuple **pat = &res.at;
|
|
|
|
- if (fct4 != NULL)
|
|
- {
|
|
- while (1)
|
|
+ if (req->ai_family == AF_INET6
|
|
+ || req->ai_family == AF_UNSPEC)
|
|
{
|
|
- status = DL_CALL_FCT (fct4, (name, &at,
|
|
- tmpbuf->data, tmpbuf->length,
|
|
- &errno, &h_errno,
|
|
- NULL));
|
|
- if (status == NSS_STATUS_SUCCESS)
|
|
- break;
|
|
- /* gethostbyname4_r may write into AT, so reset it. */
|
|
- at = NULL;
|
|
- if (status != NSS_STATUS_TRYAGAIN
|
|
- || errno != ERANGE || h_errno != NETDB_INTERNAL)
|
|
- {
|
|
- if (h_errno == TRY_AGAIN)
|
|
- no_data = EAI_AGAIN;
|
|
- else
|
|
- no_data = h_errno == NO_DATA;
|
|
- break;
|
|
- }
|
|
+ gethosts (AF_INET6);
|
|
+ no_inet6_data = no_data;
|
|
+ inet6_status = status;
|
|
+ }
|
|
+ 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)))
|
|
+ {
|
|
+ gethosts (AF_INET);
|
|
|
|
- if (!scratch_buffer_grow (tmpbuf))
|
|
+ if (req->ai_family == AF_INET)
|
|
{
|
|
- __resolv_context_put (res_ctx);
|
|
- result = -EAI_MEMORY;
|
|
- goto free_and_return;
|
|
+ no_inet6_data = no_data;
|
|
+ inet6_status = status;
|
|
}
|
|
}
|
|
|
|
- if (status == NSS_STATUS_SUCCESS)
|
|
+ /* 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)
|
|
{
|
|
- assert (!no_data);
|
|
- no_data = 1;
|
|
-
|
|
- if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
|
|
+ if ((req->ai_flags & AI_CANONNAME) != 0
|
|
+ && res.canon == NULL)
|
|
{
|
|
- char *canonbuf = __strdup (at->name);
|
|
+ char *canonbuf = getcanonname (nip, res.at, name);
|
|
if (canonbuf == NULL)
|
|
{
|
|
__resolv_context_put (res_ctx);
|
|
result = -EAI_MEMORY;
|
|
goto free_and_return;
|
|
}
|
|
- canon = canonbuf;
|
|
- }
|
|
-
|
|
- struct gaih_addrtuple **pat = &at;
|
|
-
|
|
- while (*pat != NULL)
|
|
- {
|
|
- if ((*pat)->family == AF_INET
|
|
- && req->ai_family == AF_INET6
|
|
- && (req->ai_flags & AI_V4MAPPED) != 0)
|
|
- {
|
|
- uint32_t *pataddr = (*pat)->addr;
|
|
- (*pat)->family = AF_INET6;
|
|
- pataddr[3] = pataddr[0];
|
|
- pataddr[2] = htonl (0xffff);
|
|
- pataddr[1] = 0;
|
|
- pataddr[0] = 0;
|
|
- pat = &((*pat)->next);
|
|
- no_data = 0;
|
|
- }
|
|
- else if (req->ai_family == AF_UNSPEC
|
|
- || (*pat)->family == req->ai_family)
|
|
- {
|
|
- pat = &((*pat)->next);
|
|
-
|
|
- no_data = 0;
|
|
- if (req->ai_family == AF_INET6)
|
|
- got_ipv6 = true;
|
|
- }
|
|
- else
|
|
- *pat = ((*pat)->next);
|
|
- }
|
|
- }
|
|
-
|
|
- no_inet6_data = no_data;
|
|
- }
|
|
- 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)
|
|
- {
|
|
- struct gaih_addrtuple **pat = &at;
|
|
-
|
|
- if (req->ai_family == AF_INET6
|
|
- || req->ai_family == AF_UNSPEC)
|
|
- {
|
|
- gethosts (AF_INET6);
|
|
- no_inet6_data = no_data;
|
|
- inet6_status = status;
|
|
- }
|
|
- 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)))
|
|
- {
|
|
- gethosts (AF_INET);
|
|
-
|
|
- if (req->ai_family == AF_INET)
|
|
- {
|
|
- 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)
|
|
- {
|
|
- char *canonbuf = getcanonname (nip, at, name);
|
|
- if (canonbuf == NULL)
|
|
- {
|
|
- __resolv_context_put (res_ctx);
|
|
- result = -EAI_MEMORY;
|
|
- goto free_and_return;
|
|
- }
|
|
- canon = canonbuf;
|
|
- }
|
|
- status = NSS_STATUS_SUCCESS;
|
|
- }
|
|
- else
|
|
- {
|
|
- /* 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.canon = canonbuf;
|
|
}
|
|
+ status = NSS_STATUS_SUCCESS;
|
|
}
|
|
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;
|
|
- __set_h_errno (NETDB_INTERNAL);
|
|
- __set_errno (EBUSY);
|
|
+ /* 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;
|
|
}
|
|
}
|
|
+ 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;
|
|
+ __set_h_errno (NETDB_INTERNAL);
|
|
+ __set_errno (EBUSY);
|
|
+ }
|
|
+ }
|
|
|
|
- if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
|
|
- break;
|
|
+ if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
|
|
+ break;
|
|
|
|
- /* The hosts database does not support MERGE. */
|
|
- if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
|
|
- do_merge = true;
|
|
+ /* The hosts database does not support MERGE. */
|
|
+ if (nss_next_action (nip, status) == NSS_ACTION_MERGE)
|
|
+ do_merge = true;
|
|
|
|
- nip++;
|
|
- if (nip->module == NULL)
|
|
- no_more = -1;
|
|
- }
|
|
+ nip++;
|
|
+ if (nip->module == NULL)
|
|
+ no_more = -1;
|
|
+ }
|
|
|
|
- __resolv_context_put (res_ctx);
|
|
+ __resolv_context_put (res_ctx);
|
|
|
|
- /* 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;
|
|
- }
|
|
+ /* 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;
|
|
+ }
|
|
|
|
- if (no_data != 0 && no_inet6_data != 0)
|
|
- {
|
|
- /* If both requests timed out report this. */
|
|
- if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
|
|
- result = -EAI_AGAIN;
|
|
- else
|
|
- /* We made requests but they turned out no data. The name
|
|
- is known, though. */
|
|
- result = -EAI_NODATA;
|
|
+ if (no_data != 0 && no_inet6_data != 0)
|
|
+ {
|
|
+ /* If both requests timed out report this. */
|
|
+ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
|
|
+ result = -EAI_AGAIN;
|
|
+ else
|
|
+ /* We made requests but they turned out no data. The name
|
|
+ is known, though. */
|
|
+ result = -EAI_NODATA;
|
|
|
|
- goto free_and_return;
|
|
- }
|
|
+ goto free_and_return;
|
|
}
|
|
|
|
process_list:
|
|
- if (at == NULL)
|
|
+ if (res.at == NULL)
|
|
{
|
|
result = -EAI_NONAME;
|
|
goto free_and_return;
|
|
@@ -1032,21 +1044,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
else
|
|
{
|
|
struct gaih_addrtuple *atr;
|
|
- atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
|
|
- memset (at, '\0', sizeof (struct gaih_addrtuple));
|
|
+ atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
|
|
+ alloca_used);
|
|
+ memset (res.at, '\0', sizeof (struct gaih_addrtuple));
|
|
|
|
if (req->ai_family == AF_UNSPEC)
|
|
{
|
|
- at->next = __alloca (sizeof (struct gaih_addrtuple));
|
|
- memset (at->next, '\0', sizeof (struct gaih_addrtuple));
|
|
+ res.at->next = __alloca (sizeof (struct gaih_addrtuple));
|
|
+ memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
|
|
}
|
|
|
|
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
|
|
{
|
|
- at->family = AF_INET6;
|
|
+ res.at->family = AF_INET6;
|
|
if ((req->ai_flags & AI_PASSIVE) == 0)
|
|
- memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
|
|
- atr = at->next;
|
|
+ memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
|
|
+ atr = res.at->next;
|
|
}
|
|
|
|
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
|
|
@@ -1059,10 +1072,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
|
|
{
|
|
/* Set up the canonical name if we need it. */
|
|
- if ((result = process_canonname (req, orig_name, &canon)) != 0)
|
|
+ if ((result = process_canonname (req, orig_name, &res)) != 0)
|
|
goto free_and_return;
|
|
|
|
- struct gaih_addrtuple *at2 = at;
|
|
+ struct gaih_addrtuple *at2 = res.at;
|
|
size_t socklen;
|
|
sa_family_t family;
|
|
|
|
@@ -1105,8 +1118,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
ai->ai_addr = (void *) (ai + 1);
|
|
|
|
/* We only add the canonical name once. */
|
|
- ai->ai_canonname = (char *) canon;
|
|
- canon = NULL;
|
|
+ ai->ai_canonname = res.canon;
|
|
+ res.canon = NULL;
|
|
|
|
#ifdef _HAVE_SA_LEN
|
|
ai->ai_addr->sa_len = socklen;
|
|
@@ -1152,7 +1165,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|
if (malloc_name)
|
|
free ((char *) name);
|
|
free (addrmem);
|
|
- free (canon);
|
|
+ free (res.canon);
|
|
|
|
return result;
|
|
}
|