From 28977c2c1acb789660ad47e0d88e42486059c916 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 15 Aug 2004 20:23:40 +0000 Subject: Update. * sysdeps/posix/getaddrinfo.c (gaih_addrtuple): Change type of addr to avoid casts. (gethosts): Removed. (gethosts2): Renamed to gethosts. Make it usable for family != AF_UNSPEC. Fix AI_V4MAPPED. (gaih_inet): Remove use of old gethosts. Always use what used to be gethosts2. If entry is found, try to use the same NSS module's getcanonname_r function. Use gethostbyaddr for AI_CANONNAME only if getcanonname_r was not available. Fix filtering of AI_V4MAPPED addresses. Numerous cleanups. * resolv/nss_dns/dns-canon.c: New file. * resolv/Makefile (libnss_dns-routines): Add dns-canon. * resolv/Versions (libnss_dns): Add _nss_dns_getcanonname_r. * elf/Makefile: Add rules to build and run tst-dlopenrpath. * elf/tst-dlopenrpath.c: New file. * elf/tst-dlopenrpathmod.c: New file. * intl/tst-gettext.sh: Adjust for change of de.po file to UTF-8. --- sysdeps/posix/getaddrinfo.c | 404 ++++++++++++++++++-------------------------- 1 file changed, 168 insertions(+), 236 deletions(-) (limited to 'sysdeps/posix') diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index bdfdcfbcad..23b74296c1 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -89,7 +89,7 @@ struct gaih_addrtuple { struct gaih_addrtuple *next; int family; - char addr[16]; + uint32_t addr[4]; uint32_t scopeid; }; @@ -281,92 +281,6 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, } #define gethosts(_family, _type) \ - { \ - int i, herrno; \ - size_t tmpbuflen; \ - struct hostent th; \ - char *tmpbuf = NULL; \ - tmpbuflen = 512; \ - no_data = 0; \ - do { \ - tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \ - rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \ - tmpbuflen, &h, &herrno); \ - } while (rc == ERANGE && herrno == NETDB_INTERNAL); \ - if (rc != 0) \ - { \ - if (herrno == NETDB_INTERNAL) \ - { \ - __set_h_errno (herrno); \ - return -EAI_SYSTEM; \ - } \ - if (herrno == TRY_AGAIN) \ - no_data = EAI_AGAIN; \ - else \ - no_data = herrno == NO_DATA; \ - } \ - else if (h != NULL) \ - { \ - for (i = 0; h->h_addr_list[i]; i++) \ - { \ - if (*pat == NULL) { \ - *pat = __alloca (sizeof (struct gaih_addrtuple)); \ - (*pat)->scopeid = 0; \ - } \ - (*pat)->next = NULL; \ - (*pat)->family = _family; \ - memcpy ((*pat)->addr, h->h_addr_list[i], \ - sizeof(_type)); \ - pat = &((*pat)->next); \ - } \ - if (_family == AF_INET6) \ - got_ipv6 = true; \ - } \ - else if (_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) \ - { \ - /* We have to add V4 mapped addresses. Maybe we discard them \ - later again but we get them anyhow for now. */ \ - while ((rc = __gethostbyname2_r (name, AF_INET6, &th, tmpbuf, \ - tmpbuflen, &h, &herrno)) == ERANGE \ - && herrno == NETDB_INTERNAL) \ - tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \ - \ - if (rc != 0) \ - { \ - if (herrno == NETDB_INTERNAL) \ - { \ - __set_h_errno (herrno); \ - return -EAI_SYSTEM; \ - } \ - if (herrno == TRY_AGAIN) \ - no_data = EAI_AGAIN; \ - else \ - no_data = herrno == NO_DATA; \ - } \ - else if (h != NULL) \ - { \ - for (i = 0; h->h_addr_list[i]; ++i) \ - { \ - if (*pat == NULL) \ - { \ - *pat = __alloca (sizeof (struct gaih_addrtuple)); \ - (*pat)->scopeid = 0; \ - } \ - uint32_t *addr = (uint32_t *) (*pat)->addr; \ - (*pat)->next = NULL; \ - (*pat)->family = _family; \ - addr[3] = *(uint32_t *) h->h_addr_list[i]; \ - addr[2] = htonl (0xffff); \ - addr[1] = 0; \ - addr[0] = 0; \ - pat = &((*pat)->next); \ - } \ - } \ - } \ - } - - -#define gethosts2(_family, _type) \ { \ int i, herrno; \ size_t tmpbuflen; \ @@ -400,23 +314,42 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, { \ for (i = 0; h->h_addr_list[i]; i++) \ { \ - if (*pat == NULL) { \ - *pat = __alloca (sizeof (struct gaih_addrtuple)); \ - (*pat)->scopeid = 0; \ - } \ + if (*pat == NULL) \ + { \ + *pat = __alloca (sizeof (struct gaih_addrtuple)); \ + (*pat)->scopeid = 0; \ + } \ + uint32_t *addr = (*pat)->addr; \ (*pat)->next = NULL; \ - (*pat)->family = _family; \ - memcpy ((*pat)->addr, h->h_addr_list[i], \ - sizeof(_type)); \ + if (_family == AF_INET && req->ai_family == AF_INET6) \ + { \ + (*pat)->family = AF_INET6; \ + addr[3] = *(uint32_t *) h->h_addr_list[i]; \ + addr[2] = htonl (0xffff); \ + addr[1] = 0; \ + addr[0] = 0; \ + } \ + else \ + { \ + (*pat)->family = _family; \ + memcpy (addr, h->h_addr_list[i], sizeof(_type)); \ + } \ pat = &((*pat)->next); \ } \ + \ + if (_family == AF_INET6 && i > 0) \ + got_ipv6 = true; \ } \ } + typedef enum nss_status (*nss_gethostbyname2_r) (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop); +typedef enum nss_status (*nss_getcanonname_r) + (const char *name, char *buffer, size_t buflen, char **result, + int *errnop, int *h_errnop); extern service_user *__nss_hosts_database attribute_hidden; static int @@ -428,6 +361,7 @@ gaih_inet (const char *name, const struct gaih_service *service, struct gaih_addrtuple *at = NULL; int rc; bool got_ipv6 = false; + const char *canon = NULL; if (req->ai_protocol || req->ai_socktype) { @@ -581,10 +515,10 @@ gaih_inet (const char *name, const struct gaih_service *service, at->family = AF_INET; else if (req->ai_family == AF_INET6 && req->ai_flags & AI_V4MAPPED) { - ((uint32_t *) at->addr)[3] = *(uint32_t *) at->addr; - ((uint32_t *) at->addr)[2] = htonl (0xffff); - ((uint32_t *) at->addr)[1] = 0; - ((uint32_t *) at->addr)[0] = 0; + at->addr[3] = at->addr[0]; + at->addr[2] = htonl (0xffff); + at->addr[1] = 0; + at->addr[0] = 0; at->family = AF_INET6; } else @@ -607,7 +541,7 @@ gaih_inet (const char *name, const struct gaih_service *service, else if (req->ai_family == AF_INET && IN6_IS_ADDR_V4MAPPED (at->addr)) { - *(uint32_t *) at->addr = ((uint32_t *) at->addr)[3]; + at->addr[0] = at->addr[3]; at->family = AF_INET; } else @@ -645,82 +579,110 @@ gaih_inet (const char *name, const struct gaih_service *service, struct gaih_addrtuple **pat = &at; int no_data = 0; int no_inet6_data = 0; + service_user *nip = NULL; + enum nss_status inet6_status = NSS_STATUS_UNAVAIL; + enum nss_status status = NSS_STATUS_UNAVAIL; + int no_more; + nss_gethostbyname2_r fct; + int old_res_options; + + if (__nss_hosts_database != NULL) + { + no_more = 0; + nip = __nss_hosts_database; + } + else + no_more = __nss_database_lookup ("hosts", NULL, + "dns [!UNAVAIL=return] files", + &nip); + + if (__res_maybe_init (&_res, 0) == -1) + no_more = 1; + /* 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. Currently this is decided by setting the RES_USE_INET6 bit in _res.options. */ - if (req->ai_family == AF_UNSPEC) - { - service_user *nip = NULL; - enum nss_status inet6_status, status = NSS_STATUS_UNAVAIL; - int no_more; - nss_gethostbyname2_r fct; - int old_res_options; - - if (__nss_hosts_database != NULL) - { - no_more = 0; - nip = __nss_hosts_database; - } - else - no_more = __nss_database_lookup ("hosts", NULL, - "dns [!UNAVAIL=return] files", - &nip); + old_res_options = _res.options; + _res.options &= ~RES_USE_INET6; - if (__res_maybe_init (&_res, 0) == -1) - no_more = 1; - old_res_options = _res.options; - _res.options &= ~RES_USE_INET6; + while (!no_more) + { + fct = __nss_lookup_function (nip, "gethostbyname2_r"); - while (!no_more) + if (fct != NULL) { - fct = __nss_lookup_function (nip, "gethostbyname2_r"); - - if (fct != NULL) + if (req->ai_family == AF_INET6 + || req->ai_family == AF_UNSPEC) { - gethosts2 (AF_INET6, struct in6_addr); + gethosts (AF_INET6, struct in6_addr); no_inet6_data = no_data; inet6_status = status; - gethosts2 (AF_INET, struct in_addr); - - /* 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) - break; - - /* We can have different states for AF_INET - and AF_INET6. Try to find a usefull 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 (req->ai_family == AF_INET + || req->ai_family == AF_UNSPEC + || (req->ai_family == AF_INET6 + && (req->ai_flags & AI_V4MAPPED))) + { + gethosts (AF_INET, struct in_addr); - if (nss_next_action (nip, status) == NSS_ACTION_RETURN) - break; + if (req->ai_family == AF_INET) + { + no_inet6_data = no_data; + inet6_status = status; + } + } - if (nip->next == NULL) - no_more = -1; - else - nip = nip->next; + /* 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 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, (name, buf, max_fqdn_len, + &s, &rc, &herrno)) + == NSS_STATUS_SUCCESS) + canon = s; + else + /* Set to name now to avoid using + gethostbyaddr. */ + canon = name; + } + + 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; } - _res.options = old_res_options; - } - else if (req->ai_family == AF_INET6) - { - gethosts (AF_INET6, struct in6_addr); - no_inet6_data = no_data; - } - else if (req->ai_family == AF_INET) - { - gethosts (AF_INET, struct in_addr); - no_inet6_data = no_data; + if (nss_next_action (nip, status) == NSS_ACTION_RETURN) + break; + + if (nip->next == NULL) + no_more = -1; + else + nip = nip->next; } + _res.options = old_res_options; + if (no_data != 0 && no_inet6_data != 0) { /* If both requests timed out report this. */ @@ -742,13 +704,13 @@ gaih_inet (const char *name, const struct gaih_service *service, atr = at = __alloca (sizeof (struct gaih_addrtuple)); memset (at, '\0', sizeof (struct gaih_addrtuple)); - if (req->ai_family == 0) + if (req->ai_family == AF_UNSPEC) { at->next = __alloca (sizeof (struct gaih_addrtuple)); memset (at->next, '\0', sizeof (struct gaih_addrtuple)); } - if (req->ai_family == 0 || req->ai_family == AF_INET6) + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) { at->family = AF_INET6; if ((req->ai_flags & AI_PASSIVE) == 0) @@ -756,11 +718,11 @@ gaih_inet (const char *name, const struct gaih_service *service, atr = at->next; } - if (req->ai_family == 0 || req->ai_family == AF_INET) + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) { atr->family = AF_INET; if ((req->ai_flags & AI_PASSIVE) == 0) - *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK); + atr->addr[0] = htonl (INADDR_LOOPBACK); } } @@ -770,7 +732,8 @@ gaih_inet (const char *name, const struct gaih_service *service, { struct gaih_servtuple *st2; struct gaih_addrtuple *at2 = at; - size_t socklen, namelen; + size_t socklen; + size_t canonlen; sa_family_t family; /* @@ -778,77 +741,46 @@ gaih_inet (const char *name, const struct gaih_service *service, */ while (at2 != NULL) { - const char *c = NULL; - /* Only the first entry gets the canonical name. */ if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0) { - struct hostent *h = NULL; - - int herrno; - struct hostent th; - size_t tmpbuflen = 512; - char *tmpbuf = NULL; - - do + if (canon == NULL) { - tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2); - rc = __gethostbyaddr_r (at2->addr, - ((at2->family == AF_INET6) - ? sizeof(struct in6_addr) - : sizeof(struct in_addr)), - at2->family, &th, tmpbuf, tmpbuflen, - &h, &herrno); + struct hostent *h = NULL; + int herrno; + struct hostent th; + size_t tmpbuflen = 512; + char *tmpbuf = NULL; - } - while (rc == ERANGE && herrno == NETDB_INTERNAL); - - if (rc != 0 && herrno == NETDB_INTERNAL) - { - __set_h_errno (herrno); - return -EAI_SYSTEM; - } + do + { + tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2); + rc = __gethostbyaddr_r (at2->addr, + ((at2->family == AF_INET6) + ? sizeof (struct in6_addr) + : sizeof (struct in_addr)), + at2->family, &th, tmpbuf, + tmpbuflen, &h, &herrno); + } + while (rc == ERANGE && herrno == NETDB_INTERNAL); - if (h != NULL) - c = h->h_name; - else - { - /* We have to try to get the canonical in some other - way. If we are looking for either AF_INET or - AF_INET6 try the other line. */ - if (req->ai_family == AF_UNSPEC) + if (rc != 0 && herrno == NETDB_INTERNAL) { - struct addrinfo *p = NULL; - struct addrinfo **end = &p; - struct addrinfo localreq = *req; - struct addrinfo *runp; - - localreq.ai_family = AF_INET + AF_INET6 - at2->family; - (void) gaih_inet (name, service, &localreq, end); - - runp = p; - while (runp != NULL) - { - if (p->ai_canonname != name) - { - c = strdupa (p->ai_canonname); - break; - } - runp = runp->ai_next; - } - - freeaddrinfo (p); + __set_h_errno (herrno); + return -EAI_SYSTEM; } - /* If this code is used the missing canonical name is - substituted with the name passed in by the user. */ - if (c == NULL) - c = name; + if (h != NULL) + canon = h->h_name; + else + { + assert (name != NULL); + /* If the canonical name cannot be determined, use + the passed in string. */ + canon = name; + } } - if (c == NULL) - return GAIH_OKIFUNSPEC | -EAI_NONAME; - #ifdef HAVE_LIBIDN if (req->ai_flags & AI_CANONIDN) { @@ -859,7 +791,7 @@ gaih_inet (const char *name, const struct gaih_service *service, idn_flags |= IDNA_USE_STD3_ASCII_RULES; char *out; - int rc = __idna_to_unicode_lzlz (c, &out, idn_flags); + int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags); if (rc != IDNA_SUCCESS) { if (rc == IDNA_MALLOC_ERROR) @@ -870,18 +802,18 @@ gaih_inet (const char *name, const struct gaih_service *service, } /* In case the output string is the same as the input string no new string has been allocated. */ - if (out != c) + if (out != canon) { - c = strdupa (out); + canon = strdupa (out); free (out); } } #endif - namelen = strlen (c) + 1; + canonlen = strlen (canon) + 1; } else - namelen = 0; + canonlen = 0; if (at2->family == AF_INET6) { @@ -891,7 +823,7 @@ gaih_inet (const char *name, const struct gaih_service *service, /* If we looked up IPv4 mapped address discard them here if the caller isn't interested in all address and we have found at least one IPv6 address. */ - if (! got_ipv6 + if (got_ipv6 && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED && IN6_IS_ADDR_V4MAPPED (at2->addr)) goto ignore; @@ -904,7 +836,7 @@ gaih_inet (const char *name, const struct gaih_service *service, for (st2 = st; st2 != NULL; st2 = st2->next) { - *pai = malloc (sizeof (struct addrinfo) + socklen + namelen); + *pai = malloc (sizeof (struct addrinfo) + socklen + canonlen); if (*pai == NULL) return -EAI_MEMORY; @@ -913,7 +845,7 @@ gaih_inet (const char *name, const struct gaih_service *service, (*pai)->ai_socktype = st2->socktype; (*pai)->ai_protocol = st2->protocol; (*pai)->ai_addrlen = socklen; - (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo); + (*pai)->ai_addr = (void *) (*pai + 1); #if SALEN (*pai)->ai_addr->sa_len = socklen; #endif /* SALEN */ @@ -924,30 +856,30 @@ gaih_inet (const char *name, const struct gaih_service *service, struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr; + sin6p->sin6_port = st2->port; sin6p->sin6_flowinfo = 0; memcpy (&sin6p->sin6_addr, at2->addr, sizeof (struct in6_addr)); - sin6p->sin6_port = st2->port; sin6p->sin6_scope_id = at2->scopeid; } else { struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr; + sinp->sin_port = st2->port; memcpy (&sinp->sin_addr, at2->addr, sizeof (struct in_addr)); - sinp->sin_port = st2->port; memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); } - if (namelen != 0) + if (canonlen != 0) { (*pai)->ai_canonname = ((void *) (*pai) + sizeof (struct addrinfo) + socklen); - strcpy ((*pai)->ai_canonname, c); + strcpy ((*pai)->ai_canonname, canon); /* We do not need to allocate the canonical name anymore. */ - namelen = 0; + canonlen = 0; } else (*pai)->ai_canonname = NULL; -- cgit 1.4.1