summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2013-04-11 20:06:23 +0000
committerRobert Mustacchi <rm@joyent.com>2013-04-23 00:54:54 +0000
commit0a073d9c072a82ec2d59f578a3a68c82aca35b5c (patch)
treece678151ab5f89e38d255e73fb8af07513c2e548 /usr/src/lib/nsswitch
parentebdeab6a22ff38dc9e8777a6c4ed3270a3d4b044 (diff)
downloadillumos-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.com4
-rw-r--r--usr/src/lib/nsswitch/dns/common/dns_common.c198
-rw-r--r--usr/src/lib/nsswitch/dns/common/dns_common.h4
-rw-r--r--usr/src/lib/nsswitch/dns/common/dns_mt.c125
-rw-r--r--usr/src/lib/nsswitch/dns/common/gethostent.c8
-rw-r--r--usr/src/lib/nsswitch/dns/common/gethostent6.c4
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 *);