diff options
| author | Robert Mustacchi <rm@joyent.com> | 2014-02-27 14:38:40 -0800 |
|---|---|---|
| committer | Robert Mustacchi <rm@joyent.com> | 2014-12-10 15:24:01 -0800 |
| commit | e48cae6f8c603e9a18cdb49fdf939cd4e1753e62 (patch) | |
| tree | 7fd3a9c9252b44b5268cfcc64285f53a34f4a0bc /usr/src/lib/nsswitch/dns | |
| parent | d65686849024838243515b5c40ae2c479460b4b5 (diff) | |
| download | illumos-joyent-e48cae6f8c603e9a18cdb49fdf939cd4e1753e62.tar.gz | |
4643 nss_dns fails to obtain dns ttls
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Gordon Ross <gwr@nexenta.com>
Diffstat (limited to 'usr/src/lib/nsswitch/dns')
| -rw-r--r-- | usr/src/lib/nsswitch/dns/Makefile.com | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/dns_common.c | 218 |
2 files changed, 199 insertions, 23 deletions
diff --git a/usr/src/lib/nsswitch/dns/Makefile.com b/usr/src/lib/nsswitch/dns/Makefile.com index 23c89c32f8..60716d843d 100644 --- a/usr/src/lib/nsswitch/dns/Makefile.com +++ b/usr/src/lib/nsswitch/dns/Makefile.com @@ -22,8 +22,6 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # lib/nsswitch/dns/Makefile.com LIBRARY = libnss_dns.a @@ -46,5 +44,5 @@ CPPFLAGS += -DNSS_DNS_LIBRESOLV=\"libresolv.so.2\" LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2 -LDLIBS += -lnsl +LDLIBS += -lnsl -lsocket DYNLIB1 = nss_dns.so$(VERS) diff --git a/usr/src/lib/nsswitch/dns/common/dns_common.c b/usr/src/lib/nsswitch/dns/common/dns_common.c index a9195f9f68..7a267fe60c 100644 --- a/usr/src/lib/nsswitch/dns/common/dns_common.c +++ b/usr/src/lib/nsswitch/dns/common/dns_common.c @@ -22,12 +22,19 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * dns_common.c */ #include "dns_common.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <ifaddrs.h> +#include <net/if.h> #pragma weak dn_expand #pragma weak res_ninit @@ -321,6 +328,51 @@ name_is_alias(char *aliases_ptr, char *name_ptr) { return (NSS_NOTFOUND); } +static int +_nss_has_interfaces(boolean_t *v4, boolean_t *v6) +{ + struct ifaddrs *ifp, *i; + struct in_addr in4; + struct in6_addr in6; + const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; + + *v4 = *v6 = B_FALSE; + + if (getifaddrs(&ifp) != 0) + return (-1); + + for (i = ifp; i != NULL; i = i->ifa_next) { + if (i->ifa_flags & IFF_LOOPBACK) + continue; + if ((i->ifa_flags & IFF_UP) == 0) + continue; + + if (i->ifa_addr->sa_family == AF_INET) { + if (*v4 != B_FALSE) + continue; + + if (((struct sockaddr_in *)i->ifa_addr)-> + sin_addr.s_addr == INADDR_ANY) + continue; + *v4 = B_TRUE; + } + + if (i->ifa_addr->sa_family == AF_INET6) { + if (*v6 != B_FALSE) + continue; + + if (memcmp(&in6addr_any, + &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr, + sizeof (struct in6_addr)) == 0) + continue; + *v6 = B_TRUE; + } + } + + freeifaddrs(ifp); + return (0); +} + /* * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) * nss2 get hosts/ipnodes with ttl backend DNS search engine. @@ -331,13 +383,6 @@ name_is_alias(char *aliases_ptr, char *name_ptr) { * Additionally in the extended results a nssuint_t ttl is placed. * This ttl is the lessor of the ttl's extracted from the result. * - * ***Currently the first version of this API only performs simple - * single res_nsearch lookups for with T_A or T_AAAA results. - * Other searches are deferred to the generic API w/t ttls. - * - * This function is not a generic res_* operation. It only performs - * a single T_A or T_AAAA lookups*** - * * RETURNS: NSS_SUCCESS or NSS_ERROR * If an NSS_ERROR result is returned, nscd is expected * to resubmit the gethosts request using the old style @@ -384,11 +429,14 @@ _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) int af; char *ap, *apc; int hlen = 0, alen, iplen, len, isans; + boolean_t has_v4 = B_FALSE, has_v6 = B_FALSE; + int flags, family, pass2 = 0; statp = &stat; (void) memset(statp, '\0', sizeof (struct __res_state)); - if (res_ninit(statp) == -1) + if (res_ninit(statp) == -1) { return (NSS_ERROR); + } ap = apc = (char *)aliases; alen = 0; @@ -404,33 +452,152 @@ _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) return (NSS_ERROR); } + /* + * There may be flags set when we are handling ipnode. There are three + * different values for flags: + * + * o AI_V4MAPPED + * o AI_ALL + * o AI_ADDRCONFIG + * + * The first two only have a meaning when af_family is ipv6. The latter + * means something in both cases. These flags are documented in + * getipnodebyname(3SOCKET), though the combinations leave a little + * something to be desired. It would be great if we could actually use + * getipnodebyname directly here since it already knows how to handle + * this kind of logic; however, we're not quite so lucky. Ideally we + * would add such an interface to libresolv.so.2 to handle this kind of + * thing, but that's rather painful as well. We'll summarize what has to + * happen below: + * + * AI_ALL is only meaningful when AI_V4MAPPED is also specified. Both + * are ignored if the family is not AF_INET6 + * + * family == AF_INET, flags | AI_ADDRCONFIG + * - lookup A records iff we have v4 plumbed + * family == AF_INET, !(flags | AI_ADDRCONFIG) + * - lookup A records + * family == AF_INET6, flags == 0 || flags == AI_ALL + * - lookup AAAA records + * family == AF_INET6, flags | AI_V4MAPPED + * - lookup AAAA, if none, lookup A + * family == AF_INET6, flags | AI_ADDRCONFIG + * - lookup AAAA records if ipv6 + * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ALL + * - lookup AAAA records, lookup A records + * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ADDRCONFIG + * - lookup AAAA records if ipv6 + * - If no AAAA && ipv4 exists, lookup A + * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ADDRCONFIG && + * flags | AI_ALL + * - lookup AAAA records if ipv6 + * - loookup A records if ipv4 + */ if (ipnode) { /* initially only handle the simple cases */ - if (arg.key.ipnode.flags != 0) { - res_ndestroy(statp); - return (NSS_ERROR); - } name = arg.key.ipnode.name; - if (arg.key.ipnode.af_family == AF_INET6) - qtype = T_AAAA; - else - qtype = T_A; + flags = arg.key.ipnode.flags; + family = arg.key.ipnode.af_family; + if (flags != 0) { + /* + * Figure out our first pass. We'll determine if we need + * to do a second pass afterwards once we successfully + * finish our first pass. + */ + if ((flags & AI_ADDRCONFIG) != 0) { + if (_nss_has_interfaces(&has_v4, &has_v6) != + 0) { + res_ndestroy(statp); + return (NSS_ERROR); + } + /* Impossible situations... */ + if (family == AF_INET && has_v4 == B_FALSE) { + res_ndestroy(statp); + return (NSS_NOTFOUND); + } + if (family == AF_INET6 && has_v6 == B_FALSE && + !(flags & AI_V4MAPPED)) { + res_ndestroy(statp); + return (NSS_NOTFOUND); + } + if (family == AF_INET6 && has_v6) + qtype = T_AAAA; + if (family == AF_INET || (family == AF_INET6 && + has_v6 == B_FALSE && flags & AI_V4MAPPED)) + qtype = T_A; + } else { + has_v4 = has_v6 = B_TRUE; + if (family == AF_INET6) + qtype = T_AAAA; + else + qtype = T_A; + } + } else { + if (family == AF_INET6) + qtype = T_AAAA; + else + qtype = T_A; + } } else { name = arg.key.name; qtype = T_A; } + +searchagain: ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG); if (ret == -1) { - if (statp->res_h_errno == HOST_NOT_FOUND) { + /* + * We want to continue on unless we got NO_RECOVERY. Otherwise, + * HOST_NOT_FOUND, TRY_AGAIN, and NO_DATA all suggest to me that + * we should keep going. + */ + if (statp->res_h_errno == NO_RECOVERY) { + /* else lookup error - handle in general code */ + res_ndestroy(statp); + return (NSS_ERROR); + } + + /* + * We found something on our first pass. Make sure that we do + * not clobber this information. This ultimately means that we + * were successful. + */ + if (pass2 == 2) + goto out; + + /* + * If we're on the second pass (eg. we need to check both for A + * and AAAA records), or we were only ever doing a search for + * one type of record and are not supposed to do a second pass, + * then we need to return that we couldn't find anything to the + * user. + */ + if (pass2 == 1 || flags == 0 || family == AF_INET || + (family == AF_INET6 && !(flags & AI_V4MAPPED))) { pbuf->p_herrno = HOST_NOT_FOUND; pbuf->p_status = NSS_NOTFOUND; pbuf->data_len = 0; res_ndestroy(statp); return (NSS_NOTFOUND); } - /* else lookup error - handle in general code */ - res_ndestroy(statp); - return (NSS_ERROR); + + /* + * If we were only requested to search for flags on an IPv6 + * interface or we have no IPv4 interface, we stick to only + * doing a single pass and bail now. + */ + if ((flags & AI_ADDRCONFIG) && !(flags & AI_ALL) && + has_v4 == B_FALSE) { + pbuf->p_herrno = HOST_NOT_FOUND; + pbuf->p_status = NSS_NOTFOUND; + pbuf->data_len = 0; + res_ndestroy(statp); + return (NSS_NOTFOUND); + } + qtype = T_A; + flags = 0; + pass2 = 1; + goto searchagain; } cp = resbuf.buf; @@ -579,6 +746,16 @@ _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) *bptr++ = '\n'; blen++; } + + /* Depending on our flags we may need to go back another time. */ + if (qtype == T_AAAA && family == AF_INET6 && + ((flags & AI_V4MAPPED) != 0) && ((flags & AI_ALL) != 0) && + has_v4 == B_TRUE) { + qtype = T_A; + pass2 = 2; /* Indicate that we found data this pass */ + goto searchagain; + } + /* Presumably the buffer is now filled. */ len = ROUND_UP(blen, sizeof (nssuint_t)); /* still room? */ @@ -587,6 +764,7 @@ _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) res_ndestroy(statp); return (NSS_ERROR); } +out: pbuf->ext_off = pbuf->data_off + len; pbuf->ext_len = sizeof (nssuint_t); pbuf->data_len = blen; |
