aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiddhesh Poyarekar <siddhesh@sourceware.org>2022-03-07 14:08:51 +0530
committerSiddhesh Poyarekar <siddhesh@sourceware.org>2023-09-15 19:53:24 -0400
commit922f2614d69dc47922c1a8e8a08f2bd74874587e (patch)
tree3002b0ef9803ac8f2ff00a65c1cd928ccf4da9d3
parente05e5889b8a307fe4be55b03bcbd7a1c62fc2f2d (diff)
downloadglibc-922f2614d69dc47922c1a8e8a08f2bd74874587e.tar
glibc-922f2614d69dc47922c1a8e8a08f2bd74874587e.tar.gz
glibc-922f2614d69dc47922c1a8e8a08f2bd74874587e.tar.bz2
glibc-922f2614d69dc47922c1a8e8a08f2bd74874587e.zip
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)
-rw-r--r--sysdeps/posix/getaddrinfo.c891
1 files changed, 452 insertions, 439 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 8c78ef9570..57b6834c8b 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;
}