Ian Kent 9ef58b
autofs-5.0.4 - use srv query for domain dn
Ian Kent 9ef58b
Ian Kent 9ef58b
From: Ian Kent <raven@themaw.net>
Ian Kent 9ef58b
Ian Kent 9ef58b
Add the ability to use a domain dn in the LDAP_URI configuration
Ian Kent 9ef58b
entry. If a domain dn is encountered in the LDAP_URI the list of
Ian Kent 9ef58b
servers will be queried and used for the LDAP connection. The list
Ian Kent 9ef58b
won't be queried again until the minimum ttl found in the SRV RR
Ian Kent 9ef58b
records is reached or, if ttl isn't given in any SRV RR records,
Ian Kent 9ef58b
after 1 hour.
Ian Kent 9ef58b
---
Ian Kent 9ef58b
Ian Kent 9ef58b
 CHANGELOG                      |    1 
Ian Kent 9ef58b
 include/dclist.h               |   14 +
Ian Kent 9ef58b
 include/lookup_ldap.h          |    3 
Ian Kent 9ef58b
 man/auto.master.5.in           |    8 
Ian Kent 9ef58b
 modules/Makefile               |    5 
Ian Kent 9ef58b
 modules/dclist.c               |  785 ++++++++++++++++++++++++++++++++++++++++
Ian Kent 9ef58b
 modules/lookup_ldap.c          |   86 ++++
Ian Kent 9ef58b
 redhat/autofs.sysconfig.in     |   11 +
Ian Kent 9ef58b
 samples/autofs.conf.default.in |   11 +
Ian Kent 9ef58b
 9 files changed, 911 insertions(+), 13 deletions(-)
Ian Kent 9ef58b
 create mode 100644 include/dclist.h
Ian Kent 9ef58b
 create mode 100644 modules/dclist.c
Ian Kent 9ef58b
Ian Kent 9ef58b
Ian Kent 9ef58b
diff --git a/CHANGELOG b/CHANGELOG
Ian Kent 9ef58b
index 5000f0c..f49784a 100644
Ian Kent 9ef58b
--- a/CHANGELOG
Ian Kent 9ef58b
+++ b/CHANGELOG
Ian Kent 9ef58b
@@ -49,6 +49,7 @@
Ian Kent 9ef58b
 - dont fail on ipv6 address when adding host.
Ian Kent 9ef58b
 - always read file maps multi map fix.
Ian Kent 9ef58b
 - always read file maps key lookup fixes.
Ian Kent 9ef58b
+- use srv query for domain dn.
Ian Kent 9ef58b
 
Ian Kent 9ef58b
 4/11/2008 autofs-5.0.4
Ian Kent 9ef58b
 -----------------------
Ian Kent 9ef58b
diff --git a/include/dclist.h b/include/dclist.h
Ian Kent 9ef58b
new file mode 100644
Ian Kent 9ef58b
index 0000000..ed89f97
Ian Kent 9ef58b
--- /dev/null
Ian Kent 9ef58b
+++ b/include/dclist.h
Ian Kent 9ef58b
@@ -0,0 +1,14 @@
Ian Kent 9ef58b
+#ifndef __DCLIST_H
Ian Kent 9ef58b
+#define __DCLIST_H
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#include <sys/types.h>
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+struct dclist {
Ian Kent 9ef58b
+	time_t expire;
Ian Kent 9ef58b
+	const char *uri;
Ian Kent 9ef58b
+};
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+struct dclist *get_dc_list(unsigned int logopt, const char *uri);
Ian Kent 9ef58b
+void free_dclist(struct dclist *dclist);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#endif
Ian Kent 9ef58b
diff --git a/include/lookup_ldap.h b/include/lookup_ldap.h
Ian Kent 9ef58b
index b47bf5d..dcae220 100644
Ian Kent 9ef58b
--- a/include/lookup_ldap.h
Ian Kent 9ef58b
+++ b/include/lookup_ldap.h
Ian Kent 9ef58b
@@ -10,6 +10,8 @@
Ian Kent 9ef58b
 #include <krb5.h>
Ian Kent 9ef58b
 #endif
Ian Kent 9ef58b
 
Ian Kent 9ef58b
+#include "dclist.h"
Ian Kent 9ef58b
+
Ian Kent 9ef58b
 struct ldap_schema {
Ian Kent 9ef58b
 	char *map_class;
Ian Kent 9ef58b
 	char *map_attr;
Ian Kent 9ef58b
@@ -57,6 +59,7 @@ struct lookup_context {
Ian Kent 9ef58b
 	pthread_mutex_t uris_mutex;
Ian Kent 9ef58b
 	struct list_head *uris;
Ian Kent 9ef58b
 	struct ldap_uri *uri;
Ian Kent 9ef58b
+	struct dclist *dclist;
Ian Kent 9ef58b
 	char *cur_host;
Ian Kent 9ef58b
 	struct ldap_searchdn *sdns;
Ian Kent 9ef58b
 
Ian Kent 9ef58b
diff --git a/man/auto.master.5.in b/man/auto.master.5.in
Ian Kent 9ef58b
index 7b7004f..71c4402 100644
Ian Kent 9ef58b
--- a/man/auto.master.5.in
Ian Kent 9ef58b
+++ b/man/auto.master.5.in
Ian Kent 9ef58b
@@ -271,6 +271,14 @@ Map entries that include a server name override this option and it is then
Ian Kent 9ef58b
 not used. Default is an empty list in which case either the server given
Ian Kent 9ef58b
 in a map entry or the LDAP configured default is used. This uri list is read at
Ian Kent 9ef58b
 startup and whenever the daemon receives a HUP signal.
Ian Kent 9ef58b
+.P
Ian Kent 9ef58b
+This configuration option can also be used to request autofs lookup SRV RRs
Ian Kent 9ef58b
+for a domain of the form <proto>:///[<domain dn>]. Note that a trailing
Ian Kent 9ef58b
+"/" is not allowed when using this form. If the domain dn is not specified
Ian Kent 9ef58b
+the dns domain name (if any) is used to construct the domain dn for the
Ian Kent 9ef58b
+SRV RR lookup. The server list returned from an SRV RR lookup is refreshed
Ian Kent 9ef58b
+according to the minimum ttl found in the SRV RR records or after one hour,
Ian Kent 9ef58b
+whichever is less.
Ian Kent 9ef58b
 .TP
Ian Kent 9ef58b
 .B SEARCH_BASE
Ian Kent 9ef58b
 The base dn to use when searching for amap base dn. This entry may be
Ian Kent 9ef58b
diff --git a/modules/Makefile b/modules/Makefile
Ian Kent 9ef58b
index 0d12f01..13b3bd8 100644
Ian Kent 9ef58b
--- a/modules/Makefile
Ian Kent 9ef58b
+++ b/modules/Makefile
Ian Kent 9ef58b
@@ -86,9 +86,10 @@ lookup_hesiod.so: lookup_hesiod.c
Ian Kent 9ef58b
 cyrus-sasl.o: cyrus-sasl.c
Ian Kent 9ef58b
 	$(CC) $(CFLAGS) $(LDAP_FLAGS) -c $<
Ian Kent 9ef58b
 
Ian Kent 9ef58b
-lookup_ldap.so: lookup_ldap.c $(SASL_OBJ)
Ian Kent 9ef58b
+lookup_ldap.so: lookup_ldap.c dclist.o $(SASL_OBJ)
Ian Kent 9ef58b
 	$(CC) $(SOLDFLAGS) $(CFLAGS) $(LDAP_FLAGS) -o lookup_ldap.so \
Ian Kent 9ef58b
-		lookup_ldap.c $(SASL_OBJ) $(AUTOFS_LIB) $(LIBLDAP)
Ian Kent 9ef58b
+		lookup_ldap.c dclist.o $(SASL_OBJ) \
Ian Kent 9ef58b
+		$(AUTOFS_LIB) $(LIBLDAP) $(LIBRESOLV)
Ian Kent 9ef58b
 	$(STRIP) lookup_ldap.so
Ian Kent 9ef58b
 
Ian Kent 9ef58b
 mount_nfs.so: mount_nfs.c replicated.o
Ian Kent 9ef58b
diff --git a/modules/dclist.c b/modules/dclist.c
Ian Kent 9ef58b
new file mode 100644
Ian Kent 9ef58b
index 0000000..5b0e577
Ian Kent 9ef58b
--- /dev/null
Ian Kent 9ef58b
+++ b/modules/dclist.c
Ian Kent 9ef58b
@@ -0,0 +1,785 @@
Ian Kent 9ef58b
+/*
Ian Kent 9ef58b
+ * Copyright 2009 Ian Kent <raven@themaw.net>
Ian Kent 9ef58b
+ * Copyright 2009 Red Hat, Inc.
Ian Kent 9ef58b
+ *
Ian Kent 9ef58b
+ * This module was apapted from code contained in the Samba distribution
Ian Kent 9ef58b
+ * file source/libads/dns.c which contained the following copyright
Ian Kent 9ef58b
+ * information:
Ian Kent 9ef58b
+ *
Ian Kent 9ef58b
+ * Unix SMB/CIFS implementation.
Ian Kent 9ef58b
+ * DNS utility library
Ian Kent 9ef58b
+ * Copyright (C) Gerald (Jerry) Carter           2006.
Ian Kent 9ef58b
+ * Copyright (C) Jeremy Allison                  2007.
Ian Kent 9ef58b
+ *
Ian Kent 9ef58b
+ * This program is free software; you can redistribute it and/or modify
Ian Kent 9ef58b
+ * it under the terms of the GNU General Public License as published by
Ian Kent 9ef58b
+ * the Free Software Foundation; either version 3 of the License, or
Ian Kent 9ef58b
+ * (at your option) any later version.
Ian Kent 9ef58b
+ *
Ian Kent 9ef58b
+ * This program is distributed in the hope that it will be useful,
Ian Kent 9ef58b
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
Ian Kent 9ef58b
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Ian Kent 9ef58b
+ * GNU General Public License for more details.
Ian Kent 9ef58b
+ *
Ian Kent 9ef58b
+ * You should have received a copy of the GNU General Public License
Ian Kent 9ef58b
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Ian Kent 9ef58b
+*/
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#include <netinet/in.h>
Ian Kent 9ef58b
+#include <arpa/nameser.h>
Ian Kent 9ef58b
+#include <stdlib.h>
Ian Kent 9ef58b
+#include <string.h>
Ian Kent 9ef58b
+#include <resolv.h>
Ian Kent 9ef58b
+#include <netdb.h>
Ian Kent 9ef58b
+#include <ldap.h>
Ian Kent 9ef58b
+#include <sys/param.h>
Ian Kent 9ef58b
+#include <errno.h>
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#include "automount.h"
Ian Kent 9ef58b
+#include "dclist.h"
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#define	MAX_DNS_PACKET_SIZE	0xffff
Ian Kent 9ef58b
+#define	MAX_DNS_NAME_LENGTH	MAXHOSTNAMELEN
Ian Kent 9ef58b
+/* The longest time we will cache dns srv records */
Ian Kent 9ef58b
+#define MAX_TTL			(60*60*1) /* 1 hours */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#ifdef NS_HFIXEDSZ	/* Bind 8/9 interface */
Ian Kent 9ef58b
+#if !defined(C_IN)	/* AIX 5.3 already defines C_IN */
Ian Kent 9ef58b
+#  define C_IN		ns_c_in
Ian Kent 9ef58b
+#endif
Ian Kent 9ef58b
+#if !defined(T_A)	/* AIX 5.3 already defines T_A */
Ian Kent 9ef58b
+#  define T_A   	ns_t_a
Ian Kent 9ef58b
+#endif
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#  define T_SRV 	ns_t_srv
Ian Kent 9ef58b
+#if !defined(T_NS)	/* AIX 5.3 already defines T_NS */
Ian Kent 9ef58b
+#  define T_NS 		ns_t_ns
Ian Kent 9ef58b
+#endif
Ian Kent 9ef58b
+#else
Ian Kent 9ef58b
+#  ifdef HFIXEDSZ
Ian Kent 9ef58b
+#    define NS_HFIXEDSZ HFIXEDSZ
Ian Kent 9ef58b
+#  else
Ian Kent 9ef58b
+#    define NS_HFIXEDSZ sizeof(HEADER)
Ian Kent 9ef58b
+#  endif	/* HFIXEDSZ */
Ian Kent 9ef58b
+#  ifdef PACKETSZ
Ian Kent 9ef58b
+#    define NS_PACKETSZ	PACKETSZ
Ian Kent 9ef58b
+#  else	/* 512 is usually the default */
Ian Kent 9ef58b
+#    define NS_PACKETSZ	512
Ian Kent 9ef58b
+#  endif	/* PACKETSZ */
Ian Kent 9ef58b
+#  define T_SRV 	33
Ian Kent 9ef58b
+#endif
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#define SVAL(buf, pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
Ian Kent 9ef58b
+#define IVAL(buf, pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
Ian Kent 9ef58b
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#define RSVAL(buf, pos) SREV(SVAL(buf, pos))
Ian Kent 9ef58b
+#define RIVAL(buf, pos) IREV(IVAL(buf, pos))
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#define QSORT_CAST	(int (*)(const void *, const void *))
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+/* DNS query section in replies */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+struct dns_query {
Ian Kent 9ef58b
+	const char *hostname;
Ian Kent 9ef58b
+	uint16_t type;
Ian Kent 9ef58b
+	uint16_t in_class;
Ian Kent 9ef58b
+};
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+/* DNS RR record in reply */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+struct dns_rr {
Ian Kent 9ef58b
+	const char *hostname;
Ian Kent 9ef58b
+	uint16_t type;
Ian Kent 9ef58b
+	uint16_t in_class;
Ian Kent 9ef58b
+	uint32_t ttl;
Ian Kent 9ef58b
+	uint16_t rdatalen;
Ian Kent 9ef58b
+	uint8_t *rdata;
Ian Kent 9ef58b
+};
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+/* SRV records */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+struct dns_rr_srv {
Ian Kent 9ef58b
+	const char *hostname;
Ian Kent 9ef58b
+	uint16_t priority;
Ian Kent 9ef58b
+	uint16_t weight;
Ian Kent 9ef58b
+	uint16_t port;
Ian Kent 9ef58b
+	uint32_t ttl;
Ian Kent 9ef58b
+};
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static pthread_mutex_t dclist_mutex = PTHREAD_MUTEX_INITIALIZER;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static void dclist_mutex_lock(void)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	int status = pthread_mutex_lock(&dclist_mutex);
Ian Kent 9ef58b
+	if (status)
Ian Kent 9ef58b
+		fatal(status);
Ian Kent 9ef58b
+	return;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static void dclist_mutex_unlock(void)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	int status = pthread_mutex_unlock(&dclist_mutex);
Ian Kent 9ef58b
+	if (status)
Ian Kent 9ef58b
+		fatal(status);
Ian Kent 9ef58b
+	return;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static int dns_parse_query(unsigned int logopt,
Ian Kent 9ef58b
+			   uint8_t *start, uint8_t *end,
Ian Kent 9ef58b
+			   uint8_t **ptr, struct dns_query *q)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	uint8_t *p = *ptr;
Ian Kent 9ef58b
+	char hostname[MAX_DNS_NAME_LENGTH];
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+	int namelen;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!start || !end || !q || !*ptr)
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	memset(q, 0, sizeof(*q));
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* See RFC 1035 for details. If this fails, then return. */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	namelen = dn_expand(start, end, p, hostname, sizeof(hostname));
Ian Kent 9ef58b
+	if (namelen < 0) {
Ian Kent 9ef58b
+		error(logopt, "failed to expand query hostname");
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	p += namelen;
Ian Kent 9ef58b
+	q->hostname = strdup(hostname);
Ian Kent 9ef58b
+	if (!q) {
Ian Kent 9ef58b
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+		error(logopt, "strdup: %s", estr);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* check that we have space remaining */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (p + 4 > end) {
Ian Kent 9ef58b
+		error(logopt, "insufficient buffer space for result");
Ian Kent 9ef58b
+		free((void *) q->hostname);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	q->type     = RSVAL(p, 0);
Ian Kent 9ef58b
+	q->in_class = RSVAL(p, 2);
Ian Kent 9ef58b
+	p += 4;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	*ptr = p;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return 1;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static int dns_parse_rr(unsigned int logopt,
Ian Kent 9ef58b
+			uint8_t *start, uint8_t *end,
Ian Kent 9ef58b
+			uint8_t **ptr, struct dns_rr *rr)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	uint8_t *p = *ptr;
Ian Kent 9ef58b
+	char hostname[MAX_DNS_NAME_LENGTH];
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+	int namelen;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!start || !end || !rr || !*ptr)
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	memset(rr, 0, sizeof(*rr));
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* pull the name from the answer */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	namelen = dn_expand(start, end, p, hostname, sizeof(hostname));
Ian Kent 9ef58b
+	if (namelen < 0) {
Ian Kent 9ef58b
+		error(logopt, "failed to expand query hostname");
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+	p += namelen;
Ian Kent 9ef58b
+	rr->hostname = strdup(hostname);
Ian Kent 9ef58b
+	if (!rr->hostname) {
Ian Kent 9ef58b
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+		error(logopt, "strdup: %s", estr);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* check that we have space remaining */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (p + 10 > end) {
Ian Kent 9ef58b
+		error(logopt, "insufficient buffer space for result");
Ian Kent 9ef58b
+		free((void *) rr->hostname);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* pull some values and then skip onto the string */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	rr->type     = RSVAL(p, 0);
Ian Kent 9ef58b
+	rr->in_class = RSVAL(p, 2);
Ian Kent 9ef58b
+	rr->ttl      = RIVAL(p, 4);
Ian Kent 9ef58b
+	rr->rdatalen = RSVAL(p, 8);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	p += 10;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* sanity check the available space */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (p + rr->rdatalen > end) {
Ian Kent 9ef58b
+		error(logopt, "insufficient buffer space for data");
Ian Kent 9ef58b
+		free((void *) rr->hostname);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* save a point to the rdata for this section */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	rr->rdata = p;
Ian Kent 9ef58b
+	p += rr->rdatalen;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	*ptr = p;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return 1;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static int dns_parse_rr_srv(unsigned int logopt,
Ian Kent 9ef58b
+			    uint8_t *start, uint8_t *end,
Ian Kent 9ef58b
+			    uint8_t **ptr, struct dns_rr_srv *srv)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	struct dns_rr rr;
Ian Kent 9ef58b
+	uint8_t *p;
Ian Kent 9ef58b
+	char dcname[MAX_DNS_NAME_LENGTH];
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+	int namelen;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!start || !end || !srv || !*ptr)
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Parse the RR entry.  Coming out of the this, ptr is at the beginning
Ian Kent 9ef58b
+	   of the next record */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!dns_parse_rr(logopt, start, end, ptr, &rr)) {
Ian Kent 9ef58b
+		error(logopt, "Failed to parse RR record");
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (rr.type != T_SRV) {
Ian Kent 9ef58b
+		error(logopt, "Bad answer type (%d)", rr.type);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	p = rr.rdata;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	srv->priority = RSVAL(p, 0);
Ian Kent 9ef58b
+	srv->weight   = RSVAL(p, 2);
Ian Kent 9ef58b
+	srv->port     = RSVAL(p, 4);
Ian Kent 9ef58b
+	srv->ttl      = rr.ttl;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	p += 6;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	namelen = dn_expand(start, end, p, dcname, sizeof(dcname));
Ian Kent 9ef58b
+	if (namelen < 0) {
Ian Kent 9ef58b
+		error(logopt, "Failed to expand dcname");
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	srv->hostname = strdup(dcname);
Ian Kent 9ef58b
+	if (!srv->hostname) {
Ian Kent 9ef58b
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+		error(logopt, "strdup: %s", estr);
Ian Kent 9ef58b
+		return 0;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	debug(logopt, "Parsed %s [%u, %u, %u]",
Ian Kent 9ef58b
+	      srv->hostname, srv->priority, srv->weight, srv->port);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return 1;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+/*********************************************************************
Ian Kent 9ef58b
+ Sort SRV record list based on weight and priority.  See RFC 2782.
Ian Kent 9ef58b
+*********************************************************************/
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static int dnssrvcmp(struct dns_rr_srv *a, struct dns_rr_srv *b)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	if (a->priority == b->priority) {
Ian Kent 9ef58b
+		/* randomize entries with an equal weight and priority */
Ian Kent 9ef58b
+		if (a->weight == b->weight)
Ian Kent 9ef58b
+			return 0;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		/* higher weights should be sorted lower */
Ian Kent 9ef58b
+		if (a->weight > b->weight)
Ian Kent 9ef58b
+			return -1;
Ian Kent 9ef58b
+		else
Ian Kent 9ef58b
+			return 1;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (a->priority < b->priority)
Ian Kent 9ef58b
+		return -1;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return 1;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+#define DNS_FAILED_WAITTIME          30
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static int dns_send_req(unsigned int logopt,
Ian Kent 9ef58b
+			const char *name, int q_type, uint8_t **rbuf,
Ian Kent 9ef58b
+			int *resp_length)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	uint8_t *buffer = NULL;
Ian Kent 9ef58b
+	size_t buf_len = 0;
Ian Kent 9ef58b
+	int resp_len = NS_PACKETSZ;
Ian Kent 9ef58b
+	static time_t last_dns_check = 0;
Ian Kent 9ef58b
+	static unsigned int last_dns_status = 0;
Ian Kent 9ef58b
+	time_t now = time(NULL);
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Try to prevent bursts of DNS lookups if the server is down */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Protect against large clock changes */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (last_dns_check > now)
Ian Kent 9ef58b
+		last_dns_check = 0;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* IF we had a DNS timeout or a bad server and we are still
Ian Kent 9ef58b
+	   in the 30 second cache window, just return the previous
Ian Kent 9ef58b
+	   status and save the network timeout. */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if ((last_dns_status == ETIMEDOUT ||
Ian Kent 9ef58b
+	     last_dns_status == ECONNREFUSED) &&
Ian Kent 9ef58b
+	     ((last_dns_check + DNS_FAILED_WAITTIME) > now)) {
Ian Kent 9ef58b
+		char *estr = strerror_r(last_dns_status, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+		debug(logopt, "Returning cached status (%s)", estr);
Ian Kent 9ef58b
+		return last_dns_status;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Send the Query */
Ian Kent 9ef58b
+	do {
Ian Kent 9ef58b
+		if (buffer)
Ian Kent 9ef58b
+			free(buffer);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		buf_len = resp_len * sizeof(uint8_t);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		if (buf_len) {
Ian Kent 9ef58b
+			buffer = malloc(buf_len);
Ian Kent 9ef58b
+			if (!buffer) {
Ian Kent 9ef58b
+				char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+				error(logopt, "malloc: %s", estr);
Ian Kent 9ef58b
+				last_dns_status = ENOMEM;
Ian Kent 9ef58b
+				last_dns_check = time(NULL);
Ian Kent 9ef58b
+				return last_dns_status;
Ian Kent 9ef58b
+			}
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		resp_len = res_query(name, C_IN, q_type, buffer, buf_len);
Ian Kent 9ef58b
+		if (resp_len < 0) {
Ian Kent 9ef58b
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+			error(logopt, "Failed to resolve %s (%s)", name, estr);
Ian Kent 9ef58b
+			free(buffer);
Ian Kent 9ef58b
+			last_dns_status = ENOENT;
Ian Kent 9ef58b
+			last_dns_check = time(NULL);
Ian Kent 9ef58b
+			return last_dns_status;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		/* On AIX, Solaris, and possibly some older glibc systems (e.g. SLES8)
Ian Kent 9ef58b
+		   truncated replies never give back a resp_len > buflen
Ian Kent 9ef58b
+		   which ends up causing DNS resolve failures on large tcp DNS replies */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		if (buf_len == resp_len) {
Ian Kent 9ef58b
+			if (resp_len == MAX_DNS_PACKET_SIZE) {
Ian Kent 9ef58b
+				error(logopt,
Ian Kent 9ef58b
+				      "DNS reply too large when resolving %s",
Ian Kent 9ef58b
+				      name);
Ian Kent 9ef58b
+				free(buffer);
Ian Kent 9ef58b
+				last_dns_status = EMSGSIZE;
Ian Kent 9ef58b
+				last_dns_check = time(NULL);
Ian Kent 9ef58b
+				return last_dns_status;
Ian Kent 9ef58b
+			}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+			resp_len = MIN(resp_len * 2, MAX_DNS_PACKET_SIZE);
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	} while (buf_len < resp_len && resp_len <= MAX_DNS_PACKET_SIZE);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	*rbuf = buffer;
Ian Kent 9ef58b
+	*resp_length = resp_len;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	last_dns_check = time(NULL);
Ian Kent 9ef58b
+	last_dns_status = 0;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return 0;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static int dns_lookup_srv(unsigned int logopt, const char *name,
Ian Kent 9ef58b
+			  struct dns_rr_srv **dclist, int *numdcs)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	uint8_t *buffer = NULL;
Ian Kent 9ef58b
+	int resp_len = 0;
Ian Kent 9ef58b
+	struct dns_rr_srv *dcs = NULL;
Ian Kent 9ef58b
+	int query_count, answer_count;
Ian Kent 9ef58b
+	uint8_t *p = buffer;
Ian Kent 9ef58b
+	int rrnum;
Ian Kent 9ef58b
+	int idx = 0;
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+	int ret;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!name || !dclist)
Ian Kent 9ef58b
+		return -EINVAL;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Send the request.  May have to loop several times in case
Ian Kent 9ef58b
+	   of large replies */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ret = dns_send_req(logopt, name, T_SRV, &buffer, &resp_len);
Ian Kent 9ef58b
+	if (ret) {
Ian Kent 9ef58b
+		error(logopt, "Failed to send DNS query");
Ian Kent 9ef58b
+		return ret;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+	p = buffer;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* For some insane reason, the ns_initparse() et. al. routines are only
Ian Kent 9ef58b
+	   available in libresolv.a, and not the shared lib.  Who knows why....
Ian Kent 9ef58b
+	   So we have to parse the DNS reply ourselves */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Pull the answer RR's count from the header.
Ian Kent 9ef58b
+	 * Use the NMB ordering macros */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	query_count      = RSVAL(p, 4);
Ian Kent 9ef58b
+	answer_count     = RSVAL(p, 6);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	debug(logopt,
Ian Kent 9ef58b
+	      "%d records returned in the answer section.",
Ian Kent 9ef58b
+	       answer_count);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (answer_count) {
Ian Kent 9ef58b
+		dcs = malloc(sizeof(struct dns_rr_srv) * answer_count);
Ian Kent 9ef58b
+		if (!dcs) {
Ian Kent 9ef58b
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+			error(logopt, "malloc: %s", estr);
Ian Kent 9ef58b
+			free(buffer);
Ian Kent 9ef58b
+			return ENOMEM;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* now skip the header */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	p += NS_HFIXEDSZ;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* parse the query section */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	for (rrnum = 0; rrnum < query_count; rrnum++) {
Ian Kent 9ef58b
+		struct dns_query q;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		ret = dns_parse_query(logopt, buffer, buffer+resp_len, &p, &q);
Ian Kent 9ef58b
+		if (!ret) {
Ian Kent 9ef58b
+			error(logopt,
Ian Kent 9ef58b
+			      "Failed to parse query record [%d]", rrnum);
Ian Kent 9ef58b
+			free(buffer);
Ian Kent 9ef58b
+			free(dcs);
Ian Kent 9ef58b
+			return EBADMSG;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* now we are at the answer section */
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	for (rrnum = 0; rrnum < answer_count; rrnum++) {
Ian Kent 9ef58b
+		ret = dns_parse_rr_srv(logopt,
Ian Kent 9ef58b
+				       buffer, buffer+resp_len,
Ian Kent 9ef58b
+				       &p, &dcs[rrnum]);
Ian Kent 9ef58b
+		if (!ret) {
Ian Kent 9ef58b
+			error(logopt,
Ian Kent 9ef58b
+			      "Failed to parse answer record [%d]", rrnum);
Ian Kent 9ef58b
+			free(buffer);
Ian Kent 9ef58b
+			free(dcs);
Ian Kent 9ef58b
+			return EBADMSG;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+	idx = rrnum;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	qsort(dcs, idx, sizeof(struct dns_rr_srv), QSORT_CAST dnssrvcmp);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	*dclist = dcs;
Ian Kent 9ef58b
+	*numdcs = idx;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return 0;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static char *escape_dn_commas(const char *uri)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	size_t len = strlen(uri);
Ian Kent 9ef58b
+	char *new, *tmp, *ptr;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ptr = (char *) uri;
Ian Kent 9ef58b
+	while (*ptr) {
Ian Kent 9ef58b
+		if (*ptr == '\\')
Ian Kent 9ef58b
+			ptr += 2;
Ian Kent 9ef58b
+		if (*ptr == ',')
Ian Kent 9ef58b
+			len += 2;
Ian Kent 9ef58b
+		ptr++;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	new = malloc(len + 1);
Ian Kent 9ef58b
+	if (!new)
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	memset(new, 0, len + 1);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ptr = (char *) uri;
Ian Kent 9ef58b
+	tmp = new;
Ian Kent 9ef58b
+	while (*ptr) {
Ian Kent 9ef58b
+		if (*ptr == '\\') {
Ian Kent 9ef58b
+			ptr++;
Ian Kent 9ef58b
+			*tmp++ = *ptr++;
Ian Kent 9ef58b
+			continue;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		if (*ptr == ',') {
Ian Kent 9ef58b
+			strcpy(tmp, "%2c");
Ian Kent 9ef58b
+			ptr++;
Ian Kent 9ef58b
+			tmp += 3;
Ian Kent 9ef58b
+			continue;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		*tmp++ = *ptr++;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return new;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+void free_dclist(struct dclist *dclist)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	if (dclist->uri)
Ian Kent 9ef58b
+		free((void *) dclist->uri);
Ian Kent 9ef58b
+	free(dclist);
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+static char *getdnsdomainname(unsigned int logopt)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	struct addrinfo hints, *ni;
Ian Kent 9ef58b
+	char name[MAX_DNS_NAME_LENGTH + 1];
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+	char *dnsdomain = NULL;
Ian Kent 9ef58b
+	char *ptr;
Ian Kent 9ef58b
+	int ret;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	memset(name, 0, sizeof(name));
Ian Kent 9ef58b
+	if (gethostname(name, MAX_DNS_NAME_LENGTH) == -1) {
Ian Kent 9ef58b
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+		error(logopt, "gethostname: %s", estr);
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	memset(&hints, 0, sizeof(hints));
Ian Kent 9ef58b
+	hints.ai_flags = AI_CANONNAME;
Ian Kent 9ef58b
+	hints.ai_family = AF_UNSPEC;
Ian Kent 9ef58b
+	hints.ai_socktype = SOCK_DGRAM;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ret = getaddrinfo(name, NULL, &hints, &ni);
Ian Kent 9ef58b
+	if (ret) {
Ian Kent 9ef58b
+		error(logopt, "hostname lookup failed: %s", gai_strerror(ret));
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ptr = ni->ai_canonname;
Ian Kent 9ef58b
+	while (*ptr && *ptr != '.')
Ian Kent 9ef58b
+		ptr++;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (*++ptr)
Ian Kent 9ef58b
+		dnsdomain = strdup(ptr);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	freeaddrinfo(ni);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return dnsdomain;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+struct dclist *get_dc_list(unsigned int logopt, const char *uri)
Ian Kent 9ef58b
+{
Ian Kent 9ef58b
+	LDAPURLDesc *ludlist = NULL;
Ian Kent 9ef58b
+	LDAPURLDesc **ludp;
Ian Kent 9ef58b
+	struct dns_rr_srv *dcs;
Ian Kent 9ef58b
+	unsigned int min_ttl = MAX_TTL;
Ian Kent 9ef58b
+	struct dclist *dclist = NULL;;
Ian Kent 9ef58b
+	char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+	char *dn_uri, *esc_uri;
Ian Kent 9ef58b
+	char *domain;
Ian Kent 9ef58b
+	char *list;
Ian Kent 9ef58b
+	int numdcs;
Ian Kent 9ef58b
+	int ret;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (strcmp(uri, "ldap:///") && strcmp(uri, "ldaps:///")) {
Ian Kent 9ef58b
+		dn_uri = strdup(uri);
Ian Kent 9ef58b
+		if (!dn_uri) {
Ian Kent 9ef58b
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+			error(logopt, "strdup: %s", estr);
Ian Kent 9ef58b
+			return NULL;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	} else {
Ian Kent 9ef58b
+		char *dnsdomain;
Ian Kent 9ef58b
+		char *hdn;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		dnsdomain = getdnsdomainname(logopt);
Ian Kent 9ef58b
+		if (!dnsdomain) {
Ian Kent 9ef58b
+			error(logopt, "failed to get dns domainname");
Ian Kent 9ef58b
+			return NULL;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		if (ldap_domain2dn(dnsdomain, &hdn) || hdn == NULL) {
Ian Kent 9ef58b
+			error(logopt,
Ian Kent 9ef58b
+			      "Could not turn domain \"%s\" into a dn\n",
Ian Kent 9ef58b
+			      dnsdomain);
Ian Kent 9ef58b
+			free(dnsdomain);
Ian Kent 9ef58b
+			return NULL;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		free(dnsdomain);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		dn_uri = malloc(strlen(uri) + strlen(hdn) + 1);
Ian Kent 9ef58b
+		if (!dn_uri) {
Ian Kent 9ef58b
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+			error(logopt, "malloc: %s", estr);
Ian Kent 9ef58b
+			ber_memfree(hdn);
Ian Kent 9ef58b
+			return NULL;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		strcpy(dn_uri, uri);
Ian Kent 9ef58b
+		strcat(dn_uri, hdn);
Ian Kent 9ef58b
+		ber_memfree(hdn);
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	esc_uri = escape_dn_commas(dn_uri);
Ian Kent 9ef58b
+	if (!esc_uri) {
Ian Kent 9ef58b
+		error(logopt, "Could not escape commas in uri %s", dn_uri);
Ian Kent 9ef58b
+		free(dn_uri);
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ret = ldap_url_parse(esc_uri, &ludlist);
Ian Kent 9ef58b
+	if (ret != LDAP_URL_SUCCESS) {
Ian Kent 9ef58b
+		error(logopt, "Could not parse uri %s (%d)", dn_uri, ret);
Ian Kent 9ef58b
+		free(esc_uri);
Ian Kent 9ef58b
+		free(dn_uri);
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	free(esc_uri);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!ludlist) {
Ian Kent 9ef58b
+		error(logopt, "No dn found in uri %s", dn_uri);
Ian Kent 9ef58b
+		free(dn_uri);
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	free(dn_uri);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	dclist = malloc(sizeof(struct dclist));
Ian Kent 9ef58b
+	if (!dclist) {
Ian Kent 9ef58b
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+		error(logopt, "malloc: %s", estr);
Ian Kent 9ef58b
+		ldap_free_urldesc(ludlist);
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+	memset(dclist, 0, sizeof(struct dclist));
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	list = NULL;
Ian Kent 9ef58b
+	for (ludp = &ludlist; *ludp != NULL;) {
Ian Kent 9ef58b
+		LDAPURLDesc *lud = *ludp;
Ian Kent 9ef58b
+		size_t req_len, len;
Ian Kent 9ef58b
+		char *request = NULL;
Ian Kent 9ef58b
+		char *tmp;
Ian Kent 9ef58b
+		int i;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		if (!lud->lud_dn && !lud->lud_dn[0] &&
Ian Kent 9ef58b
+		   (!lud->lud_host || !lud->lud_host[0])) {
Ian Kent 9ef58b
+			*ludp = lud->lud_next;
Ian Kent 9ef58b
+			continue;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		domain = NULL;
Ian Kent 9ef58b
+		if (ldap_dn2domain(lud->lud_dn, &domain) || domain == NULL) {
Ian Kent 9ef58b
+			error(logopt,
Ian Kent 9ef58b
+			      "Could not turn dn \"%s\" into a domain",
Ian Kent 9ef58b
+			      lud->lud_dn);
Ian Kent 9ef58b
+			*ludp = lud->lud_next;
Ian Kent 9ef58b
+			continue;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		debug(logopt, "doing lookup of SRV RRs for domain %s", domain);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		req_len = sizeof("_ldap._tcp.") + strlen(domain);
Ian Kent 9ef58b
+		request = malloc(req_len);
Ian Kent 9ef58b
+		if (!request) {
Ian Kent 9ef58b
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+			error(logopt, "malloc: %s", estr);
Ian Kent 9ef58b
+			goto out_error;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		ret = snprintf(request, req_len, "_ldap._tcp.%s", domain);
Ian Kent 9ef58b
+		if (ret >= req_len) {
Ian Kent 9ef58b
+			free(request);
Ian Kent 9ef58b
+			goto out_error;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		dclist_mutex_lock();
Ian Kent 9ef58b
+		if (dns_lookup_srv(logopt, request, &dcs, &numdcs)) {
Ian Kent 9ef58b
+			error(logopt,
Ian Kent 9ef58b
+			      "DNS SRV query failed for domain %s", domain);
Ian Kent 9ef58b
+			dclist_mutex_unlock();
Ian Kent 9ef58b
+			free(request);
Ian Kent 9ef58b
+			goto out_error;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		dclist_mutex_unlock();
Ian Kent 9ef58b
+		free(request);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		len = strlen(lud->lud_scheme);
Ian Kent 9ef58b
+		len += sizeof("://");
Ian Kent 9ef58b
+		len *= numdcs;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		for (i = 0; i < numdcs; i++) {
Ian Kent 9ef58b
+			if (dcs[i].ttl > 0 && dcs[i].ttl < min_ttl)
Ian Kent 9ef58b
+				min_ttl = dcs[i].ttl;
Ian Kent 9ef58b
+			len += strlen(dcs[i].hostname);
Ian Kent 9ef58b
+			if (dcs[i].port > 0)
Ian Kent 9ef58b
+				len += sizeof(":65535");
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		tmp = realloc(list, len);
Ian Kent 9ef58b
+		if (!tmp) {
Ian Kent 9ef58b
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Ian Kent 9ef58b
+			error(logopt, "realloc: %s", estr);
Ian Kent 9ef58b
+			goto out_error;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		if (!list)
Ian Kent 9ef58b
+			memset(tmp, 0, len);
Ian Kent 9ef58b
+		else
Ian Kent 9ef58b
+			strcat(tmp, " ");
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		for (i = 0; i < numdcs; i++) {
Ian Kent 9ef58b
+			if (i > 0)
Ian Kent 9ef58b
+				strcat(tmp, " ");
Ian Kent 9ef58b
+			strcat(tmp, lud->lud_scheme);
Ian Kent 9ef58b
+			strcat(tmp, "://");
Ian Kent 9ef58b
+			strcat(tmp, dcs[i].hostname);
Ian Kent 9ef58b
+			if (dcs[i].port > 0) {
Ian Kent 9ef58b
+				char port[7];
Ian Kent 9ef58b
+				ret = snprintf(port, 7, ":%d", dcs[i].port);
Ian Kent 9ef58b
+				if (ret > 6) {
Ian Kent 9ef58b
+					error(logopt,
Ian Kent 9ef58b
+					      "invalid port: %u", dcs[i].port);
Ian Kent 9ef58b
+					goto out_error;
Ian Kent 9ef58b
+				}
Ian Kent 9ef58b
+				strcat(tmp, port);
Ian Kent 9ef58b
+			}
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		list = tmp;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+		*ludp = lud->lud_next;
Ian Kent 9ef58b
+		ber_memfree(domain);
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ldap_free_urldesc(ludlist);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	dclist->expire = time(NULL) + min_ttl;
Ian Kent 9ef58b
+	dclist->uri = list;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	return dclist;
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+out_error:
Ian Kent 9ef58b
+	if (list)
Ian Kent 9ef58b
+		free(list);
Ian Kent 9ef58b
+	if (domain)
Ian Kent 9ef58b
+		ber_memfree(domain);
Ian Kent 9ef58b
+	ldap_free_urldesc(ludlist);
Ian Kent 9ef58b
+	free_dclist(dclist);
Ian Kent 9ef58b
+	return NULL;
Ian Kent 9ef58b
+}
Ian Kent 9ef58b
diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c
Ian Kent 9ef58b
index a847622..f6b3f42 100644
Ian Kent 9ef58b
--- a/modules/lookup_ldap.c
Ian Kent 9ef58b
+++ b/modules/lookup_ldap.c
Ian Kent 9ef58b
@@ -643,14 +643,26 @@ static LDAP *find_server(unsigned logopt, struct lookup_context *ctxt)
Ian Kent 9ef58b
 	LDAP *ldap = NULL;
Ian Kent 9ef58b
 	struct ldap_uri *this;
Ian Kent 9ef58b
 	struct list_head *p, *first;
Ian Kent 9ef58b
+	struct dclist *dclist = NULL;
Ian Kent 9ef58b
+	char *uri = NULL;
Ian Kent 9ef58b
 
Ian Kent 9ef58b
-	/* Try each uri in list, add connect fails to tmp list */
Ian Kent 9ef58b
 	uris_mutex_lock(ctxt);
Ian Kent 9ef58b
+	if (ctxt->dclist) {
Ian Kent 9ef58b
+		dclist = ctxt->dclist;
Ian Kent 9ef58b
+		if (ctxt->dclist->expire < time(NULL)) {
Ian Kent 9ef58b
+			free_dclist(ctxt->dclist);
Ian Kent 9ef58b
+			ctxt->dclist = NULL;
Ian Kent 9ef58b
+			dclist = NULL;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
 	if (!ctxt->uri)
Ian Kent 9ef58b
 		first = ctxt->uris;
Ian Kent 9ef58b
 	else
Ian Kent 9ef58b
 		first = &ctxt->uri->list;
Ian Kent 9ef58b
 	uris_mutex_unlock(ctxt);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	/* Try each uri, save point in server list upon success */
Ian Kent 9ef58b
 	p = first->next;
Ian Kent 9ef58b
 	while(p != first) {
Ian Kent 9ef58b
 		/* Skip list head */
Ian Kent 9ef58b
@@ -659,25 +671,62 @@ static LDAP *find_server(unsigned logopt, struct lookup_context *ctxt)
Ian Kent 9ef58b
 			continue;
Ian Kent 9ef58b
 		}
Ian Kent 9ef58b
 		this = list_entry(p, struct ldap_uri, list);
Ian Kent 9ef58b
-		debug(logopt, "trying server %s", this->uri);
Ian Kent 9ef58b
-		ldap = connect_to_server(logopt, this->uri, ctxt);
Ian Kent 9ef58b
+		if (!strstr(this->uri, ":///"))
Ian Kent 9ef58b
+			uri = strdup(this->uri);
Ian Kent 9ef58b
+		else {
Ian Kent 9ef58b
+			if (dclist)
Ian Kent 9ef58b
+				uri = strdup(dclist->uri);
Ian Kent 9ef58b
+			else {
Ian Kent 9ef58b
+				struct dclist *tmp;
Ian Kent 9ef58b
+				tmp = get_dc_list(logopt, this->uri);
Ian Kent 9ef58b
+				if (!tmp) {
Ian Kent 9ef58b
+					p = p->next;
Ian Kent 9ef58b
+					continue;
Ian Kent 9ef58b
+				}
Ian Kent 9ef58b
+				dclist = tmp;
Ian Kent 9ef58b
+				uri = strdup(dclist->uri);
Ian Kent 9ef58b
+			}
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		if (!uri) {
Ian Kent 9ef58b
+			p = p->next;
Ian Kent 9ef58b
+			continue;
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+		debug(logopt, "trying server uri %s", uri);
Ian Kent 9ef58b
+		ldap = connect_to_server(logopt, uri, ctxt);
Ian Kent 9ef58b
 		if (ldap) {
Ian Kent 9ef58b
-			info(logopt, "connected to uri %s", this->uri);
Ian Kent 9ef58b
-			uris_mutex_lock(ctxt);
Ian Kent 9ef58b
-			ctxt->uri = this;
Ian Kent 9ef58b
-			uris_mutex_unlock(ctxt);
Ian Kent 9ef58b
+			info(logopt, "connected to uri %s", uri);
Ian Kent 9ef58b
+			free(uri);
Ian Kent 9ef58b
 			break;
Ian Kent 9ef58b
 		}
Ian Kent 9ef58b
+		free(uri);
Ian Kent 9ef58b
+		uri = NULL;
Ian Kent 9ef58b
+		free_dclist(dclist);
Ian Kent 9ef58b
+		dclist = NULL;
Ian Kent 9ef58b
 		p = p->next;
Ian Kent 9ef58b
 	}
Ian Kent 9ef58b
 
Ian Kent 9ef58b
+	uris_mutex_lock(ctxt);
Ian Kent 9ef58b
+	if (ldap)
Ian Kent 9ef58b
+		ctxt->uri = this;
Ian Kent 9ef58b
+	if (dclist) {
Ian Kent 9ef58b
+		if (!ctxt->dclist)
Ian Kent 9ef58b
+			ctxt->dclist = dclist;
Ian Kent 9ef58b
+		else {
Ian Kent 9ef58b
+			if (ctxt->dclist != dclist) {
Ian Kent 9ef58b
+				free_dclist(ctxt->dclist);
Ian Kent 9ef58b
+				ctxt->dclist = dclist;
Ian Kent 9ef58b
+			}
Ian Kent 9ef58b
+		}
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+	uris_mutex_unlock(ctxt);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
 	return ldap;
Ian Kent 9ef58b
 }
Ian Kent 9ef58b
 
Ian Kent 9ef58b
 static LDAP *do_reconnect(unsigned logopt, struct lookup_context *ctxt)
Ian Kent 9ef58b
 {
Ian Kent 9ef58b
-	struct ldap_uri *this;
Ian Kent 9ef58b
 	LDAP *ldap;
Ian Kent 9ef58b
+	char *uri;
Ian Kent 9ef58b
 
Ian Kent 9ef58b
 	if (ctxt->server || !ctxt->uris) {
Ian Kent 9ef58b
 		ldap = do_connect(logopt, ctxt->server, ctxt);
Ian Kent 9ef58b
@@ -692,9 +741,20 @@ static LDAP *do_reconnect(unsigned logopt, struct lookup_context *ctxt)
Ian Kent 9ef58b
 	}
Ian Kent 9ef58b
 
Ian Kent 9ef58b
 	uris_mutex_lock(ctxt);
Ian Kent 9ef58b
-	this = ctxt->uri;
Ian Kent 9ef58b
+	if (ctxt->dclist)
Ian Kent 9ef58b
+		uri = strdup(ctxt->dclist->uri);
Ian Kent 9ef58b
+	else
Ian Kent 9ef58b
+		uri = strdup(ctxt->uri->uri);
Ian Kent 9ef58b
 	uris_mutex_unlock(ctxt);
Ian Kent 9ef58b
-	ldap = do_connect(logopt, this->uri, ctxt);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	if (!uri) {
Ian Kent 9ef58b
+		char buf[MAX_ERR_BUF];
Ian Kent 9ef58b
+		char *estr = strerror_r(errno, buf, sizeof(buf));
Ian Kent 9ef58b
+		crit(logopt, MODPREFIX "strdup: %s", estr);
Ian Kent 9ef58b
+		return NULL;
Ian Kent 9ef58b
+	}
Ian Kent 9ef58b
+
Ian Kent 9ef58b
+	ldap = do_connect(logopt, uri, ctxt);
Ian Kent 9ef58b
 #ifdef WITH_SASL
Ian Kent 9ef58b
 	/*
Ian Kent 9ef58b
 	 * Dispose of the sasl authentication connection and try the
Ian Kent 9ef58b
@@ -702,9 +762,11 @@ static LDAP *do_reconnect(unsigned logopt, struct lookup_context *ctxt)
Ian Kent 9ef58b
 	 */
Ian Kent 9ef58b
 	if (!ldap) {
Ian Kent 9ef58b
 		autofs_sasl_dispose(ctxt);
Ian Kent 9ef58b
-		ldap = connect_to_server(logopt, this->uri, ctxt);
Ian Kent 9ef58b
+		ldap = connect_to_server(logopt, uri, ctxt);
Ian Kent 9ef58b
 	}
Ian Kent 9ef58b
 #endif
Ian Kent 9ef58b
+	free(uri);
Ian Kent 9ef58b
+
Ian Kent 9ef58b
 	if (ldap)
Ian Kent 9ef58b
 		return ldap;
Ian Kent 9ef58b
 
Ian Kent 9ef58b
@@ -1296,6 +1358,8 @@ static void free_context(struct lookup_context *ctxt)
Ian Kent 9ef58b
 		fatal(ret);
Ian Kent 9ef58b
 	if (ctxt->sdns)
Ian Kent 9ef58b
 		defaults_free_searchdns(ctxt->sdns);
Ian Kent 9ef58b
+	if (ctxt->dclist)
Ian Kent 9ef58b
+		free_dclist(ctxt->dclist);
Ian Kent 9ef58b
 	free(ctxt);
Ian Kent 9ef58b
 
Ian Kent 9ef58b
 	return;
Ian Kent 9ef58b
diff --git a/redhat/autofs.sysconfig.in b/redhat/autofs.sysconfig.in
Ian Kent 9ef58b
index 97e20fe..37448ea 100644
Ian Kent 9ef58b
--- a/redhat/autofs.sysconfig.in
Ian Kent 9ef58b
+++ b/redhat/autofs.sysconfig.in
Ian Kent 9ef58b
@@ -50,6 +50,17 @@ BROWSE_MODE="no"
Ian Kent 9ef58b
 # 	     Map entries that include a server name override
Ian Kent 9ef58b
 # 	     this option.
Ian Kent 9ef58b
 #
Ian Kent 9ef58b
+#	     This configuration option can also be used to
Ian Kent 9ef58b
+#	     request autofs lookup SRV RRs for a domain of
Ian Kent 9ef58b
+#	     the form <proto>:///[<domain dn>]. Note that a
Ian Kent 9ef58b
+#	     trailing "/" is not allowed when using this form.
Ian Kent 9ef58b
+#	     If the domain dn is not specified the dns domain
Ian Kent 9ef58b
+#	     name (if any) is used to construct the domain dn
Ian Kent 9ef58b
+#	     for the SRV RR lookup. The server list returned
Ian Kent 9ef58b
+#	     from an SRV RR lookup is refreshed according to
Ian Kent 9ef58b
+#	     the minimum ttl found in the SRV RR records or
Ian Kent 9ef58b
+#	     after one hour, whichever is less.
Ian Kent 9ef58b
+#
Ian Kent 9ef58b
 #LDAP_URI=""
Ian Kent 9ef58b
 #
Ian Kent 9ef58b
 # LDAP__TIMEOUT - timeout value for the synchronous API  calls
Ian Kent 9ef58b
diff --git a/samples/autofs.conf.default.in b/samples/autofs.conf.default.in
Ian Kent 9ef58b
index 62084c2..7dee5fd 100644
Ian Kent 9ef58b
--- a/samples/autofs.conf.default.in
Ian Kent 9ef58b
+++ b/samples/autofs.conf.default.in
Ian Kent 9ef58b
@@ -48,6 +48,17 @@ BROWSE_MODE="no"
Ian Kent 9ef58b
 # 	     Map entries that include a server name override
Ian Kent 9ef58b
 # 	     this option.
Ian Kent 9ef58b
 #
Ian Kent 9ef58b
+#	     This configuration option can also be used to
Ian Kent 9ef58b
+#	     request autofs lookup SRV RRs for a domain of
Ian Kent 9ef58b
+#	     the form <proto>:///[<domain dn>]. Note that a
Ian Kent 9ef58b
+#	     trailing "/" is not allowed when using this form.
Ian Kent 9ef58b
+#	     If the domain dn is not specified the dns domain
Ian Kent 9ef58b
+#	     name (if any) is used to construct the domain dn
Ian Kent 9ef58b
+#	     for the SRV RR lookup. The server list returned
Ian Kent 9ef58b
+#	     from an SRV RR lookup is refreshed according to
Ian Kent 9ef58b
+#	     the minimum ttl found in the SRV RR records or
Ian Kent 9ef58b
+#	     after one hour, whichever is less.
Ian Kent 9ef58b
+#
Ian Kent 9ef58b
 #LDAP_URI=""
Ian Kent 9ef58b
 #
Ian Kent 9ef58b
 # LDAP__TIMEOUT - timeout value for the synchronous API  calls