autofs-5.0.5 - replace GPLv3 code

From: Ian Kent <raven@themaw.net>

The code to get SRV records from DNS was taken from Samba.
Samba is a GPLv3 licensed work which forces autofs to GPLv3.

I don't know enough about GPLv3 to know if that is a good thing
but I also don't like autofs being forced to GPLv3 because one
of the copyright holders won't grant permission to use the code
under a GPLv2 license.
---

 CHANGELOG        |    1 
 modules/dclist.c |  590 +++++++++++++++++-------------------------------------
 2 files changed, 192 insertions(+), 399 deletions(-)


diff --git a/CHANGELOG b/CHANGELOG
index b9d8299..347d7d7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -61,6 +61,7 @@
 - fix prune cache valid check.
 - fix mountd vers retry.
 - fix expire race.
+- replace GPLv3 code.
 
 03/09/2009 autofs-5.0.5
 -----------------------
diff --git a/modules/dclist.c b/modules/dclist.c
index 967581c..aeb107f 100644
--- a/modules/dclist.c
+++ b/modules/dclist.c
@@ -1,19 +1,10 @@
 /*
- * Copyright 2009 Ian Kent <raven@themaw.net>
- * Copyright 2009 Red Hat, Inc.
- *
- * This module was apapted from code contained in the Samba distribution
- * file source/libads/dns.c which contained the following copyright
- * information:
- *
- * Unix SMB/CIFS implementation.
- * DNS utility library
- * Copyright (C) Gerald (Jerry) Carter           2006.
- * Copyright (C) Jeremy Allison                  2007.
+ * Copyright 2011 Ian Kent <raven@themaw.net>
+ * Copyright 2011 Red Hat, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -25,7 +16,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <sys/types.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <arpa/nameser.h>
 #include <stdlib.h>
 #include <string.h>
@@ -39,80 +32,21 @@
 #include "automount.h"
 #include "dclist.h"
 
-#define	MAX_DNS_PACKET_SIZE	0xffff
-#define	MAX_DNS_NAME_LENGTH	MAXHOSTNAMELEN
-/* The longest time we will cache dns srv records */
-#define MAX_TTL			(60*60*1) /* 1 hours */
-
-#ifdef NS_HFIXEDSZ	/* Bind 8/9 interface */
-#if !defined(C_IN)	/* AIX 5.3 already defines C_IN */
-#  define C_IN		ns_c_in
-#endif
-#if !defined(T_A)	/* AIX 5.3 already defines T_A */
-#  define T_A   	ns_t_a
-#endif
-
-#  define T_SRV 	ns_t_srv
-#if !defined(T_NS)	/* AIX 5.3 already defines T_NS */
-#  define T_NS 		ns_t_ns
-#endif
-#else
-#  ifdef HFIXEDSZ
-#    define NS_HFIXEDSZ HFIXEDSZ
-#  else
-#    define NS_HFIXEDSZ sizeof(HEADER)
-#  endif	/* HFIXEDSZ */
-#  ifdef PACKETSZ
-#    define NS_PACKETSZ	PACKETSZ
-#  else	/* 512 is usually the default */
-#    define NS_PACKETSZ	512
-#  endif	/* PACKETSZ */
-#  define T_SRV 	33
-#endif
-
-#define SVAL(buf, pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
-#define IVAL(buf, pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
-#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
-#else
-#define SREV(x) (x)
-#define IREV(x) (x)
-#endif
-
-#define RSVAL(buf, pos) SREV(SVAL(buf, pos))
-#define RIVAL(buf, pos) IREV(IVAL(buf, pos))
-
-#define QSORT_CAST	(int (*)(const void *, const void *))
-
-/* DNS query section in replies */
-
-struct dns_query {
-	const char *hostname;
-	uint16_t type;
-	uint16_t in_class;
-};
-
-/* DNS RR record in reply */
+#define MAX_TTL		(60*60) /* 1 hour */
 
-struct dns_rr {
-	const char *hostname;
-	uint16_t type;
-	uint16_t in_class;
-	uint32_t ttl;
-	uint16_t rdatalen;
-	uint8_t *rdata;
+struct rr {
+	unsigned int type;
+	unsigned int class;
+	unsigned long ttl;
+	unsigned int len;
 };
 
-/* SRV records */
-
-struct dns_rr_srv {
-	const char *hostname;
-	uint16_t priority;
-	uint16_t weight;
-	uint16_t port;
-	uint32_t ttl;
+struct srv_rr {
+	const char *name;
+	unsigned int priority;
+	unsigned int weight;
+	unsigned int port;
+	unsigned long ttl;
 };
 
 static pthread_mutex_t dclist_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -133,374 +67,224 @@ static void dclist_mutex_unlock(void)
 	return;
 }
 
-static int dns_parse_query(unsigned int logopt,
-			   uint8_t *start, uint8_t *end,
-			   uint8_t **ptr, struct dns_query *q)
+static int do_srv_query(unsigned int logopt, char *name, u_char **packet)
 {
-	uint8_t *p = *ptr;
-	char hostname[MAX_DNS_NAME_LENGTH];
-	char buf[MAX_ERR_BUF];
-	int namelen;
-
-	if (!start || !end || !q || !*ptr)
-		return 0;
-
-	memset(q, 0, sizeof(*q));
-
-	/* See RFC 1035 for details. If this fails, then return. */
-
-	namelen = dn_expand(start, end, p, hostname, sizeof(hostname));
-	if (namelen < 0) {
-		error(logopt, "failed to expand query hostname");
-		return 0;
-	}
+	unsigned int len = PACKETSZ;
+	unsigned int last_len = len;
+	char ebuf[MAX_ERR_BUF];
+	u_char *buf;
+
+	while (1) {
+		buf = malloc(last_len);
+		if (!buf) {
+			char *estr = strerror_r(errno, ebuf, MAX_ERR_BUF);
+			error(logopt, "malloc: %s", estr);
+			return -1;
+		}
 
-	p += namelen;
-	q->hostname = strdup(hostname);
-	if (!q) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		error(logopt, "strdup: %s", estr);
-		return 0;
-	}
+		len = res_query(name, C_IN, T_SRV, buf, last_len);
+		if (len < 0) {
+			char *estr = strerror_r(errno, ebuf, MAX_ERR_BUF);
+			error(logopt, "Failed to resolve %s (%s)", name, estr);
+			free(buf);
+			return -1;
+		}
 
-	/* check that we have space remaining */
+		if (len == last_len) {
+			/* These shouldn't too large, bump by PACKETSZ only */
+			last_len += PACKETSZ;
+			free(buf);
+			continue;
+		}
 
-	if (p + 4 > end) {
-		error(logopt, "insufficient buffer space for result");
-		free((void *) q->hostname);
-		return 0;
+		break;
 	}
 
-	q->type     = RSVAL(p, 0);
-	q->in_class = RSVAL(p, 2);
-	p += 4;
+	*packet = buf;
 
-	*ptr = p;
-
-	return 1;
+	return len;
 }
 
-static int dns_parse_rr(unsigned int logopt,
-			uint8_t *start, uint8_t *end,
-			uint8_t **ptr, struct dns_rr *rr)
+static int get_name_len(u_char *buffer, u_char *start, u_char *end)
 {
-	uint8_t *p = *ptr;
-	char hostname[MAX_DNS_NAME_LENGTH];
-	char buf[MAX_ERR_BUF];
-	int namelen;
-
-	if (!start || !end || !rr || !*ptr)
-		return 0;
-
-	memset(rr, 0, sizeof(*rr));
-
-	/* pull the name from the answer */
-
-	namelen = dn_expand(start, end, p, hostname, sizeof(hostname));
-	if (namelen < 0) {
-		error(logopt, "failed to expand query hostname");
-		return 0;
-	}
-	p += namelen;
-	rr->hostname = strdup(hostname);
-	if (!rr->hostname) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		error(logopt, "strdup: %s", estr);
-		return 0;
-	}
-
-	/* check that we have space remaining */
-
-	if (p + 10 > end) {
-		error(logopt, "insufficient buffer space for result");
-		free((void *) rr->hostname);
-		return 0;
-	}
-
-	/* pull some values and then skip onto the string */
-
-	rr->type     = RSVAL(p, 0);
-	rr->in_class = RSVAL(p, 2);
-	rr->ttl      = RIVAL(p, 4);
-	rr->rdatalen = RSVAL(p, 8);
-
-	p += 10;
-
-	/* sanity check the available space */
-
-	if (p + rr->rdatalen > end) {
-		error(logopt, "insufficient buffer space for data");
-		free((void *) rr->hostname);
-		return 0;
-	}
-
-	/* save a point to the rdata for this section */
-
-	rr->rdata = p;
-	p += rr->rdatalen;
-
-	*ptr = p;
-
-	return 1;
+	char tmp[MAXDNAME];
+	return dn_expand(buffer, end, start, tmp, MAXDNAME);
 }
 
-static int dns_parse_rr_srv(unsigned int logopt,
-			    uint8_t *start, uint8_t *end,
-			    uint8_t **ptr, struct dns_rr_srv *srv)
+static int get_data_offset(u_char *buffer,
+			   u_char *start, u_char *end,
+			   struct rr *rr)
 {
-	struct dns_rr rr;
-	uint8_t *p;
-	char dcname[MAX_DNS_NAME_LENGTH];
-	char buf[MAX_ERR_BUF];
-	int namelen;
-
-	if (!start || !end || !srv || !*ptr)
-		return 0;
-
-	/* Parse the RR entry.  Coming out of the this, ptr is at the beginning
-	   of the next record */
-
-	if (!dns_parse_rr(logopt, start, end, ptr, &rr)) {
-		error(logopt, "Failed to parse RR record");
-		return 0;
-	}
+	u_char *cp = start;
+	int name_len;
 
-	if (rr.type != T_SRV) {
-		error(logopt, "Bad answer type (%d)", rr.type);
-		return 0;
-	}
-
-	p = rr.rdata;
+	name_len = get_name_len(buffer, start, end);
+	if (name_len < 0)
+		return -1;
+	cp += name_len;
 
-	srv->priority = RSVAL(p, 0);
-	srv->weight   = RSVAL(p, 2);
-	srv->port     = RSVAL(p, 4);
-	srv->ttl      = rr.ttl;
+	GETSHORT(rr->type, cp);
+	GETSHORT(rr->class, cp);
+	GETLONG(rr->ttl, cp);
+	GETSHORT(rr->len, cp);
 
-	p += 6;
+	return (cp - start);
+}
 
-	namelen = dn_expand(start, end, p, dcname, sizeof(dcname));
-	if (namelen < 0) {
-		error(logopt, "Failed to expand dcname");
-		return 0;
+static struct srv_rr *parse_srv_rr(unsigned int logopt,
+				   u_char *buffer, u_char *start, u_char *end,
+				   struct rr *rr, struct srv_rr *srv)
+{
+	u_char *cp = start;
+	char ebuf[MAX_ERR_BUF];
+	char tmp[MAXDNAME];
+	int len;
+
+	GETSHORT(srv->priority, cp);
+	GETSHORT(srv->weight, cp);
+	GETSHORT(srv->port, cp);
+	srv->ttl = rr->ttl;
+
+	len = dn_expand(buffer, end, cp, tmp, MAXDNAME);
+	if (len < 0) {
+		error(logopt, "failed to expand name");
+		return NULL;
 	}
-
-	srv->hostname = strdup(dcname);
-	if (!srv->hostname) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+	srv->name = strdup(tmp);
+	if (!srv->name) {
+		char *estr = strerror_r(errno, ebuf, MAX_ERR_BUF);
 		error(logopt, "strdup: %s", estr);
-		return 0;
+		return NULL;
 	}
 
-	debug(logopt, "Parsed %s [%u, %u, %u]",
-	      srv->hostname, srv->priority, srv->weight, srv->port);
-
-	return 1;
+	return srv;
 }
 
-/*********************************************************************
- Sort SRV record list based on weight and priority.  See RFC 2782.
-*********************************************************************/
-
-static int dnssrvcmp(struct dns_rr_srv *a, struct dns_rr_srv *b)
+static int cmp(struct srv_rr *a, struct srv_rr *b)
 {
-	if (a->priority == b->priority) {
-		/* randomize entries with an equal weight and priority */
-		if (a->weight == b->weight)
-			return 0;
+	if (a->priority < b->priority)
+		return -1;
 
-		/* higher weights should be sorted lower */
-		if (a->weight > b->weight)
-			return -1;
-		else
-			return 1;
-	}
+	if (a->priority > b->priority)
+		return 1;
 
-	if (a->priority < b->priority)
+	if (!a->weight || a->weight == b->weight)
+		return 0;
+
+	if (a->weight > b->weight)
 		return -1;
 
 	return 1;
 }
 
-#define DNS_FAILED_WAITTIME          30
-
-static int dns_send_req(unsigned int logopt,
-			const char *name, int q_type, uint8_t **rbuf,
-			int *resp_length)
+static void free_srv_rrs(struct srv_rr *dcs, unsigned int count)
 {
-	uint8_t *buffer = NULL;
-	size_t buf_len = 0;
-	int resp_len = NS_PACKETSZ;
-	static time_t last_dns_check = 0;
-	static unsigned int last_dns_status = 0;
-	time_t now = time(NULL);
-	char buf[MAX_ERR_BUF];
-
-	/* Try to prevent bursts of DNS lookups if the server is down */
-
-	/* Protect against large clock changes */
-
-	if (last_dns_check > now)
-		last_dns_check = 0;
+	int i;
 
-	/* IF we had a DNS timeout or a bad server and we are still
-	   in the 30 second cache window, just return the previous
-	   status and save the network timeout. */
-
-	if ((last_dns_status == ETIMEDOUT ||
-	     last_dns_status == ECONNREFUSED) &&
-	     ((last_dns_check + DNS_FAILED_WAITTIME) > now)) {
-		char *estr = strerror_r(last_dns_status, buf, MAX_ERR_BUF);
-		debug(logopt, "Returning cached status (%s)", estr);
-		return last_dns_status;
+	for (i = 0; i < count; i++) {
+		if (dcs[i].name)
+			free((void *) dcs[i].name);
 	}
-
-	/* Send the Query */
-	do {
-		if (buffer)
-			free(buffer);
-
-		buf_len = resp_len * sizeof(uint8_t);
-
-		if (buf_len) {
-			buffer = malloc(buf_len);
-			if (!buffer) {
-				char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-				error(logopt, "malloc: %s", estr);
-				last_dns_status = ENOMEM;
-				last_dns_check = time(NULL);
-				return last_dns_status;
-			}
-		}
-
-		resp_len = res_query(name, C_IN, q_type, buffer, buf_len);
-		if (resp_len < 0) {
-			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-			error(logopt, "Failed to resolve %s (%s)", name, estr);
-			free(buffer);
-			last_dns_status = ENOENT;
-			last_dns_check = time(NULL);
-			return last_dns_status;
-		}
-
-		/* On AIX, Solaris, and possibly some older glibc systems (e.g. SLES8)
-		   truncated replies never give back a resp_len > buflen
-		   which ends up causing DNS resolve failures on large tcp DNS replies */
-
-		if (buf_len == resp_len) {
-			if (resp_len == MAX_DNS_PACKET_SIZE) {
-				error(logopt,
-				      "DNS reply too large when resolving %s",
-				      name);
-				free(buffer);
-				last_dns_status = EMSGSIZE;
-				last_dns_check = time(NULL);
-				return last_dns_status;
-			}
-
-			resp_len = MIN(resp_len * 2, MAX_DNS_PACKET_SIZE);
-		}
-	} while (buf_len < resp_len && resp_len <= MAX_DNS_PACKET_SIZE);
-
-	*rbuf = buffer;
-	*resp_length = resp_len;
-
-	last_dns_check = time(NULL);
-	last_dns_status = 0;
-
-	return 0;
+	free(dcs);
 }
 
-static int dns_lookup_srv(unsigned int logopt, const char *name,
-			  struct dns_rr_srv **dclist, int *numdcs)
+int get_srv_rrs(unsigned int logopt,
+		char *name, struct srv_rr **dcs, unsigned int *dcs_count)
 {
-	uint8_t *buffer = NULL;
-	int resp_len = 0;
-	struct dns_rr_srv *dcs = NULL;
-	int query_count, answer_count;
-	uint8_t *p = buffer;
-	int rrnum;
-	int idx = 0;
-	char buf[MAX_ERR_BUF];
-	int ret;
+	struct srv_rr *srvs;
+	unsigned int srv_num;
+	HEADER *header;
+	u_char *packet;
+	u_char *start;
+	u_char *end;
+	unsigned int count;
+	int i, len;
+	char ebuf[MAX_ERR_BUF];
+
+	len = do_srv_query(logopt, name, &packet);
+	if (len < 0)
+		return 0;
 
-	if (!name || !dclist)
-		return -EINVAL;
+	header = (HEADER *) packet;
+	start = packet + sizeof(HEADER);
+	end = packet + len;
 
-	/* Send the request.  May have to loop several times in case
-	   of large replies */
+	srvs = NULL;
+	srv_num = 0;
 
-	ret = dns_send_req(logopt, name, T_SRV, &buffer, &resp_len);
-	if (ret) {
-		error(logopt, "Failed to send DNS query");
-		return ret;
+	/* Skip over question */
+	len = get_name_len(packet, start, end);
+	if (len < 0) {
+		error(logopt, "failed to get name length");
+		goto error_out;
 	}
-	p = buffer;
 
-	/* For some insane reason, the ns_initparse() et. al. routines are only
-	   available in libresolv.a, and not the shared lib.  Who knows why....
-	   So we have to parse the DNS reply ourselves */
+	start += len + QFIXEDSZ;
 
-	/* Pull the answer RR's count from the header.
-	 * Use the NMB ordering macros */
+	count = ntohs(header->ancount);
 
-	query_count      = RSVAL(p, 4);
-	answer_count     = RSVAL(p, 6);
+	debug(logopt, "%d records returned in the answer section", count);
 
-	debug(logopt,
-	      "%d records returned in the answer section.",
-	       answer_count);
+	if (count <= 0) {
+		error(logopt, "no records found in answers section");
+		goto error_out;
+	}
 
-	if (answer_count) {
-		dcs = malloc(sizeof(struct dns_rr_srv) * answer_count);
-		if (!dcs) {
-			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-			error(logopt, "malloc: %s", estr);
-			free(buffer);
-			return ENOMEM;
-		}
+	srvs = malloc(sizeof(struct srv_rr) * count);
+	if (!srvs) {
+		char *estr = strerror_r(errno, ebuf, MAX_ERR_BUF);
+		error(logopt, "malloc: %s", estr);
+		goto error_out;
 	}
+	memset(srvs, 0, sizeof(struct srv_rr) * count);
 
-	/* now skip the header */
+	srv_num = 0;
+	for (i = 0; i < count && (start < end); i++) {
+		unsigned int data_offset;
+		struct srv_rr srv;
+		struct srv_rr *psrv;
+		struct rr rr;
 
-	p += NS_HFIXEDSZ;
+		memset(&rr, 0, sizeof(struct rr));
 
-	/* parse the query section */
+		data_offset = get_data_offset(packet, start, end, &rr);
+		if (data_offset <= 0) {
+			error(logopt, "failed to get start of data");
+			goto error_out;
+		}
+		start += data_offset;
 
-	for (rrnum = 0; rrnum < query_count; rrnum++) {
-		struct dns_query q;
+		if (rr.type != T_SRV)
+			continue;
 
-		ret = dns_parse_query(logopt, buffer, buffer+resp_len, &p, &q);
-		if (!ret) {
-			error(logopt,
-			      "Failed to parse query record [%d]", rrnum);
-			free(buffer);
-			free(dcs);
-			return EBADMSG;
+		psrv = parse_srv_rr(logopt, packet, start, end, &rr, &srv);
+		if (psrv) {
+			memcpy(&srvs[srv_num], psrv, sizeof(struct srv_rr));
+			srv_num++;
 		}
-	}
 
-	/* now we are at the answer section */
+		start += rr.len;
+	}
+	free(packet);
 
-	for (rrnum = 0; rrnum < answer_count; rrnum++) {
-		ret = dns_parse_rr_srv(logopt,
-				       buffer, buffer+resp_len,
-				       &p, &dcs[rrnum]);
-		if (!ret) {
-			error(logopt,
-			      "Failed to parse answer record [%d]", rrnum);
-			free(buffer);
-			free(dcs);
-			return EBADMSG;
-		}
+	if (!srv_num) {
+		error(logopt, "no srv resource records found");
+		goto error_srvs;
 	}
-	idx = rrnum;
 
-	qsort(dcs, idx, sizeof(struct dns_rr_srv), QSORT_CAST dnssrvcmp);
+	qsort(srvs, srv_num, sizeof(struct srv_rr),
+		(int (*)(const void *, const void *)) cmp);
 
-	*dclist = dcs;
-	*numdcs = idx;
+	*dcs = srvs;
+	*dcs_count = srv_num;
 
+	return 1;
+
+error_out:
+	free(packet);
+error_srvs:
+	if (srvs)
+		free_srv_rrs(srvs, srv_num);
 	return 0;
 }
 
@@ -553,14 +337,14 @@ void free_dclist(struct dclist *dclist)
 static char *getdnsdomainname(unsigned int logopt)
 {
 	struct addrinfo hints, *ni;
-	char name[MAX_DNS_NAME_LENGTH + 1];
+	char name[MAXDNAME + 1];
 	char buf[MAX_ERR_BUF];
 	char *dnsdomain = NULL;
 	char *ptr;
 	int ret;
 
 	memset(name, 0, sizeof(name));
-	if (gethostname(name, MAX_DNS_NAME_LENGTH) == -1) {
+	if (gethostname(name, MAXDNAME) == -1) {
 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		error(logopt, "gethostname: %s", estr);
 		return NULL;
@@ -593,14 +377,12 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 {
 	LDAPURLDesc *ludlist = NULL;
 	LDAPURLDesc **ludp;
-	struct dns_rr_srv *dcs;
 	unsigned int min_ttl = MAX_TTL;
 	struct dclist *dclist = NULL;;
 	char buf[MAX_ERR_BUF];
 	char *dn_uri, *esc_uri;
 	char *domain;
 	char *list;
-	int numdcs;
 	int ret;
 
 	if (strcmp(uri, "ldap:///") && strcmp(uri, "ldaps:///")) {
@@ -679,6 +461,8 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 	list = NULL;
 	for (ludp = &ludlist; *ludp != NULL;) {
 		LDAPURLDesc *lud = *ludp;
+		struct srv_rr *dcs = NULL;
+		unsigned int numdcs = 0;
 		size_t req_len, len;
 		char *request = NULL;
 		char *tmp;
@@ -716,7 +500,7 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 		}
 
 		dclist_mutex_lock();
-		if (dns_lookup_srv(logopt, request, &dcs, &numdcs)) {
+		if (!get_srv_rrs(logopt, request, &dcs, &numdcs)) {
 			error(logopt,
 			      "DNS SRV query failed for domain %s", domain);
 			dclist_mutex_unlock();
@@ -733,7 +517,7 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 		for (i = 0; i < numdcs; i++) {
 			if (dcs[i].ttl > 0 && dcs[i].ttl < min_ttl)
 				min_ttl = dcs[i].ttl;
-			len += strlen(dcs[i].hostname);
+			len += strlen(dcs[i].name);
 			if (dcs[i].port > 0)
 				len += sizeof(":65535");
 		}
@@ -742,6 +526,8 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 		if (!tmp) {
 			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 			error(logopt, "realloc: %s", estr);
+			if (dcs)
+				free_srv_rrs(dcs, numdcs);
 			goto out_error;
 		}
 
@@ -755,13 +541,15 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 				strcat(tmp, " ");
 			strcat(tmp, lud->lud_scheme);
 			strcat(tmp, "://");
-			strcat(tmp, dcs[i].hostname);
+			strcat(tmp, dcs[i].name);
 			if (dcs[i].port > 0) {
 				char port[7];
 				ret = snprintf(port, 7, ":%d", dcs[i].port);
 				if (ret > 6) {
 					error(logopt,
 					      "invalid port: %u", dcs[i].port);
+					if (dcs)
+						free_srv_rrs(dcs, numdcs);
 					goto out_error;
 				}
 				strcat(tmp, port);
@@ -771,10 +559,14 @@ struct dclist *get_dc_list(unsigned int logopt, const char *uri)
 
 		*ludp = lud->lud_next;
 		ber_memfree(domain);
+		free_srv_rrs(dcs, numdcs);
 	}
 
 	ldap_free_urldesc(ludlist);
 
+	if (!list)
+		goto out_error;
+
 	dclist->expire = time(NULL) + min_ttl;
 	dclist->uri = list;