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.gz 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. */ - 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; } |