diff options
| author | Robert Mustacchi <rm@joyent.com> | 2013-04-11 20:06:23 +0000 |
|---|---|---|
| committer | Robert Mustacchi <rm@joyent.com> | 2013-04-23 00:54:54 +0000 |
| commit | 0a073d9c072a82ec2d59f578a3a68c82aca35b5c (patch) | |
| tree | ce678151ab5f89e38d255e73fb8af07513c2e548 /usr/src/lib/nsswitch | |
| parent | ebdeab6a22ff38dc9e8777a6c4ed3270a3d4b044 (diff) | |
| download | illumos-joyent-0a073d9c072a82ec2d59f578a3a68c82aca35b5c.tar.gz | |
OS-2115 /etc/resolv.conf only handles a fixed number of DNS resolvers
OS-2148 suspect nscd is not honoring DNS TTLs
Diffstat (limited to 'usr/src/lib/nsswitch')
| -rw-r--r-- | usr/src/lib/nsswitch/dns/Makefile.com | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/dns_common.c | 198 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/dns_common.h | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/dns_mt.c | 125 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/gethostent.c | 8 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/gethostent6.c | 4 |
6 files changed, 196 insertions, 147 deletions
diff --git a/usr/src/lib/nsswitch/dns/Makefile.com b/usr/src/lib/nsswitch/dns/Makefile.com index 23c89c32f8..0366633c0c 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 -lresolv_joy -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..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? */ diff --git a/usr/src/lib/nsswitch/dns/common/dns_common.h b/usr/src/lib/nsswitch/dns/common/dns_common.h index 717f56d70f..83d486cf57 100644 --- a/usr/src/lib/nsswitch/dns/common/dns_common.h +++ b/usr/src/lib/nsswitch/dns/common/dns_common.h @@ -31,8 +31,6 @@ #ifndef _DNS_COMMON_H #define _DNS_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <ctype.h> #include <sys/types.h> @@ -43,7 +41,7 @@ #include <thread.h> #include <arpa/inet.h> #include <arpa/nameser.h> -#include <resolv.h> +#include <resolv_joy.h> #include <syslog.h> #include <nsswitch.h> #include <nss_common.h> diff --git a/usr/src/lib/nsswitch/dns/common/dns_mt.c b/usr/src/lib/nsswitch/dns/common/dns_mt.c index 128b1bde75..f17cd5170a 100644 --- a/usr/src/lib/nsswitch/dns/common/dns_mt.c +++ b/usr/src/lib/nsswitch/dns/common/dns_mt.c @@ -23,8 +23,9 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * dns_mt.c @@ -49,39 +50,30 @@ static void _nss_dns_init(void); extern struct hostent *res_gethostbyname(const char *); -#pragma weak res_gethostbyname -#define RES_SET_NO_HOSTS_FALLBACK "__res_set_no_hosts_fallback" -extern void __res_set_no_hosts_fallback(void); -#pragma weak __res_set_no_hosts_fallback +#define RES_SET_NO_HOSTS_FALLBACK "__joy_res_set_no_hosts_fallback" +extern void __joy_res_set_no_hosts_fallback(void); -#define RES_UNSET_NO_HOSTS_FALLBACK "__res_unset_no_hosts_fallback" -extern void __res_unset_no_hosts_fallback(void); -#pragma weak __res_unset_no_hosts_fallback +#define RES_UNSET_NO_HOSTS_FALLBACK "__joy_res_unset_no_hosts_fallback" +extern void __joy_res_unset_no_hosts_fallback(void); #define RES_GET_RES "__res_get_res" extern struct __res_state *__res_get_res(void); -#pragma weak __res_get_res #define RES_ENABLE_MT "__res_enable_mt" extern int __res_enable_mt(void); -#pragma weak __res_enable_mt #define RES_DISABLE_MT "__res_disable_mt" extern int __res_disable_mt(void); -#pragma weak __res_disable_mt #define RES_GET_H_ERRNO "__res_get_h_errno" extern int *__res_get_h_errno(); -#pragma weak __res_get_h_errno -#define __H_ERRNO "__h_errno" -extern int *__h_errno(void); -#pragma weak __h_errno +#define __H_ERRNO "__joy_h_errno" +extern int *__joy_h_errno(void); -#define RES_OVERRIDE_RETRY "__res_override_retry" -extern int __res_override_retry(int); -#pragma weak __res_override_retry +#define RES_OVERRIDE_RETRY "__joy_res_override_retry" +extern int __joy_res_override_retry(int); static void __fallback_set_no_hosts(void); static int *__fallback_h_errno(void); @@ -91,10 +83,10 @@ static int __is_mt_safe(void); void (*set_no_hosts_fallback)(void) = __fallback_set_no_hosts; void (*unset_no_hosts_fallback)(void) = __fallback_set_no_hosts; struct __res_state *(*set_res_retry)() = 0; -int (*enable_mt)() = 0; -int (*disable_mt)() = 0; -int *(*get_h_errno)(void) = 0; -int (*override_retry)(int) = 0; +int (*enable_mt)() = __is_mt_safe; +int (*disable_mt)() = __is_mt_safe; +int *(*get_h_errno)(void) = __joy_h_errno; +int (*override_retry)(int) = __joy_res_override_retry; /* Usually set from the Makefile */ #ifndef NSS_DNS_LIBRESOLV @@ -106,91 +98,12 @@ extern int h_errno; mutex_t one_lane = DEFAULTMUTEX; +/* Because we link against libresolv_joy.so.2, this is relatively easy. */ void _nss_dns_init(void) { - void *reslib, (*f_void_ptr)(); - - /* If no libresolv library, then load one */ - if (res_gethostbyname == 0) { - if ((reslib = - dlopen(NSS_DNS_LIBRESOLV, RTLD_LAZY|RTLD_GLOBAL)) != 0) { - /* Turn off /etc/hosts fall back in libresolv */ - if ((f_void_ptr = (void (*)(void))dlsym(reslib, - RES_SET_NO_HOSTS_FALLBACK)) != 0) { - set_no_hosts_fallback = f_void_ptr; - } - if ((f_void_ptr = (void (*)(void))dlsym(reslib, - RES_SET_NO_HOSTS_FALLBACK)) != 0) { - unset_no_hosts_fallback = f_void_ptr; - } - /* Set number of resolver retries */ - if ((override_retry = (int (*)(int))dlsym(reslib, - RES_OVERRIDE_RETRY)) == 0) { - set_res_retry = - (struct __res_state *(*)(void))dlsym(reslib, - RES_GET_RES); - override_retry = __fallback_override_retry; - } - /* - * Select h_errno retrieval function. A BIND 8.2.2 - * libresolv.so.2 will have __h_errno, a BIND 8.1.2 - * one will have __res_get_h_errno, and other - * versions may have nothing at all. - * - * Also try to bind to the relevant MT enable/disable - * functions which are also dependent on the version - * of the BIND libresolv.so.2 being used. - */ - if ((get_h_errno = (int *(*)(void))dlsym(reslib, - __H_ERRNO)) != 0) { - /* BIND 8.2.2 libresolv.so.2 is MT safe. */ - enable_mt = __is_mt_safe; - disable_mt = __is_mt_safe; - } else { - if ((get_h_errno = - (int *(*)(void))dlsym(reslib, - RES_GET_H_ERRNO)) == 0) { - get_h_errno = __fallback_h_errno; - } - /* - * Pre-BIND 8.2.2 was not MT safe. Try to - * bind the MT enable/disable functions. - */ - if ((enable_mt = (int (*)(void))dlsym(reslib, - RES_ENABLE_MT)) != 0 && - (disable_mt = (int (*)(void))dlsym(reslib, - RES_DISABLE_MT)) == 0) { - enable_mt = 0; - } - } - } - } else { - /* Libresolv already loaded */ - if ((f_void_ptr = __res_set_no_hosts_fallback) != 0) { - set_no_hosts_fallback = f_void_ptr; - } - if ((f_void_ptr = __res_unset_no_hosts_fallback) != 0) { - unset_no_hosts_fallback = f_void_ptr; - } - if ((override_retry = __res_override_retry) == 0) { - set_res_retry = __res_get_res; - override_retry = __fallback_override_retry; - } - if ((get_h_errno = __h_errno) == 0 && - (get_h_errno = __res_get_h_errno) == 0) { - get_h_errno = __fallback_h_errno; - } - if (get_h_errno == __h_errno) { - enable_mt = __is_mt_safe; - disable_mt = __is_mt_safe; - } else { - if ((enable_mt = __res_enable_mt) != 0 && - (disable_mt = __res_disable_mt) == 0) { - enable_mt = 0; - } - } - } + enable_mt = __is_mt_safe; + disable_mt = __is_mt_safe; } @@ -233,7 +146,7 @@ __is_mt_safe(void) { */ static int * __fallback_h_errno(void) { - return (&h_errno); + return (NULL); } diff --git a/usr/src/lib/nsswitch/dns/common/gethostent.c b/usr/src/lib/nsswitch/dns/common/gethostent.c index 648ea8ba01..d321dd24c6 100644 --- a/usr/src/lib/nsswitch/dns/common/gethostent.c +++ b/usr/src/lib/nsswitch/dns/common/gethostent.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * gethostent.c * @@ -53,12 +51,6 @@ static struct hostent *_gethostbyaddr(int *h_errnop, const char *addr, int len, int type); struct hostent *_nss_dns_gethostbyname2(int *h_errnop, const char *name); -#pragma weak res_gethostbyname -#pragma weak res_gethostbyname2 -#pragma weak res_gethostbyaddr -#pragma weak res_sethostent -#pragma weak res_endhostent - nss_backend_t *_nss_dns_constr(dns_backend_op_t ops[], int n_ops); nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *); diff --git a/usr/src/lib/nsswitch/dns/common/gethostent6.c b/usr/src/lib/nsswitch/dns/common/gethostent6.c index ee85832073..f3efc4eae6 100644 --- a/usr/src/lib/nsswitch/dns/common/gethostent6.c +++ b/usr/src/lib/nsswitch/dns/common/gethostent6.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This is the DNS backend for IPv6 addresses. * getbyname() is a local routine, but getbyaddr() actually shares the @@ -50,8 +48,6 @@ */ -#pragma weak res_endhostent - extern struct hostent *_gethostbyname(int *, const char *); extern struct hostent *_nss_dns_gethostbyname2(int *, const char *); |
