From 1eb946b93509b94db2bddce741f2f3b483418a6d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 10 May 2008 23:27:39 +0000 Subject: * include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend prototypes. * include/arpa/nameser_compat.h: Define T_UNSPEC. * nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r. (libnss_nisplus): Export _nss_nisplus_gethostbyname4_r. * nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle af==AF_UNSPEC. (_nss_nis_gethostbyname4_r): New function. * nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent): Change to also handle af==AF_UNSPEC. (get_tablename): New function. Use it to avoid duplication. (_nss_nisplus_gethostbyname4_r): New function. * nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is available. * nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r. * nss/nss.h: Define struct gaih_addrtuple. * nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle af==AF_UNSPEC. (_nss_files_gethostbyname4_r): New function. * resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r. * resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery calls. * resolv/res_query.c (__libc_res_nquery): Take two additional parameters for second answer buffer. Handle type=T_UNSPEC to mean look up IPv4 and IPv6. Change all callers. * resolv/res_send.c (__libc_res_nsend): Take five aditional parameters for an additional query and answer buffer. Pass to send_vc and send_dg. (send_vc): Send possibly two requests and receive two answers. (send_dg): Likewise. * resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and __libc_res_nquery. (_nss_dns_gethostbyname4_r): New function. (gaih_getanswer_slice): Likewise. (gaih_getanswer): Likewise. * resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust __libc_res_nquery call. * resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise. (_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call. * sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is available. --- resolv/nss_dns/dns-host.c | 337 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 328 insertions(+), 9 deletions(-) (limited to 'resolv/nss_dns/dns-host.c') diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c index dfdf9c5dcb..c52f9f7f84 100644 --- a/resolv/nss_dns/dns-host.c +++ b/resolv/nss_dns/dns-host.c @@ -71,6 +71,7 @@ * --Copyright-- */ +#include #include #include #include @@ -127,6 +128,14 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen, size_t buflen, int *errnop, int *h_errnop, int map, int32_t *ttlp, char **canonp); +static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1, + const querybuf *answer2, int anslen2, + const char *qname, + struct gaih_addrtuple **pat, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); + extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result, char *buffer, size_t buflen, @@ -186,11 +195,11 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result, host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf, - 1024, &host_buffer.ptr); + 1024, &host_buffer.ptr, NULL, NULL); if (n < 0) { - enum nss_status status = (errno == ECONNREFUSED - ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); + status = (errno == ECONNREFUSED + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); *h_errnop = h_errno; if (h_errno == TRY_AGAIN) *errnop = EAGAIN; @@ -203,7 +212,8 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result, if (af == AF_INET6 && (_res.options & RES_USE_INET6)) n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf, host_buffer.buf != orig_host_buffer - ? MAXPACKET : 1024, &host_buffer.ptr); + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL); if (n < 0) { @@ -255,6 +265,70 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result, } +enum nss_status +_nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp) +{ + if (__res_maybe_init (&_res, 0) == -1) + return NSS_STATUS_UNAVAIL; + + char tmp[NS_MAXDNAME]; + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_query() since we are not the only + * function that looks up host names. + */ + if (strchr (name, '.') == NULL) + { + const char *cp = res_hostalias (&_res, name, tmp, sizeof (tmp)); + if (cp != NULL) + name = cp; + } + + union + { + querybuf *buf; + u_char *ptr; + } host_buffer; + querybuf *orig_host_buffer; + host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048); + u_char *ans2p = NULL; + int nans2p = 0; + + int olderr = errno; + enum nss_status status; + int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC, + host_buffer.buf->buf, 2048, &host_buffer.ptr, + &ans2p, &nans2p); + if (n < 0) + { + status = (errno == ECONNREFUSED + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); + *herrnop = h_errno; + if (h_errno == TRY_AGAIN) + *errnop = EAGAIN; + else + __set_errno (olderr); + + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + + return status; + } + + status = gaih_getanswer(host_buffer.buf, n, (const querybuf *) ans2p, + nans2p, name, pat, buffer, buflen, + errnop, herrnop, ttlp); + + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + + return status; +} + + extern enum nss_status _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, struct hostent *result, @@ -342,7 +416,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, qp += sprintf (qp, "%02hhx", uaddr[n]); strcpy (qp, "].ip6.arpa"); n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, - host_buffer.buf->buf, 1024, &host_buffer.ptr); + host_buffer.buf->buf, 1024, &host_buffer.ptr, + NULL, NULL); if (n >= 0) goto got_it_already; } @@ -363,13 +438,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, } n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, - 1024, &host_buffer.ptr); + 1024, &host_buffer.ptr, NULL, NULL); if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) { strcpy (qp, "ip6.int"); n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, host_buffer.buf != orig_host_buffer - ? MAXPACKET : 1024, &host_buffer.ptr); + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL); } if (n < 0) { @@ -555,7 +631,8 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, if (n > 0 && bp[0] == '.') bp[0] = '\0'; - if (n < 0 || (*name_ok) (bp) == 0) + if (__builtin_expect (n < 0 || ((*name_ok) (bp) == 0 && (errno = EBADMSG)), + 0)) { *errnop = errno; *h_errnop = NO_RECOVERY; @@ -629,7 +706,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, continue; /* XXX - had_error++ ? */ } - if ((qtype ==T_A || qtype == T_AAAA) && type == T_CNAME) + if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1]) continue; @@ -857,3 +934,245 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN); } + + +static enum nss_status +gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, + struct gaih_addrtuple ***patp, + char **bufferp, size_t *buflenp, + int *errnop, int *h_errnop, int32_t *ttlp, int *firstp) +{ + char *buffer = *bufferp; + size_t buflen = *buflenp; + + struct gaih_addrtuple **pat = *patp; + const HEADER *hp = &answer->hdr; + int ancount = ntohs (hp->ancount); + int qdcount = ntohs (hp->qdcount); + const u_char *cp = answer->buf + HFIXEDSZ; + const u_char *end_of_message = answer->buf + anslen; + if (__builtin_expect (qdcount != 1, 0)) + { + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + + u_char packtmp[NS_MAXCDNAME]; + int n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + /* We unpack the name to check it for validity. But we do not need + it later. */ + if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1) + { + if (__builtin_expect (errno, 0) == EMSGSIZE) + { + too_small: + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + n = -1; + } + + if (__builtin_expect (n < 0 || (res_hnok (buffer) == 0 + && (errno = EBADMSG)), 0)) + { + *errnop = errno; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + cp += n + QFIXEDSZ; + + int haveanswer = 0; + int had_error = 0; + char *canon = NULL; + char *h_name = NULL; + int h_namelen = 0; + + while (ancount-- > 0 && cp < end_of_message && had_error == 0) + { + n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && + (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1) + { + if (__builtin_expect (errno, 0) == EMSGSIZE) + goto too_small; + + n = -1; + } + if (n < 0 || res_hnok (buffer) == 0) + { + ++had_error; + continue; + } + if (*firstp) + { + h_name = buffer; + buffer += h_namelen; + buflen -= h_namelen; + } + + cp += n; /* name */ + int type = ns_get16 (cp); + cp += INT16SZ; /* type */ + int class = ns_get16 (cp); + cp += INT16SZ; /* class */ + int32_t ttl = ns_get32 (cp); + cp += INT32SZ; /* TTL */ + n = ns_get16 (cp); + cp += INT16SZ; /* len */ + + if (class != C_IN) + { + cp += n; + continue; + } + + if (type == T_CNAME) + { + char tbuf[MAXDNAME]; + n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf); + if (n < 0 || res_hnok (tbuf) == 0) + { + ++had_error; + continue; + } + cp += n; + + if (*firstp) + { + /* Reclaim buffer space. */ + if (h_name + h_namelen == buffer) + { + buffer = h_name; + buflen += h_namelen; + } + + n = strlen (tbuf) + 1; + if (__builtin_expect (n > buflen, 0)) + goto too_small; + if (__builtin_expect (n >= MAXHOSTNAMELEN, 0)) + { + ++had_error; + continue; + } + + canon = buffer; + buffer = __mempcpy (buffer, tbuf, n); + buflen -= n; + h_namelen = 0; + } + continue; + } + if (__builtin_expect (type == T_SIG, 0) + || __builtin_expect (type == T_KEY, 0) + || __builtin_expect (type == T_NXT, 0) + || __builtin_expect (type == T_PTR, 0)) + { + /* We don't support DNSSEC yet. For now, ignore the record + and send a low priority message to syslog. + + We also don't expect T_PTR messages. */ + syslog (LOG_DEBUG | LOG_AUTH, + "getaddrinfo*.gaih_getanswer: got type \"%s\"", + p_type (type)); + cp += n; + continue; + } + if (type != T_A && type != T_AAAA) + abort (); + + if (*pat == NULL) + { + uintptr_t pad = (-(uintptr_t) buffer + % __alignof__ (struct gaih_addrtuple)); + buffer += pad; + buflen = buflen > pad ? buflen - pad : 0; + + if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), + 0)) + { + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + *pat = (struct gaih_addrtuple *) buffer; + buffer += sizeof (struct gaih_addrtuple); + buflen -= sizeof (struct gaih_addrtuple); + } + + (*pat)->name = NULL; + (*pat)->next = NULL; + + if (*firstp) + { + if (ttl != 0 && ttlp != NULL) + *ttlp = ttl; + + if (canon != NULL) + { + (*pat)->name = canon; + + /* Reclaim buffer space. */ + if (h_name + h_namelen == buffer) + { + buffer = h_name; + buflen += h_namelen; + } + } + else + (*pat)->name = h_name; + + *firstp = 0; + } + + (*pat)->family = type == T_A ? AF_INET : AF_INET6; + memcpy ((*pat)->addr, cp, n); + cp += n; + (*pat)->scopeid = 0; + + pat = &((*pat)->next); + + haveanswer = 1; + } + + if (haveanswer) + { + *patp = pat; + *bufferp = buffer; + *buflenp = buflen; + + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + } + + /* Special case here: if the resolver sent a result but it only + contains a CNAME while we are looking for a T_A or T_AAAA record, + we fail with NOTFOUND instead of TRYAGAIN. */ + return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; +} + + +static enum nss_status +gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2, + int anslen2, const char *qname, + struct gaih_addrtuple **pat, char *buffer, size_t buflen, + int *errnop, int *h_errnop, int32_t *ttlp) +{ + int first = 1; + + enum nss_status status = gaih_getanswer_slice(answer1, anslen1, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, + &first); + if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND) + && answer2 != NULL) + status = gaih_getanswer_slice(answer2, anslen2, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, &first); + + return status; +} -- cgit 1.4.1