diff options
Diffstat (limited to 'usr/src/lib/nsswitch/dns/common/dns_common.c')
-rw-r--r-- | usr/src/lib/nsswitch/dns/common/dns_common.c | 198 |
1 files changed, 175 insertions, 23 deletions
diff --git a/usr/src/lib/nsswitch/dns/common/dns_common.c b/usr/src/lib/nsswitch/dns/common/dns_common.c index a9195f9f68..9e739a66f6 100644 --- a/usr/src/lib/nsswitch/dns/common/dns_common.c +++ b/usr/src/lib/nsswitch/dns/common/dns_common.c @@ -22,22 +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" - -#pragma weak dn_expand -#pragma weak res_ninit -#pragma weak res_ndestroy -#pragma weak res_nsearch -#pragma weak res_nclose -#pragma weak ns_get16 -#pragma weak ns_get32 -#pragma weak __ns_get16 -#pragma weak __ns_get32 +#include <sys/types.h> +#include <sys/socket.h> +#include <ifaddrs.h> +#include <net/if.h> #define DNS_ALIASES 0 #define DNS_ADDRLIST 1 @@ -321,6 +318,52 @@ name_is_alias(char *aliases_ptr, char *name_ptr) { return (NSS_NOTFOUND); } + +static int +_nss_has_interfaces(int *v4, int *v6) +{ + struct ifaddrs *ifp, *i; + struct in_addr in4; + struct in6_addr in6; + const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; + + *v4 = *v6 = 0; + + 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 != 0) + continue; + + if (((struct sockaddr_in *)i->ifa_addr)-> + sin_addr.s_addr == INADDR_ANY) + continue; + *v4 = 1; + } + + if (i->ifa_addr->sa_family == AF_INET6) { + if (*v6 != 0) + continue; + + if (memcmp(&in6addr_any, + &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr, + sizeof (struct in6_addr)) == 0) + continue; + *v6 = 1; + } + } + + 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. @@ -384,11 +427,14 @@ _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) int af; char *ap, *apc; int hlen = 0, alen, iplen, len, isans; + int has_v4 = 0, has_v6 = 0; + 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 +450,130 @@ _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 == 0) { + res_ndestroy(statp); + return (NSS_NOTFOUND); + } + if (family == AF_INET6 && has_v6 == 0 && + !(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 == 0 && flags & AI_V4MAPPED)) + qtype = T_A; + } else { + has_v4 = has_v6 = 1; + 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); + } + 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 ((flags & AI_ADDRCONFIG) && !(flags & AI_ALL) && + has_v4 == 0) { + 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 +722,15 @@ _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) { + qtype = T_A; + pass2 = 1; + goto searchagain; + } + /* Presumably the buffer is now filled. */ len = ROUND_UP(blen, sizeof (nssuint_t)); /* still room? */ |