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 26dea46119)
This commit is contained in:
Siddhesh Poyarekar 2022-03-07 14:08:51 +05:30
parent e05e5889b8
commit 922f2614d6

View File

@ -116,6 +116,12 @@ struct gaih_typeproto
char name[8]; char name[8];
}; };
struct gaih_result
{
struct gaih_addrtuple *at;
char *canon;
};
/* Values for `protoflag'. */ /* Values for `protoflag'. */
#define GAI_PROTO_NOSERVICE 1 #define GAI_PROTO_NOSERVICE 1
#define GAI_PROTO_PROTOANY 2 #define GAI_PROTO_PROTOANY 2
@ -297,7 +303,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
} \ } \
*pat = addrmem; \ *pat = addrmem; \
\ \
if (localcanon != NULL && canon == NULL) \ if (localcanon != NULL && res.canon == NULL) \
{ \ { \
char *canonbuf = __strdup (localcanon); \ char *canonbuf = __strdup (localcanon); \
if (canonbuf == NULL) \ if (canonbuf == NULL) \
@ -306,7 +312,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
result = -EAI_SYSTEM; \ result = -EAI_SYSTEM; \
goto free_and_return; \ goto free_and_return; \
} \ } \
canon = canonbuf; \ res.canon = canonbuf; \
} \ } \
if (_family == AF_INET6 && *pat != NULL) \ if (_family == AF_INET6 && *pat != NULL) \
got_ipv6 = true; \ got_ipv6 = true; \
@ -342,9 +348,9 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
static int static int
process_canonname (const struct addrinfo *req, const char *orig_name, 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) if ((req->ai_flags & AI_CANONNAME) != 0)
{ {
@ -368,7 +374,7 @@ process_canonname (const struct addrinfo *req, const char *orig_name,
return -EAI_MEMORY; return -EAI_MEMORY;
} }
*canonp = canon; res->canon = canon;
return 0; return 0;
} }
@ -460,6 +466,105 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
return 0; 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 static int
gaih_inet (const char *name, const struct gaih_service *service, gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai, 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) struct gaih_servtuple st[sizeof (gaih_inet_typeproto)
/ sizeof (struct gaih_typeproto)] = {0}; / sizeof (struct gaih_typeproto)] = {0};
struct gaih_addrtuple *at = NULL;
bool got_ipv6 = false; bool got_ipv6 = false;
char *canon = NULL;
const char *orig_name = name; const char *orig_name = name;
/* Reserve stack memory for the scratch buffer in the getaddrinfo /* 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; struct gaih_addrtuple *addrmem = NULL;
int result = 0; int result = 0;
struct gaih_result res = {0};
if (name != NULL) if (name != NULL)
{ {
if (req->ai_flags & AI_IDN) if (req->ai_flags & AI_IDN)
@ -497,106 +601,15 @@ gaih_inet (const char *name, const struct gaih_service *service,
malloc_name = true; malloc_name = true;
} }
uint32_t addr[4]; res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used);
if (__inet_aton_exact (name, (struct in_addr *) addr) != 0) res.at->scopeid = 0;
{ res.at->next = NULL;
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) if ((result = text_to_binary_address (name, req, &res)) != 0)
{
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; goto free_and_return;
} else if (res.at != NULL)
if (req->ai_flags & AI_CANONNAME)
{
char *canonbuf = __strdup (name);
if (canonbuf == NULL)
{
result = -EAI_MEMORY;
goto free_and_return;
}
canon = canonbuf;
}
goto process_list; 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))
{
at->addr[0] = addr[3];
at->addr[1] = addr[1];
at->addr[2] = addr[2];
at->addr[3] = addr[3];
at->family = AF_INET;
}
else
{
result = -EAI_ADDRFAMILY;
goto free_and_return;
}
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)
{
char *canonbuf = __strdup (name);
if (canonbuf == 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_data = 0;
int no_inet6_data = 0; int no_inet6_data = 0;
nss_action_list nip; nss_action_list nip;
@ -641,7 +654,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
result = -EAI_MEMORY; result = -EAI_MEMORY;
goto free_and_return; goto free_and_return;
} }
at = addrmem; res.at = addrmem;
} }
else else
{ {
@ -694,7 +707,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
} }
struct gaih_addrtuple *addrfree = addrmem; struct gaih_addrtuple *addrfree = addrmem;
struct gaih_addrtuple **pat = &at; struct gaih_addrtuple **pat = &res.at;
for (int i = 0; i < air->naddrs; ++i) for (int i = 0; i < air->naddrs; ++i)
{ {
@ -721,7 +734,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
(*pat)->next = NULL; (*pat)->next = NULL;
if (added_canon || air->canon == NULL) if (added_canon || air->canon == NULL)
(*pat)->name = NULL; (*pat)->name = NULL;
else if (canon == NULL) else if (res.canon == NULL)
{ {
char *canonbuf = __strdup (air->canon); char *canonbuf = __strdup (air->canon);
if (canonbuf == NULL) if (canonbuf == NULL)
@ -729,7 +742,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
result = -EAI_MEMORY; result = -EAI_MEMORY;
goto free_and_return; goto free_and_return;
} }
canon = (*pat)->name = canonbuf; res.canon = (*pat)->name = canonbuf;
} }
if (air->family[i] == AF_INET if (air->family[i] == AF_INET
@ -792,10 +805,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
{ {
/* Always start afresh; continue should discard previous results /* Always start afresh; continue should discard previous results
and the hosts database does not support merge. */ and the hosts database does not support merge. */
at = NULL; res.at = NULL;
free (canon); free (res.canon);
free (addrmem); free (addrmem);
canon = NULL; res.canon = NULL;
addrmem = NULL; addrmem = NULL;
got_ipv6 = false; got_ipv6 = false;
@ -818,14 +831,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
{ {
while (1) while (1)
{ {
status = DL_CALL_FCT (fct4, (name, &at, status = DL_CALL_FCT (fct4, (name, &res.at,
tmpbuf->data, tmpbuf->length, tmpbuf->data, tmpbuf->length,
&errno, &h_errno, &errno, &h_errno,
NULL)); NULL));
if (status == NSS_STATUS_SUCCESS) if (status == NSS_STATUS_SUCCESS)
break; break;
/* gethostbyname4_r may write into AT, so reset it. */ /* gethostbyname4_r may write into AT, so reset it. */
at = NULL; res.at = NULL;
if (status != NSS_STATUS_TRYAGAIN if (status != NSS_STATUS_TRYAGAIN
|| errno != ERANGE || h_errno != NETDB_INTERNAL) || errno != ERANGE || h_errno != NETDB_INTERNAL)
{ {
@ -849,19 +862,19 @@ gaih_inet (const char *name, const struct gaih_service *service,
assert (!no_data); assert (!no_data);
no_data = 1; 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 = __strdup (res.at->name);
if (canonbuf == NULL) if (canonbuf == NULL)
{ {
__resolv_context_put (res_ctx); __resolv_context_put (res_ctx);
result = -EAI_MEMORY; result = -EAI_MEMORY;
goto free_and_return; goto free_and_return;
} }
canon = canonbuf; res.canon = canonbuf;
} }
struct gaih_addrtuple **pat = &at; struct gaih_addrtuple **pat = &res.at;
while (*pat != NULL) while (*pat != NULL)
{ {
@ -913,7 +926,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (fct != NULL) if (fct != NULL)
{ {
struct gaih_addrtuple **pat = &at; struct gaih_addrtuple **pat = &res.at;
if (req->ai_family == AF_INET6 if (req->ai_family == AF_INET6
|| req->ai_family == AF_UNSPEC) || req->ai_family == AF_UNSPEC)
@ -945,16 +958,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
|| status == NSS_STATUS_SUCCESS) || status == NSS_STATUS_SUCCESS)
{ {
if ((req->ai_flags & AI_CANONNAME) != 0 if ((req->ai_flags & AI_CANONNAME) != 0
&& canon == NULL) && res.canon == NULL)
{ {
char *canonbuf = getcanonname (nip, at, name); char *canonbuf = getcanonname (nip, res.at, name);
if (canonbuf == NULL) if (canonbuf == NULL)
{ {
__resolv_context_put (res_ctx); __resolv_context_put (res_ctx);
result = -EAI_MEMORY; result = -EAI_MEMORY;
goto free_and_return; goto free_and_return;
} }
canon = canonbuf; res.canon = canonbuf;
} }
status = NSS_STATUS_SUCCESS; status = NSS_STATUS_SUCCESS;
} }
@ -1020,10 +1033,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
goto free_and_return; goto free_and_return;
} }
}
process_list: process_list:
if (at == NULL) if (res.at == NULL)
{ {
result = -EAI_NONAME; result = -EAI_NONAME;
goto free_and_return; goto free_and_return;
@ -1032,21 +1044,22 @@ gaih_inet (const char *name, const struct gaih_service *service,
else else
{ {
struct gaih_addrtuple *atr; struct gaih_addrtuple *atr;
atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); atr = res.at = alloca_account (sizeof (struct gaih_addrtuple),
memset (at, '\0', sizeof (struct gaih_addrtuple)); alloca_used);
memset (res.at, '\0', sizeof (struct gaih_addrtuple));
if (req->ai_family == AF_UNSPEC) if (req->ai_family == AF_UNSPEC)
{ {
at->next = __alloca (sizeof (struct gaih_addrtuple)); res.at->next = __alloca (sizeof (struct gaih_addrtuple));
memset (at->next, '\0', sizeof (struct gaih_addrtuple)); memset (res.at->next, '\0', sizeof (struct gaih_addrtuple));
} }
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) 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) if ((req->ai_flags & AI_PASSIVE) == 0)
memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr)); memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr));
atr = at->next; atr = res.at->next;
} }
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) 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. */ /* 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; goto free_and_return;
struct gaih_addrtuple *at2 = at; struct gaih_addrtuple *at2 = res.at;
size_t socklen; size_t socklen;
sa_family_t family; sa_family_t family;
@ -1105,8 +1118,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
ai->ai_addr = (void *) (ai + 1); ai->ai_addr = (void *) (ai + 1);
/* We only add the canonical name once. */ /* We only add the canonical name once. */
ai->ai_canonname = (char *) canon; ai->ai_canonname = res.canon;
canon = NULL; res.canon = NULL;
#ifdef _HAVE_SA_LEN #ifdef _HAVE_SA_LEN
ai->ai_addr->sa_len = socklen; ai->ai_addr->sa_len = socklen;
@ -1152,7 +1165,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (malloc_name) if (malloc_name)
free ((char *) name); free ((char *) name);
free (addrmem); free (addrmem);
free (canon); free (res.canon);
return result; return result;
} }