diff options
| author | Siddhesh Poyarekar <siddhesh@sourceware.org> | 2022-03-07 14:08:51 +0530 |
|---|---|---|
| committer | Siddhesh Poyarekar <siddhesh@sourceware.org> | 2022-03-22 19:39:17 +0530 |
| commit | 26dea461191cca519b498890a9682fe4bc8e4c2f (patch) | |
| tree | 2560fa5912b4313e1fd052a73ce26e0f85138418 | |
| parent | 8d6cf99f2fb81a097f9334c125e5c23604af1a98 (diff) | |
| download | glibc-26dea461191cca519b498890a9682fe4bc8e4c2f.tar.xz glibc-26dea461191cca519b498890a9682fe4bc8e4c2f.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>
| -rw-r--r-- | sysdeps/posix/getaddrinfo.c | 891 |
1 files changed, 452 insertions, 439 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index dae5e9f55f..19bb13db59 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 |
