diff options
Diffstat (limited to 'sysdeps/posix/getaddrinfo.c')
-rw-r--r-- | sysdeps/posix/getaddrinfo.c | 189 |
1 files changed, 116 insertions, 73 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index 9a27efdf83..3de83e3948 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <errno.h> #include <ifaddrs.h> #include <netdb.h> +#include <nss.h> #include <resolv.h> #include <stdbool.h> #include <stdio.h> @@ -91,14 +92,6 @@ struct gaih_servtuple static const struct gaih_servtuple nullserv; -struct gaih_addrtuple - { - struct gaih_addrtuple *next; - char *name; - int family; - uint32_t addr[4]; - uint32_t scopeid; - }; struct gaih_typeproto { @@ -202,6 +195,7 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, if (herrno == NETDB_INTERNAL) \ { \ __set_h_errno (herrno); \ + _res.options = old_res_options; \ return -EAI_SYSTEM; \ } \ if (herrno == TRY_AGAIN) \ @@ -246,6 +240,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, } +typedef enum nss_status (*nss_gethostbyname4_r) + (const char *name, struct gaih_addrtuple **pat, + char *buffer, size_t buflen, int *errnop, + int *h_errnop, int32_t *ttlp); typedef enum nss_status (*nss_gethostbyname3_r) (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, @@ -685,87 +683,132 @@ gaih_inet (const char *name, const struct gaih_service *service, while (!no_more) { - 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) + nss_gethostbyname4_r fct4 + = __nss_lookup_function (nip, "gethostbyname4_r"); + if (fct4 != NULL) { - if (req->ai_family == AF_INET6 - || req->ai_family == AF_UNSPEC) + int herrno; + + while (1) { - gethosts (AF_INET6, struct in6_addr); - no_inet6_data = no_data; - inet6_status = status; + rc = 0; + status = DL_CALL_FCT (fct4, (name, pat, tmpbuf, + tmpbuflen, &rc, &herrno, + NULL)); + if (status != NSS_STATUS_TRYAGAIN + || rc != ERANGE || herrno != NETDB_INTERNAL) + { + if (herrno == NETDB_INTERNAL) + { + __set_h_errno (herrno); + _res.options = old_res_options; + return -EAI_SYSTEM; + } + if (herrno == TRY_AGAIN) + no_data = EAI_AGAIN; + else + no_data = herrno == NO_DATA; + break; + } + tmpbuf = extend_alloca (tmpbuf, + tmpbuflen, 2 * tmpbuflen); } - 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))) + + if (status == NSS_STATUS_SUCCESS) { - gethosts (AF_INET, struct in_addr); + canon = (*pat)->name; - if (req->ai_family == AF_INET) + while (*pat != NULL) + pat = &((*pat)->next); + } + } + 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, struct in6_addr); 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) + 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))) { - /* If we need the canonical name, get it - from the same service as the result. */ - nss_getcanonname_r cfct; - int herrno; + gethosts (AF_INET, struct in_addr); - cfct = __nss_lookup_function (nip, "getcanonname_r"); - if (cfct != NULL) + if (req->ai_family == AF_INET) { - const size_t max_fqdn_len = 256; - char *buf = alloca (max_fqdn_len); - char *s; - - if (DL_CALL_FCT (cfct, (at->name ?: name, buf, - max_fqdn_len, &s, &rc, - &herrno)) - == NSS_STATUS_SUCCESS) - canon = s; - else - /* Set to name now to avoid using - gethostbyaddr. */ - canon = name; + no_inet6_data = no_data; + inet6_status = status; } } - break; - } + /* 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) + { + /* If we need the canonical name, get it + from the same service as the result. */ + nss_getcanonname_r cfct; + int herrno; + + cfct = __nss_lookup_function (nip, + "getcanonname_r"); + if (cfct != NULL) + { + const size_t max_fqdn_len = 256; + char *buf = alloca (max_fqdn_len); + char *s; + + if (DL_CALL_FCT (cfct, (at->name ?: name, + buf, max_fqdn_len, + &s, &rc, &herrno)) + == NSS_STATUS_SUCCESS) + canon = s; + else + /* Set to name now to avoid using + gethostbyaddr. */ + canon = name; + } + } - /* 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; + break; + } + + /* 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; + } } if (nss_next_action (nip, status) == NSS_ACTION_RETURN) |