mirror of
https://sourceware.org/git/glibc.git
synced 2025-01-09 02:40:08 +00:00
gaih_inet: separate nss lookup loop into its own function
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: DJ Delorie <dj@redhat.com>
(cherry picked from commit 906cecbe08
)
This commit is contained in:
parent
ce64e72b7d
commit
9098deb96a
@ -159,6 +159,14 @@ static const struct addrinfo default_hints =
|
|||||||
.ai_next = NULL
|
.ai_next = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
gaih_result_reset (struct gaih_result *res)
|
||||||
|
{
|
||||||
|
if (res->free_at)
|
||||||
|
free (res->at);
|
||||||
|
free (res->canon);
|
||||||
|
memset (res, 0, sizeof (*res));
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
||||||
@ -197,13 +205,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
|||||||
|
|
||||||
/* Convert struct hostent to a list of struct gaih_addrtuple objects. h_name
|
/* 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
|
is not copied, and the struct hostent object must not be deallocated
|
||||||
prematurely. The new addresses are appended to the tuple array in
|
prematurely. The new addresses are appended to the tuple array in RES. */
|
||||||
RESULT. */
|
|
||||||
static bool
|
static bool
|
||||||
convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
|
||||||
int family,
|
struct hostent *h, struct gaih_result *res)
|
||||||
struct hostent *h,
|
|
||||||
struct gaih_addrtuple **result)
|
|
||||||
{
|
{
|
||||||
/* Count the number of addresses in h->h_addr_list. */
|
/* Count the number of addresses in h->h_addr_list. */
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
@ -215,7 +220,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|||||||
if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
|
if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
struct gaih_addrtuple *array = *result;
|
struct gaih_addrtuple *array = res->at;
|
||||||
size_t old = 0;
|
size_t old = 0;
|
||||||
|
|
||||||
while (array != NULL)
|
while (array != NULL)
|
||||||
@ -224,12 +229,14 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|||||||
array = array->next;
|
array = array->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
array = realloc (*result, (old + count) * sizeof (*array));
|
array = realloc (res->at, (old + count) * sizeof (*array));
|
||||||
|
|
||||||
if (array == NULL)
|
if (array == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*result = array;
|
res->got_ipv6 = family == AF_INET6;
|
||||||
|
res->at = array;
|
||||||
|
res->free_at = true;
|
||||||
|
|
||||||
/* Update the next pointers on reallocation. */
|
/* Update the next pointers on reallocation. */
|
||||||
for (size_t i = 0; i < old; i++)
|
for (size_t i = 0; i < old; i++)
|
||||||
@ -278,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|||||||
{ \
|
{ \
|
||||||
__resolv_context_put (res_ctx); \
|
__resolv_context_put (res_ctx); \
|
||||||
result = -EAI_MEMORY; \
|
result = -EAI_MEMORY; \
|
||||||
goto free_and_return; \
|
goto out; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
if (status == NSS_STATUS_NOTFOUND \
|
if (status == NSS_STATUS_NOTFOUND \
|
||||||
@ -288,7 +295,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|||||||
{ \
|
{ \
|
||||||
__resolv_context_put (res_ctx); \
|
__resolv_context_put (res_ctx); \
|
||||||
result = -EAI_SYSTEM; \
|
result = -EAI_SYSTEM; \
|
||||||
goto free_and_return; \
|
goto out; \
|
||||||
} \
|
} \
|
||||||
if (h_errno == TRY_AGAIN) \
|
if (h_errno == TRY_AGAIN) \
|
||||||
no_data = EAI_AGAIN; \
|
no_data = EAI_AGAIN; \
|
||||||
@ -297,27 +304,24 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
|||||||
} \
|
} \
|
||||||
else if (status == NSS_STATUS_SUCCESS) \
|
else if (status == NSS_STATUS_SUCCESS) \
|
||||||
{ \
|
{ \
|
||||||
if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem)) \
|
if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res)) \
|
||||||
{ \
|
{ \
|
||||||
__resolv_context_put (res_ctx); \
|
__resolv_context_put (res_ctx); \
|
||||||
result = -EAI_SYSTEM; \
|
result = -EAI_SYSTEM; \
|
||||||
goto free_and_return; \
|
goto out; \
|
||||||
} \
|
} \
|
||||||
*pat = addrmem; \
|
|
||||||
\
|
\
|
||||||
if (localcanon != NULL && res.canon == NULL) \
|
if (localcanon != NULL && res->canon == NULL) \
|
||||||
{ \
|
{ \
|
||||||
char *canonbuf = __strdup (localcanon); \
|
char *canonbuf = __strdup (localcanon); \
|
||||||
if (canonbuf == NULL) \
|
if (canonbuf == NULL) \
|
||||||
{ \
|
{ \
|
||||||
__resolv_context_put (res_ctx); \
|
__resolv_context_put (res_ctx); \
|
||||||
result = -EAI_SYSTEM; \
|
result = -EAI_SYSTEM; \
|
||||||
goto free_and_return; \
|
goto out; \
|
||||||
} \
|
} \
|
||||||
res.canon = canonbuf; \
|
res->canon = canonbuf; \
|
||||||
} \
|
} \
|
||||||
if (_family == AF_INET6 && *pat != NULL) \
|
|
||||||
res.got_ipv6 = true; \
|
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,6 +594,260 @@ out:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_nss_addresses (const char *name, const struct addrinfo *req,
|
||||||
|
struct scratch_buffer *tmpbuf, struct gaih_result *res)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
/* Always start afresh; continue should discard previous results
|
||||||
|
and the hosts database does not support merge. */
|
||||||
|
gaih_result_reset (res);
|
||||||
|
|
||||||
|
if (do_merge)
|
||||||
|
{
|
||||||
|
__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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scratch_buffer_grow (tmpbuf))
|
||||||
|
{
|
||||||
|
__resolv_context_put (res_ctx);
|
||||||
|
result = -EAI_MEMORY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == NSS_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
assert (!no_data);
|
||||||
|
no_data = 1;
|
||||||
|
|
||||||
|
if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL)
|
||||||
|
{
|
||||||
|
char *canonbuf = __strdup (res->at->name);
|
||||||
|
if (canonbuf == NULL)
|
||||||
|
{
|
||||||
|
__resolv_context_put (res_ctx);
|
||||||
|
result = -EAI_MEMORY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
res->canon = canonbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gaih_addrtuple **pat = &res->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)
|
||||||
|
res->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)
|
||||||
|
{
|
||||||
|
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) || !res->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
|
||||||
|
&& res->canon == NULL)
|
||||||
|
{
|
||||||
|
char *canonbuf = getcanonname (nip, res->at, name);
|
||||||
|
if (canonbuf == NULL)
|
||||||
|
{
|
||||||
|
__resolv_context_put (res_ctx);
|
||||||
|
result = -EAI_MEMORY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
res->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
__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 out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (result != 0)
|
||||||
|
gaih_result_reset (res);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert numeric addresses to binary into RES. On failure, RES->AT is set to
|
/* 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
|
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
|
the function cannot determine a result, RES->AT is set to NULL and 0
|
||||||
@ -723,7 +981,7 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req,
|
|||||||
/* We found data, convert it. RES->AT from the conversion will
|
/* We found data, convert it. RES->AT from the conversion will
|
||||||
either be an allocated block or NULL, both of which are safe to
|
either be an allocated block or NULL, both of which are safe to
|
||||||
pass to free (). */
|
pass to free (). */
|
||||||
if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at))
|
if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res))
|
||||||
return -EAI_MEMORY;
|
return -EAI_MEMORY;
|
||||||
|
|
||||||
res->free_at = true;
|
res->free_at = true;
|
||||||
@ -801,264 +1059,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|||||||
goto process_list;
|
goto process_list;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int no_data = 0;
|
if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0)
|
||||||
int no_inet6_data = 0;
|
goto free_and_return;
|
||||||
nss_action_list nip;
|
else if (res.at != NULL)
|
||||||
enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
|
goto process_list;
|
||||||
enum nss_status status = NSS_STATUS_UNAVAIL;
|
|
||||||
int no_more;
|
|
||||||
struct resolv_context *res_ctx = NULL;
|
|
||||||
bool do_merge = false;
|
|
||||||
|
|
||||||
no_more = !__nss_database_get (nss_database_hosts, &nip);
|
/* None of the lookups worked, so name not found. */
|
||||||
|
result = -EAI_NONAME;
|
||||||
/* If we are looking for both IPv4 and IPv6 address we don't
|
goto free_and_return;
|
||||||
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)
|
|
||||||
{
|
|
||||||
__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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scratch_buffer_grow (tmpbuf))
|
|
||||||
{
|
|
||||||
__resolv_context_put (res_ctx);
|
|
||||||
result = -EAI_MEMORY;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == NSS_STATUS_SUCCESS)
|
|
||||||
{
|
|
||||||
assert (!no_data);
|
|
||||||
no_data = 1;
|
|
||||||
|
|
||||||
if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL)
|
|
||||||
{
|
|
||||||
char *canonbuf = __strdup (res.at->name);
|
|
||||||
if (canonbuf == NULL)
|
|
||||||
{
|
|
||||||
__resolv_context_put (res_ctx);
|
|
||||||
result = -EAI_MEMORY;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
res.canon = canonbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gaih_addrtuple **pat = &res.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)
|
|
||||||
res.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 = &res.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) || !res.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
|
|
||||||
&& res.canon == NULL)
|
|
||||||
{
|
|
||||||
char *canonbuf = getcanonname (nip, res.at, name);
|
|
||||||
if (canonbuf == NULL)
|
|
||||||
{
|
|
||||||
__resolv_context_put (res_ctx);
|
|
||||||
result = -EAI_MEMORY;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
res.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
__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 (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
process_list:
|
|
||||||
if (res.at == NULL)
|
|
||||||
{
|
|
||||||
result = -EAI_NONAME;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1089,6 +1097,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_list:
|
||||||
{
|
{
|
||||||
/* Set up the canonical name if we need it. */
|
/* Set up the canonical name if we need it. */
|
||||||
if ((result = process_canonname (req, orig_name, &res)) != 0)
|
if ((result = process_canonname (req, orig_name, &res)) != 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user