[PATCH] Make SRV/NS lookup routines async

Volker Lendecke Volker.Lendecke at SerNet.DE
Sat Jan 12 09:37:37 UTC 2019


Hi!

Attached find a patch that provides basic infrastructure for fully
async DNS lookups. The code as such is pretty small, the majority of
this patchset extends our autobuild infrastructure to cope with that.
The main one there is the idea of a central DNS server that just
proxies DNS requests to the different real DNS servers that we run.
With that in place, converting our SRV record lookup to routines that
avoid the synchronous res_search & friends is simple.

This is just a start. The next steps will be to push asynchrony up the
call chain, up to a point where dsgetdcname can be made fully async.

This is a case of NIH again. One way to go forward is to use c-ares.
Why did I not go there directly? I had taken a look, but when I saw
the ares_fds() routine I dismissed this completely as it seemed to
depend on select(). Much later Jeremy told me that c-ares of course
can live with poll. But at that point I was already pretty far down
into our own routines.

The NIH syndrome is not as bad as it first looks: We need the
marshalling routines in our code anyway, and we already have the basic
DNS client code around for the AD DC DNS forwarder. Adding a simple
resolv.conf reader is not really much work.

If we later decide to convert to a third party async DNS library,
ripping out our own lookup routines is not months of work if all the
callers are properly async.

Survived two private autobuilds.

Review appreciated!

Thanks, Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: 0551-370000-0, mailto:kontakt at sernet.de
Gesch.F.: Dr. Johannes Loxen und Reinhild Jung
AG Göttingen: HR-B 2816 - http://www.sernet.de
-------------- next part --------------
From 949e24dfe12ca78c099d9c40185e5918b754e5ff Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 11 Jan 2019 14:18:53 +0100
Subject: [PATCH 01/15] addns: Centralize siteless lookup fallback

We had the same logic 3 times, coalesce into one

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/addns/dnsquery.c | 89 +++++++++++++++++-----------------------------------
 1 file changed, 29 insertions(+), 60 deletions(-)

diff --git a/lib/addns/dnsquery.c b/lib/addns/dnsquery.c
index 4e2aaf4f3be..30e5a564eec 100644
--- a/lib/addns/dnsquery.c
+++ b/lib/addns/dnsquery.c
@@ -742,18 +742,41 @@ static NTSTATUS ads_dns_query_internal(TALLOC_CTX *ctx,
 				       int *numdcs )
 {
 	char *name;
-	if (sitename && strlen(sitename)) {
+	NTSTATUS status;
+	int num_srvs = 0;
+
+	if ((sitename != NULL) && (strlen(sitename) != 0)) {
 		name = talloc_asprintf(ctx, "%s._tcp.%s._sites.%s._msdcs.%s",
 				       servicename, sitename,
 				       dc_pdc_gc_domains, realm);
-	} else {
-		name = talloc_asprintf(ctx, "%s._tcp.%s._msdcs.%s",
-				servicename, dc_pdc_gc_domains, realm);
+		if (name == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		status = ads_dns_lookup_srv(ctx, name, dclist, &num_srvs);
+
+		TALLOC_FREE(name);
+
+		if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+		    NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
+			return status;
+		}
+
+		if (NT_STATUS_IS_OK(status) && (num_srvs != 0)) {
+			goto done;
+		}
 	}
-	if (!name) {
+
+	name = talloc_asprintf(ctx, "%s._tcp.%s._msdcs.%s",
+			       servicename, dc_pdc_gc_domains, realm);
+	if (name == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	return ads_dns_lookup_srv(ctx, name, dclist, numdcs);
+	status = ads_dns_lookup_srv(ctx, name, dclist, &num_srvs);
+
+done:
+	*numdcs = num_srvs; /* automatic conversion size_t->int */
+	return status;
 }
 
 /********************************************************************
@@ -775,24 +798,6 @@ NTSTATUS ads_dns_query_dcs(TALLOC_CTX *ctx,
 					sitename,
 					dclist,
 					numdcs);
-
-	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
-	    NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
-		return status;
-	}
-
-	if (sitename &&
-	    ((!NT_STATUS_IS_OK(status)) ||
-	     (NT_STATUS_IS_OK(status) && (numdcs == 0)))) {
-		/* Sitename DNS query may have failed. Try without. */
-		status = ads_dns_query_internal(ctx,
-						"_ldap",
-						"dc",
-						realm,
-						NULL,
-						dclist,
-						numdcs);
-	}
 	return status;
 }
 
@@ -815,24 +820,6 @@ NTSTATUS ads_dns_query_gcs(TALLOC_CTX *ctx,
 					sitename,
 					dclist,
 					numdcs);
-
-	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
-	    NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
-		return status;
-	}
-
-	if (sitename &&
-	    ((!NT_STATUS_IS_OK(status)) ||
-	     (NT_STATUS_IS_OK(status) && (numdcs == 0)))) {
-		/* Sitename DNS query may have failed. Try without. */
-		status = ads_dns_query_internal(ctx,
-						"_ldap",
-						"gc",
-						realm,
-						NULL,
-						dclist,
-						numdcs);
-	}
 	return status;
 }
 
@@ -857,24 +844,6 @@ NTSTATUS ads_dns_query_kdcs(TALLOC_CTX *ctx,
 					sitename,
 					dclist,
 					numdcs);
-
-	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
-	    NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_REFUSED)) {
-		return status;
-	}
-
-	if (sitename &&
-	    ((!NT_STATUS_IS_OK(status)) ||
-	     (NT_STATUS_IS_OK(status) && (numdcs == 0)))) {
-		/* Sitename DNS query may have failed. Try without. */
-		status = ads_dns_query_internal(ctx,
-						"_kerberos",
-						"dc",
-						dns_forest_name,
-						NULL,
-						dclist,
-						numdcs);
-	}
 	return status;
 }
 
-- 
2.11.0


From a4a188cb24c56896f2f8d3cbec557c24a321d5eb Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Dec 2018 14:16:38 +0100
Subject: [PATCH 02/15] dns_update: samba_dnsupdate's exit code is not an errno

This avoids confusing messages, samba_dnsupdate returns the number of
failed updates

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source4/dsdb/dns/dns_update.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source4/dsdb/dns/dns_update.c b/source4/dsdb/dns/dns_update.c
index b2b951c0561..0a1f0ac2330 100644
--- a/source4/dsdb/dns/dns_update.c
+++ b/source4/dsdb/dns/dns_update.c
@@ -327,8 +327,8 @@ static void dnsupdate_nameupdate_done(struct tevent_req *subreq)
 	TALLOC_FREE(subreq);
 
 	if (ret != 0) {
-		DBG_ERR("Failed DNS update - with error code %d: %s\n",
-			sys_errno, strerror(sys_errno));
+		DBG_ERR("Failed DNS update with exit code %d\n",
+			sys_errno);
 	} else {
 		DEBUG(3,("Completed DNS update check OK\n"));
 	}
-- 
2.11.0


From 1e4f1c9fae5b2cd3ba5d16af5f6b46bb6c913c4e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 1 Jan 2018 19:35:46 +0100
Subject: [PATCH 03/15] libcli/dns: Add resolv.conf parsing

Right now this only looks at the nameserver setting. It is initally made for
asynchronous AD DC lookup routines, where we don't need the "search", "domain"
and other settings. When we convert general "net", "smbclient" and others to
use this, we might either add "domain" handling to this code or look at
something like c-ares which already does it.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 libcli/dns/resolvconf.c                 | 123 ++++++++++++++++++++++++++++++++
 libcli/dns/resolvconf.h                 |  37 ++++++++++
 libcli/dns/resolvconftest.c             |  82 +++++++++++++++++++++
 libcli/dns/wscript_build                |   7 +-
 source3/script/tests/test_resolvconf.sh |  20 ++++++
 source3/selftest/tests.py               |   4 ++
 6 files changed, 272 insertions(+), 1 deletion(-)
 create mode 100644 libcli/dns/resolvconf.c
 create mode 100644 libcli/dns/resolvconf.h
 create mode 100644 libcli/dns/resolvconftest.c
 create mode 100755 source3/script/tests/test_resolvconf.sh

diff --git a/libcli/dns/resolvconf.c b/libcli/dns/resolvconf.c
new file mode 100644
index 00000000000..90d4e6a74b6
--- /dev/null
+++ b/libcli/dns/resolvconf.c
@@ -0,0 +1,123 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Internal DNS query structures
+ *  Copyright (C) Volker Lendecke 2018
+ *
+ *  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
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <stdio.h>
+#include <errno.h>
+#include "libcli/dns/resolvconf.h"
+#include "lib/util/memory.h"
+
+int parse_resolvconf_fp(
+	FILE *fp,
+	TALLOC_CTX *mem_ctx,
+	char ***pnameservers,
+	size_t *pnum_nameservers)
+{
+	char *line = NULL;
+	char **nameservers = NULL;
+	size_t num_nameservers = 0;
+	int ret = 0;
+
+	while (true) {
+		char *saveptr, *option, *ns;
+		char **tmp;
+		ssize_t n;
+		size_t len;
+
+		n = getline(&line, &len, fp);
+		if (n < 0) {
+			if (!feof(fp)) {
+				/* real error */
+				ret = errno;
+			}
+			break;
+		}
+		if ((n > 0) && (line[n-1] == '\n')) {
+			line[n-1] = '\0';
+		}
+
+		if ((line[0] == '#') || (line[0] == ';')) {
+			continue;
+		}
+
+		option = strtok_r(line, " \t", &saveptr);
+		if (option == NULL) {
+			continue;
+		}
+
+		if (strcmp(option, "nameserver") != 0) {
+			continue;
+		}
+
+		ns = strtok_r(NULL, " \t", &saveptr);
+		if (ns == NULL) {
+			continue;
+		}
+
+		tmp = talloc_realloc(
+			mem_ctx,
+			nameservers,
+			char *,
+			num_nameservers+1);
+		if (tmp == NULL) {
+			ret = ENOMEM;
+			break;
+		}
+		nameservers = tmp;
+
+		nameservers[num_nameservers] = talloc_strdup(nameservers, ns);
+		if (nameservers[num_nameservers] == NULL) {
+			ret = ENOMEM;
+			break;
+		}
+		num_nameservers += 1;
+	}
+
+	SAFE_FREE(line);
+
+	if (ret == 0) {
+		*pnameservers = nameservers;
+		*pnum_nameservers = num_nameservers;
+	} else {
+		TALLOC_FREE(nameservers);
+	}
+
+	return ret;
+}
+
+int parse_resolvconf(
+	const char *resolvconf,
+	TALLOC_CTX *mem_ctx,
+	char ***pnameservers,
+	size_t *pnum_nameservers)
+{
+	FILE *fp;
+	int ret;
+
+	fp = fopen(resolvconf ? resolvconf : "/etc/resolv.conf", "r");
+	if (fp == NULL) {
+		return errno;
+	}
+
+	ret = parse_resolvconf_fp(fp, mem_ctx, pnameservers, pnum_nameservers);
+
+	fclose(fp);
+
+	return ret;
+}
diff --git a/libcli/dns/resolvconf.h b/libcli/dns/resolvconf.h
new file mode 100644
index 00000000000..3ea258c3cab
--- /dev/null
+++ b/libcli/dns/resolvconf.h
@@ -0,0 +1,37 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Internal DNS query structures
+ *  Copyright (C) Volker Lendecke 2018
+ *
+ *  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
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBCLI_DNS_RESOLVCONF_H__
+#define __LIBCLI_DNS_RESOLVCONF_H__
+
+#include <talloc.h>
+#include <stdio.h>
+
+int parse_resolvconf_fp(
+	FILE *fp,
+	TALLOC_CTX *mem_ctx,
+	char ***pnameservers,
+	size_t *pnum_nameservers);
+int parse_resolvconf(
+	const char *resolvconf,
+	TALLOC_CTX *mem_ctx,
+	char ***pnameservers,
+	size_t *pnum_nameservers);
+
+#endif
diff --git a/libcli/dns/resolvconftest.c b/libcli/dns/resolvconftest.c
new file mode 100644
index 00000000000..639526443f0
--- /dev/null
+++ b/libcli/dns/resolvconftest.c
@@ -0,0 +1,82 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Internal DNS query structures
+ *  Copyright (C) Volker Lendecke 2018
+ *
+ *  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
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <talloc.h>
+#include <errno.h>
+#include "libcli/dns/resolvconf.h"
+#include "lib/util/memory.h"
+
+static int resolvconftest1(void)
+{
+	const char *content =
+		"#foo\nbar\nnameserver 1.2.3.4\nnameserver 2.3.4.5";
+	char *file;
+	FILE *fp;
+	int ret;
+	char **nameservers;
+	size_t num_nameservers;
+
+	file = strdup(content);
+	if (file == NULL) {
+		perror("strdup failed");
+		return ENOMEM;
+	}
+	fp = fmemopen(file, strlen(file), "r");
+	if (fp == NULL) {
+		perror("fmemopen failed");
+		return errno;
+	}
+
+	ret = parse_resolvconf_fp(fp, NULL, &nameservers, &num_nameservers);
+	if (ret != 0) {
+		fprintf(stderr, "parse_resolvconf_fp failed: %s\n",
+			strerror(ret));
+		return ret;
+	}
+
+	if (num_nameservers != 2) {
+		fprintf(stderr, "expected 2 nameservers, got %zu\n",
+			num_nameservers);
+		return EIO;
+	}
+	if ((strcmp(nameservers[0], "1.2.3.4") != 0) ||
+	    (strcmp(nameservers[1], "2.3.4.5") != 0)) {
+		fprintf(stderr, "got wrong nameservers\n");
+		return EIO;
+	}
+
+	TALLOC_FREE(nameservers);
+	fclose(fp);
+	SAFE_FREE(file);
+
+	return 0;
+}
+
+int main(void) {
+	int ret;
+
+	ret = resolvconftest1();
+	if (ret != 0) {
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build
index 2dcd8c17d9d..0d7c746dd80 100644
--- a/libcli/dns/wscript_build
+++ b/libcli/dns/wscript_build
@@ -1,5 +1,10 @@
 #!/usr/bin/env python
 
 bld.SAMBA_SUBSYSTEM('clidns',
-        source='dns.c',
+        source='dns.c resolvconf.c',
         public_deps='LIBTSOCKET tevent-util NDR_DNS')
+
+bld.SAMBA_BINARY('resolvconftest',
+                  source='resolvconftest.c',
+                  deps='clidns',
+                  install=False)
diff --git a/source3/script/tests/test_resolvconf.sh b/source3/script/tests/test_resolvconf.sh
new file mode 100755
index 00000000000..ffe4da9d1f4
--- /dev/null
+++ b/source3/script/tests/test_resolvconf.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+if [ ! -x $BINDIR/resolvconftest ] ; then
+    # Some machines don't have /bin/true, simulate it
+    cat >$BINDIR/resolvconftest <<EOF
+#!/bin/sh
+exit 0
+EOF
+    chmod +x $BINDIR/resolvconftest
+fi
+
+failed=0
+
+testit "resolvconf" $VALGRIND $BINDIR/resolvconftest ||
+	failed=`expr $failed + 1`
+
+testok $0 $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index c0fde3ad3f0..46f078759e1 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -406,6 +406,10 @@ if with_pthreadpool:
                                 "script/tests/test_libwbclient_threads.sh"),
                    "$DOMAIN", "$DC_USERNAME"])
 
+plantestsuite(
+    "samba3.resolvconf", "none",
+    [os.path.join(samba3srcdir, "script/tests/test_resolvconf.sh")])
+
 plantestsuite("samba3.async_req", "nt4_dc",
               [os.path.join(samba3srcdir, "script/tests/test_async_req.sh")])
 
-- 
2.11.0


From c442cad795d3d9d4ed324ce7e03fb912b07f47c2 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 2 Jan 2018 13:56:56 +0100
Subject: [PATCH 04/15] libcli/dns: Add dns_lookup

Wrapper function to parse resolv.conf and talk to multiple nameservers. This is
the code where we might want to add a "working nameserver" cache. glibc always
looks at the first configured nameserver. If that's dead, glibc runs into a
timeout and only then asks the second one that might succeed. When more than
one dns query is to be performed, these timeouts add up.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 libcli/dns/dns_lookup.c     | 326 ++++++++++++++++++++++++++++++++++++++++++++
 libcli/dns/dns_lookup.h     |  45 ++++++
 libcli/dns/dns_lookuptest.c |  55 ++++++++
 libcli/dns/wscript_build    |   9 ++
 4 files changed, 435 insertions(+)
 create mode 100644 libcli/dns/dns_lookup.c
 create mode 100644 libcli/dns/dns_lookup.h
 create mode 100644 libcli/dns/dns_lookuptest.c

diff --git a/libcli/dns/dns_lookup.c b/libcli/dns/dns_lookup.c
new file mode 100644
index 00000000000..6fc912cb8f3
--- /dev/null
+++ b/libcli/dns/dns_lookup.c
@@ -0,0 +1,326 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Internal DNS query structures
+ *  Copyright (C) Volker Lendecke 2018
+ *
+ *  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
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "libcli/dns/dns_lookup.h"
+#include "libcli/dns/resolvconf.h"
+#include "libcli/dns/libdns.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/debug.h"
+
+struct dns_lookup_state {
+	struct tevent_context *ev;
+	const char *name;
+	enum dns_qclass qclass;
+	enum dns_qtype qtype;
+
+	char **nameservers;
+	size_t num_nameservers;
+	size_t num_sent;
+
+	struct tevent_req **dns_subreqs;
+	struct tevent_req *wait_subreq;
+
+	struct dns_name_packet *reply;
+};
+
+static int dns_lookup_send_next(struct tevent_req *req);
+
+static void dns_lookup_done(struct tevent_req *subreq);
+static void dns_lookup_waited(struct tevent_req *subreq);
+
+struct tevent_req *dns_lookup_send(TALLOC_CTX *mem_ctx,
+				   struct tevent_context *ev,
+				   FILE *resolv_conf_fp,
+				   const char *name,
+				   enum dns_qclass qclass,
+				   enum dns_qtype qtype)
+{
+	struct tevent_req *req;
+	struct dns_lookup_state *state;
+	FILE *fp = resolv_conf_fp;
+	int ret;
+
+	req = tevent_req_create(mem_ctx, &state, struct dns_lookup_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->name = name;
+	state->qclass = qclass;
+	state->qtype = qtype;
+
+	if (resolv_conf_fp == NULL) {
+		fp = fopen("/etc/resolv.conf", "r");
+		if (fp == NULL) {
+			tevent_req_error(req, errno);
+			return tevent_req_post(req, ev);
+		}
+	}
+
+	ret = parse_resolvconf_fp(
+		fp,
+		state,
+		&state->nameservers,
+		&state->num_nameservers);
+
+	if (resolv_conf_fp == NULL) {
+		fclose(fp);
+	}
+
+	if (ret != 0) {
+		tevent_req_error(req, ret);
+		return tevent_req_post(req, ev);
+	}
+
+	if (state->num_nameservers == 0) {
+		/*
+		 * glibc's getaddrinfo returns EAI_AGAIN when no
+		 * nameservers are configured. EAGAIN seems closest.
+		 */
+		tevent_req_error(req, EAGAIN);
+		return tevent_req_post(req, ev);
+	}
+
+	state->dns_subreqs = talloc_zero_array(
+		state,
+		struct tevent_req *,
+		state->num_nameservers);
+
+	if (tevent_req_nomem(state->dns_subreqs, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	ret = dns_lookup_send_next(req);
+	if (tevent_req_error(req, ret)) {
+		return tevent_req_post(req, ev);
+	}
+
+	return req;
+}
+
+static int dns_lookup_send_next(struct tevent_req *req)
+{
+	struct dns_lookup_state *state = tevent_req_data(
+		req, struct dns_lookup_state);
+
+	DBG_DEBUG("Sending DNS request #%zu to %s\n",
+		  state->num_sent,
+		  state->nameservers[state->num_sent]);
+
+	state->dns_subreqs[state->num_sent] = dns_cli_request_send(
+		state->dns_subreqs,
+		state->ev,
+		state->nameservers[state->num_sent],
+		state->name,
+		state->qclass,
+		state->qtype);
+
+	if (state->dns_subreqs[state->num_sent] == NULL) {
+		return ENOMEM;
+	}
+	tevent_req_set_callback(state->dns_subreqs[state->num_sent],
+				dns_lookup_done,
+				req);
+	state->num_sent += 1;
+
+	if (state->num_sent == state->num_nameservers) {
+		/*
+		 * No more nameservers left
+		 */
+		DBG_DEBUG("cancelling wait_subreq\n");
+		TALLOC_FREE(state->wait_subreq);
+		return 0;
+	}
+
+	if (state->wait_subreq != NULL) {
+		/*
+		 * This can happen if we fire the next request upon
+		 * dns_cli_request returning a network-level error
+		 */
+		return 0;
+	}
+
+	state->wait_subreq = tevent_wakeup_send(
+		state,
+		state->ev,
+		tevent_timeval_current_ofs(1, 0));
+	if (state->wait_subreq == NULL) {
+		return ENOMEM;
+	}
+	tevent_req_set_callback(state->wait_subreq, dns_lookup_waited, req);
+
+	return 0;
+}
+
+static void dns_lookup_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct dns_lookup_state *state = tevent_req_data(
+		req, struct dns_lookup_state);
+	int dns_cli_request_ret;
+	size_t i;
+
+	dns_cli_request_ret = dns_cli_request_recv(
+		subreq,
+		state,
+		&state->reply);
+
+	for (i = 0; i < state->num_nameservers; i++) {
+		if (state->dns_subreqs[i] == subreq) {
+			break;
+		}
+	}
+
+	TALLOC_FREE(subreq);
+
+	if (i == state->num_nameservers) {
+		/* should never happen */
+		DBG_WARNING("Failed to find subreq");
+		tevent_req_error(req, EINVAL);
+		return;
+	}
+	state->dns_subreqs[i] = NULL;
+
+	if (dns_cli_request_ret == 0) {
+		/*
+		 * Success, cancel everything else
+		 */
+		TALLOC_FREE(state->dns_subreqs);
+		TALLOC_FREE(state->wait_subreq);
+		tevent_req_done(req);
+		return;
+	}
+
+	DBG_DEBUG("dns_cli_request[%zu] returned %s\n", i,
+		  strerror(dns_cli_request_ret));
+
+	if (state->num_sent < state->num_nameservers) {
+		/*
+		 * We have a nameserver left to try
+		 */
+		int ret;
+
+		ret = dns_lookup_send_next(req);
+		if (tevent_req_error(req, ret)) {
+			return;
+		}
+	}
+
+	DBG_DEBUG("looking for outstanding requests\n");
+
+	for (i = 0; i<state->num_nameservers; i++) {
+		if (state->dns_subreqs[i] != NULL) {
+			break;
+		}
+	}
+
+	DBG_DEBUG("i=%zu, num_nameservers=%zu\n",
+		  i, state->num_nameservers);
+
+	if (i == state->num_nameservers) {
+		/*
+		 * Report the lower-level error if we have nothing
+		 * outstanding anymore
+		 */
+		tevent_req_error(req, dns_cli_request_ret);
+		return;
+	}
+
+	/*
+	 * Do nothing: We have other nameservers that might come back
+	 * with something good.
+	 */
+}
+
+static void dns_lookup_waited(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct dns_lookup_state *state = tevent_req_data(
+		req, struct dns_lookup_state);
+	int ret;
+	bool ok;
+
+	DBG_DEBUG("waited\n");
+
+	ok = tevent_wakeup_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (!ok) {
+		tevent_req_oom(req);
+		return;
+	}
+	state->wait_subreq = NULL;
+
+	ret = dns_lookup_send_next(req);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+
+	/*
+	 * dns_lookup_send_next() has already triggered the next wakeup
+	 */
+}
+
+int dns_lookup_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+		    struct dns_name_packet **reply)
+{
+	struct dns_lookup_state *state = tevent_req_data(
+		req, struct dns_lookup_state);
+	int err;
+
+	if (tevent_req_is_unix_error(req, &err)) {
+		return err;
+	}
+
+	*reply = talloc_move(mem_ctx, &state->reply);
+
+	tevent_req_received(req);
+	return 0;
+}
+
+int dns_lookup(FILE *resolv_conf_fp,
+	       const char *name,
+	       enum dns_qclass qclass,
+	       enum dns_qtype qtype,
+	       TALLOC_CTX *mem_ctx,
+	       struct dns_name_packet **reply)
+{
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	int ret = ENOMEM;
+
+	ev = samba_tevent_context_init(mem_ctx);
+	if (ev == NULL) {
+		goto fail;
+	}
+	req = dns_lookup_send(ev, ev, resolv_conf_fp, name, qclass, qtype);
+	if (req == NULL) {
+		goto fail;
+	}
+	if (!tevent_req_poll_unix(req, ev, &ret)) {
+		goto fail;
+	}
+	ret = dns_lookup_recv(req, mem_ctx, reply);
+fail:
+	TALLOC_FREE(ev);
+	return ret;
+}
diff --git a/libcli/dns/dns_lookup.h b/libcli/dns/dns_lookup.h
new file mode 100644
index 00000000000..157a3d252c1
--- /dev/null
+++ b/libcli/dns/dns_lookup.h
@@ -0,0 +1,45 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Internal DNS query structures
+ *  Copyright (C) Volker Lendecke 2018
+ *
+ *  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
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBCLI_DNS_DNS_LOOKUP_H__
+#define __LIBCLI_DNS_DNS_LOOKUP_H__
+
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "librpc/gen_ndr/dns.h"
+
+struct tevent_req *dns_lookup_send(TALLOC_CTX *mem_ctx,
+				   struct tevent_context *ev,
+				   FILE *resolv_conf_fp,
+				   const char *name,
+				   enum dns_qclass qclass,
+				   enum dns_qtype qtype);
+int dns_lookup_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+		    struct dns_name_packet **reply);
+int dns_lookup(FILE *resolv_conf_fp,
+	       const char *name,
+	       enum dns_qclass qclass,
+	       enum dns_qtype qtype,
+	       TALLOC_CTX *mem_ctx,
+	       struct dns_name_packet **reply);
+
+#endif
diff --git a/libcli/dns/dns_lookuptest.c b/libcli/dns/dns_lookuptest.c
new file mode 100644
index 00000000000..c8e03439773
--- /dev/null
+++ b/libcli/dns/dns_lookuptest.c
@@ -0,0 +1,55 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Internal DNS query structures
+ *  Copyright (C) Volker Lendecke 2018
+ *
+ *  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
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <talloc.h>
+#include <errno.h>
+#include "libcli/dns/dns_lookup.h"
+#include "lib/util/debug.h"
+
+static int dns_lookuptest1(void)
+{
+	struct dns_name_packet *reply = NULL;
+	int ret;
+
+	ret = dns_lookup(NULL, "www.samba.org", DNS_QCLASS_IN, DNS_QTYPE_A,
+			 NULL, &reply);
+	if (ret != 0) {
+		fprintf(stderr, "dns_lookup failed: %s\n", strerror(ret));
+		return ret;
+	}
+
+	TALLOC_FREE(reply);
+	return 0;
+}
+
+int main(int argc, const char *argv[]) {
+	int ret;
+
+	setup_logging(argv[0], DEBUG_DEFAULT_STDERR);
+	debug_parse_levels("10");
+
+	ret = dns_lookuptest1();
+	if (ret != 0) {
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build
index 0d7c746dd80..d06f84d3cc3 100644
--- a/libcli/dns/wscript_build
+++ b/libcli/dns/wscript_build
@@ -8,3 +8,12 @@ bld.SAMBA_BINARY('resolvconftest',
                   source='resolvconftest.c',
                   deps='clidns',
                   install=False)
+
+bld.SAMBA_SUBSYSTEM('dns_lookup',
+                    source='dns_lookup.c',
+                    public_deps='clidns')
+
+bld.SAMBA_BINARY('dns_lookuptest',
+                  source='dns_lookuptest.c',
+                  deps='dns_lookup',
+                  install=False)
-- 
2.11.0


From 52c5ce9d12b2e85927c873f4c0fba13408c1eb93 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 4 Jan 2018 20:58:05 +0100
Subject: [PATCH 05/15] dns_lookup: Let make test override the resolv.conf
 location

Make this a separate commit: That is the feature that libc unfortunately does
not give us.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 libcli/dns/dns_lookup.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/libcli/dns/dns_lookup.c b/libcli/dns/dns_lookup.c
index 6fc912cb8f3..b99ab426e10 100644
--- a/libcli/dns/dns_lookup.c
+++ b/libcli/dns/dns_lookup.c
@@ -68,7 +68,18 @@ struct tevent_req *dns_lookup_send(TALLOC_CTX *mem_ctx,
 	state->qtype = qtype;
 
 	if (resolv_conf_fp == NULL) {
-		fp = fopen("/etc/resolv.conf", "r");
+		const char *resolvconf = "/etc/resolv.conf";
+
+#ifdef DEVELOPER
+		{
+			const char *envvar = getenv("RESOLV_CONF");
+			if (envvar != NULL) {
+				resolvconf = envvar;
+			}
+		}
+#endif
+
+		fp = fopen(resolvconf, "r");
 		if (fp == NULL) {
 			tevent_req_error(req, errno);
 			return tevent_req_post(req, ev);
-- 
2.11.0


From e74c6ed953405d31b592d71913bbcca14ebaf2f3 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 4 Jan 2018 17:06:53 +0100
Subject: [PATCH 06/15] libcli/dns: Make "clidns" a library

This will be linked into the SAMBA_LIBRARY "addns" in the next step. Because
the other user, "dnsserver_common", is also a library, we can't link this as a
subsystem anymore.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 libcli/dns/wscript_build | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build
index d06f84d3cc3..0def78e2e1d 100644
--- a/libcli/dns/wscript_build
+++ b/libcli/dns/wscript_build
@@ -1,8 +1,9 @@
 #!/usr/bin/env python
 
-bld.SAMBA_SUBSYSTEM('clidns',
+bld.SAMBA_LIBRARY('clidns',
         source='dns.c resolvconf.c',
-        public_deps='LIBTSOCKET tevent-util NDR_DNS')
+        public_deps='LIBTSOCKET tevent-util NDR_DNS',
+        private_library=True)
 
 bld.SAMBA_BINARY('resolvconftest',
                   source='resolvconftest.c',
-- 
2.11.0


From 6cb64102d390610316096705ac48f166ae539e00 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 4 Jan 2018 16:05:35 +0100
Subject: [PATCH 07/15] libcli/dns: clidns must depend on ndr_standard, not on
 NDR_DNS

Otherwise we can't link this into other libraries

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 libcli/dns/wscript_build | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build
index 0def78e2e1d..ef1c34ffd2f 100644
--- a/libcli/dns/wscript_build
+++ b/libcli/dns/wscript_build
@@ -2,7 +2,7 @@
 
 bld.SAMBA_LIBRARY('clidns',
         source='dns.c resolvconf.c',
-        public_deps='LIBTSOCKET tevent-util NDR_DNS',
+        public_deps='LIBTSOCKET tevent-util ndr-standard',
         private_library=True)
 
 bld.SAMBA_BINARY('resolvconftest',
-- 
2.11.0


From 0723678943556270126a462d183040580ea07ac5 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 10 Jan 2019 16:54:41 +0100
Subject: [PATCH 08/15] libcli/dns: Add dns_res_rec_get_sockaddr

Pull the address from a res_rec if it's there

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 libcli/dns/dns_lookup.c | 37 +++++++++++++++++++++++++++++++++++++
 libcli/dns/dns_lookup.h |  3 +++
 2 files changed, 40 insertions(+)

diff --git a/libcli/dns/dns_lookup.c b/libcli/dns/dns_lookup.c
index b99ab426e10..7402d443fba 100644
--- a/libcli/dns/dns_lookup.c
+++ b/libcli/dns/dns_lookup.c
@@ -335,3 +335,40 @@ fail:
 	TALLOC_FREE(ev);
 	return ret;
 }
+
+bool dns_res_rec_get_sockaddr(const struct dns_res_rec *rec,
+			      struct sockaddr_storage *addr)
+{
+	sa_family_t family;
+	const char *src;
+	void *dst;
+	int ret;
+
+	switch (rec->rr_type) {
+	    case DNS_QTYPE_A:
+		    family = AF_INET;
+		    src = rec->rdata.ipv4_record;
+		    dst = &(((struct sockaddr_in *)addr)->sin_addr);
+		    break;
+#ifdef HAVE_IPV6
+	    case DNS_QTYPE_AAAA:
+		    family = AF_INET6;
+		    src = rec->rdata.ipv6_record;
+		    dst = &(((struct sockaddr_in6 *)addr)->sin6_addr);
+		    break;
+#endif
+	    default:
+		    /* We only care about IP addresses */
+		    return false;
+	}
+
+	*addr = (struct sockaddr_storage) { .ss_family = family };
+
+	ret = inet_pton(family, src, dst);
+	if (ret != 1) {
+		DBG_DEBUG("inet_pton(%s) failed\n", src);
+		return false;
+	}
+
+	return true;
+}
diff --git a/libcli/dns/dns_lookup.h b/libcli/dns/dns_lookup.h
index 157a3d252c1..0c05e0490c0 100644
--- a/libcli/dns/dns_lookup.h
+++ b/libcli/dns/dns_lookup.h
@@ -42,4 +42,7 @@ int dns_lookup(FILE *resolv_conf_fp,
 	       TALLOC_CTX *mem_ctx,
 	       struct dns_name_packet **reply);
 
+bool dns_res_rec_get_sockaddr(const struct dns_res_rec *rec,
+			      struct sockaddr_storage *addr);
+
 #endif
-- 
2.11.0


From e8a246432d1658f735bee7af2b7e5bf0cbc6a625 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 12 Jan 2018 15:53:03 +0100
Subject: [PATCH 09/15] selftest: add central dns forwarder

This is a small DNS server that has hard redirects to the different domain
controllers based on domain names. This is required because future commits will
avoid calling into libresolv's code which resolv_wrapper takes care of.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 .../samba/tests/dns_forwarder_helpers/dns_hub.py   | 146 +++++++++++++++++++++
 1 file changed, 146 insertions(+)
 create mode 100755 python/samba/tests/dns_forwarder_helpers/dns_hub.py

diff --git a/python/samba/tests/dns_forwarder_helpers/dns_hub.py b/python/samba/tests/dns_forwarder_helpers/dns_hub.py
new file mode 100755
index 00000000000..42891f741cf
--- /dev/null
+++ b/python/samba/tests/dns_forwarder_helpers/dns_hub.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+#
+# Unix SMB/CIFS implementation.
+# Copyright (C) Volker Lendecke 2017
+#
+# 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
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Based on the EchoServer example from python docs
+
+import threading
+import sys
+import os
+import select
+import socket
+from samba.dcerpc import dns
+from samba.tests.dns_base import DNSTest
+import samba.ndr as ndr
+
+if sys.version_info[0] < 3:
+    import SocketServer
+    sserver = SocketServer
+else:
+    import socketserver
+    sserver = socketserver
+
+class DnsHandler(sserver.BaseRequestHandler):
+    def dns_transaction_udp(self, packet, host):
+        "send a DNS query and read the reply"
+        s = None
+        try:
+            send_packet = ndr.ndr_pack(packet)
+            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+            s.settimeout(5)
+            s.connect((host, 53))
+            s.sendall(send_packet, 0)
+            recv_packet = s.recv(2048, 0)
+            return ndr.ndr_unpack(dns.name_packet, recv_packet)
+        except socket.error as err:
+            print("Error sending to host %s for name %s: %s\n" %
+                  (host, packet.questions[0].name, err.errno))
+            raise
+        finally:
+            if s is not None:
+                s.close()
+        return None
+
+    def forwarder(self, name):
+        lname = name.lower()
+
+        if lname.endswith('an-address-that-will-not-resolve'):
+            return 'ignore'
+        if lname.endswith('dsfsdfs'):
+            return 'fail'
+        if lname.endswith('adnonssdom.samba.example.com'):
+            return '127.0.0.17'
+        if lname.endswith('adnontlmdom.samba.example.com'):
+            return '127.0.0.18'
+        if lname.endswith('samba2000.example.com'):
+            return '127.0.0.25'
+        if lname.endswith('samba2003.example.com'):
+            return '127.0.0.26'
+        if lname.endswith('samba2008r2.example.com'):
+            return '127.0.0.27'
+        if lname.endswith('addom.samba.example.com'):
+            return '127.0.0.30'
+        if lname.endswith('sub.samba.example.com'):
+            return '127.0.0.31'
+        if lname.endswith('chgdcpassword.samba.example.com'):
+            return '127.0.0.32'
+        if lname.endswith('backupdom.samba.example.com'):
+            return '127.0.0.40'
+        if lname.endswith('renamedom.samba.example.com'):
+            return '127.0.0.42'
+        if lname.endswith('labdom.samba.example.com'):
+            return '127.0.0.43'
+        if lname.endswith('samba.example.com'):
+            return '127.0.0.21'
+        return None
+
+    def handle(self):
+        data, socket = self.request
+        query = ndr.ndr_unpack(dns.name_packet, data);
+        name = query.questions[0].name
+        forwarder = self.forwarder(name)
+        response = None
+
+        if forwarder is 'ignore':
+            return
+        elif forwarder is 'fail':
+            pass
+        elif forwarder is not None:
+            response = self.dns_transaction_udp(query, forwarder)
+        else:
+            response = query
+            response.operation |= dns.DNS_FLAG_REPLY
+            response.operation |= dns.DNS_FLAG_RECURSION_AVAIL
+            response.operation |= dns.DNS_RCODE_NXDOMAIN
+
+        if response is None:
+            response = query
+            response.operation |= dns.DNS_FLAG_REPLY
+            response.operation |= dns.DNS_FLAG_RECURSION_AVAIL
+            response.operation |= dns.DNS_RCODE_SERVFAIL
+
+        send_packet = ndr.ndr_pack(response)
+
+        socket.sendto(send_packet, self.client_address)
+
+class server_thread(threading.Thread):
+    def __init__(self, server):
+        threading.Thread.__init__(self)
+        self.server = server
+
+    def run(self):
+        self.server.serve_forever()
+        print("dns_hub: after serve_forever()")
+
+def main():
+    timeout = int(sys.argv[1])*1000
+    timeout = min(timeout, 2**31-1) # poll with 32-bit int can't take more
+    host = sys.argv[2]
+    server = sserver.UDPServer((host, int(53)), DnsHandler)
+    t = server_thread(server)
+    t.start()
+    p = select.poll()
+    stdin = sys.stdin.fileno()
+    p.register(stdin, select.POLLIN)
+    p.poll(timeout)
+    print("dns_hub: after poll()")
+    server.shutdown()
+    t.join()
+    print("dns_hub: before exit()")
+    sys.exit(0)
+
+main()
-- 
2.11.0


From 09985bc22624ae2e2d06886b07afa2b8f533ecf6 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 6 Feb 2018 09:46:41 +0100
Subject: [PATCH 10/15] selftest: setup_dns_hub

Start the central dns forwarder on interface 64

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/target/Samba.pm  |   2 +
 selftest/target/Samba4.pm | 109 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+)

diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm
index 4820f987fd7..3fe53f94a2c 100644
--- a/selftest/target/Samba.pm
+++ b/selftest/target/Samba.pm
@@ -427,6 +427,8 @@ sub get_interface($)
     $interfaces{"prockilldc"} = 46;
     $interfaces{"proclimitdc"} = 47;
 
+    $interfaces{"rootdnsforwarder"} = 64;
+
     # update lib/socket_wrapper/socket_wrapper.c
     #  #define MAX_WRAPPED_INTERFACES 64
     # if you wish to have more than 64 interfaces
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 87c9f7dc4f2..44c7b46b126 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -16,6 +16,7 @@ use SocketWrapper;
 use target::Samba;
 use target::Samba3;
 use Archive::Tar;
+use File::Path 'make_path';
 
 sub new($$$$$) {
 	my ($classname, $bindir, $ldap, $srcdir, $server_maxtime) = @_;
@@ -329,6 +330,114 @@ sub mk_openldap($$)
 	return ($slapd_conf_d, $pidfile);
 }
 
+sub setup_dns_hub_internal($$$)
+{
+	my ($self, $hostname, $prefix) = @_;
+	my $STDIN_READER;
+
+	unless(-d $prefix or make_path($prefix, 0777)) {
+		warn("Unable to create $prefix");
+		return undef;
+	}
+	my $prefix_abs = abs_path($prefix);
+
+	die ("prefix=''") if $prefix_abs eq "";
+	die ("prefix='/'") if $prefix_abs eq "/";
+
+	unless (system("rm -rf $prefix_abs/*") == 0) {
+		warn("Unable to clean up");
+	}
+
+	my $swiface = Samba::get_interface($hostname);
+
+	my $env = undef;
+	$env->{prefix} = $prefix;
+	$env->{prefix_abs} = $prefix_abs;
+
+	$env->{hostname} = $hostname;
+	$env->{swiface} = $swiface;
+
+	$env->{ipv4} = "127.0.0.$swiface";
+	$env->{ipv6} = sprintf("fd00:0000:0000:0000:0000:0000:5357:5f%02x", $swiface);
+
+	$env->{DNS_HUB_LOG} = "$prefix_abs/dns_hub.log";
+
+	$env->{RESOLV_CONF} = "$prefix_abs/resolv.conf";
+
+	open(RESOLV_CONF, ">$env->{RESOLV_CONF}");
+	print RESOLV_CONF "nameserver $env->{ipv4}\n";
+	print RESOLV_CONF "nameserver $env->{ipv6}\n";
+	close(RESOLV_CONF);
+
+	# use a pipe for stdin in the child processes. This allows
+	# those processes to monitor the pipe for EOF to ensure they
+	# exit when the test script exits
+	pipe($STDIN_READER, $env->{STDIN_PIPE});
+
+	print "STARTING rootdnsforwarder...\n";
+	my $pid = fork();
+	if ($pid == 0) {
+		# we want out from samba to go to the log file, but also
+		# to the users terminal when running 'make test' on the command
+		# line. This puts it on stderr on the terminal
+		open STDOUT, "| tee $env->{DNS_HUB_LOG} 1>&2";
+		open STDERR, '>&STDOUT';
+
+		SocketWrapper::set_default_iface($swiface);
+		my $pcap_file = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/env-$hostname$.pcap";
+		SocketWrapper::setup_pcap($pcap_file);
+
+		my @preargs = ();
+		my @args = ();
+		my @optargs = ();
+		if (!defined($ENV{PYTHON})) {
+		    push (@preargs, "env");
+		    push (@preargs, "python");
+		} else {
+		    push (@preargs, $ENV{PYTHON});
+		}
+		$ENV{MAKE_TEST_BINARY} = Samba::bindir_path($self, "python/samba/tests/dns_forwarder_helpers/dns_hub.py");
+		push (@args, "$self->{server_maxtime}");
+		push (@args, "$env->{ipv4}");
+		close($env->{STDIN_PIPE});
+		open STDIN, ">&", $STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
+
+		exec(@preargs, $ENV{MAKE_TEST_BINARY}, @args, @optargs)
+			or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
+	}
+	$env->{SAMBA_PID} = $pid;
+	$env->{KRB5_CONFIG} = "${prefix_abs}/no_krb5.conf";
+	close($STDIN_READER);
+
+	print "DONE\n";
+	return $env;
+}
+
+sub setup_dns_hub
+{
+	my ($self, $prefix) = @_;
+
+	my $hostname = "rootdnsforwarder";
+
+	my $env = $self->setup_dns_hub_internal("$hostname", "$prefix/$hostname");
+
+	$self->{dns_hub_env} = $env;
+
+	return $env;
+}
+
+sub get_dns_hub_env($)
+{
+	my ($self, $prefix) = @_;
+
+	if (defined($self->{dns_hub_env})) {
+	        return $self->{dns_hub_env};
+	}
+
+	die("get_dns_hub_env() not setup 'dns_hub_env'");
+	return undef;
+}
+
 sub setup_namespaces($$:$$)
 {
 	my ($self, $localenv, $upn_array, $spn_array) = @_;
-- 
2.11.0


From 9d1be307e42ee9d35f1e1b44673d611546949f61 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 2 Jan 2019 14:18:44 +0100
Subject: [PATCH 11/15] selftest: Add dns_hub deps

All the DCs want the dns forwarder

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 selftest/target/Samba4.pm | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 44c7b46b126..b4e155ad7ff 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -2315,17 +2315,18 @@ sub check_env($$)
 #   samba4->setup_$envname($self, $path, $dep_1_vars, $dep_2_vars, ...)
 %Samba4::ENV_DEPS = (
 	# name               => [dep_1, dep_2, ...],
-	ad_dc_ntvfs          => [],
-	ad_dc                => [],
-	ad_dc_no_nss         => [],
-	ad_dc_no_ntlm        => [],
-	backupfromdc         => [],
-	customdc             => [],
-	preforkrestartdc     => [],
+	dns_hub              => [],
+	ad_dc_ntvfs          => ["dns_hub"],
+	ad_dc                => ["dns_hub"],
+	ad_dc_no_nss         => ["dns_hub"],
+	ad_dc_no_ntlm        => ["dns_hub"],
+	backupfromdc         => ["dns_hub"],
+	customdc             => ["dns_hub"],
+	preforkrestartdc     => ["dns_hub"],
 
 	fl2008r2dc           => ["ad_dc"],
 	fl2003dc             => ["ad_dc"],
-	fl2000dc             => [],
+	fl2000dc             => ["dns_hub"],
 
 	vampire_2000_dc      => ["fl2000dc"],
 	vampire_dc           => ["ad_dc_ntvfs"],
@@ -2334,7 +2335,7 @@ sub check_env($$)
 
 	rodc                 => ["ad_dc_ntvfs"],
 	rpc_proxy            => ["ad_dc_ntvfs"],
-	chgdcpass            => [],
+	chgdcpass            => ["dns_hub"],
 
 	s4member_dflt_domain => ["ad_dc_ntvfs"],
 	s4member             => ["ad_dc_ntvfs"],
-- 
2.11.0


From 3c48ca5dd0e33504652e7d1e93257d40ca36eacb Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 2 Jan 2019 21:24:34 +0100
Subject: [PATCH 12/15] selftest: Use dns_hub's resolv.conf

Pass it as RESOLV_CONF envvar everywhere

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 selftest/target/Samba3.pm |  4 ++++
 selftest/target/Samba4.pm | 31 +++++++++++++++++++++++--------
 2 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index a86a37aa8f0..49bdd2ac885 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -451,6 +451,7 @@ sub setup_ad_member
 	Samba::mk_krb5_conf($ctx, "");
 
 	$ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
+	$ret->{RESOLV_CONF} = $dcvars->{RESOLV_CONF};
 
 	my $net = Samba::bindir_path($self, "net");
 	# Add hosts file for name lookups
@@ -546,6 +547,7 @@ sub setup_ad_member_rfc2307
 	Samba::mk_krb5_conf($ctx, "");
 
 	$ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
+	$ret->{RESOLV_CONF} = $dcvars->{RESOLV_CONF};
 
 	my $net = Samba::bindir_path($self, "net");
 	# Add hosts file for name lookups
@@ -636,6 +638,7 @@ sub setup_ad_member_idmap_rid
 	Samba::mk_krb5_conf($ctx, "");
 
 	$ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
+	$ret->{RESOLV_CONF} = $dcvars->{RESOLV_CONF};
 
 	my $net = Samba::bindir_path($self, "net");
 	# Add hosts file for name lookups
@@ -724,6 +727,7 @@ sub setup_ad_member_idmap_ad
 	Samba::mk_krb5_conf($ctx, "");
 
 	$ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
+	$ret->{RESOLV_CONF} = $dcvars->{RESOLV_CONF};
 
 	my $net = Samba::bindir_path($self, "net");
 	# Add hosts file for name lookups
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index b4e155ad7ff..5346cb172df 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -146,6 +146,7 @@ sub check_or_start($$$)
 		} else {
 			$ENV{RESOLV_WRAPPER_HOSTS} = $env_vars->{RESOLV_WRAPPER_HOSTS};
 		}
+		$ENV{RESOLV_CONF} = $env_vars->{RESOLV_CONF};
 
 		$ENV{UID_WRAPPER} = "1";
 		$ENV{UID_WRAPPER_ROOT} = "1";
@@ -239,6 +240,7 @@ sub wait_for_start($$)
 		} else {
 			$cmd .= "RESOLV_WRAPPER_HOSTS='$testenv_vars->{RESOLV_WRAPPER_HOSTS}' ";
 		}
+		$cmd .= "RESOLV_CONF='$testenv_vars->{RESOLV_CONF}' ";
 
 		$cmd .= "$ldbsearch ";
 		$cmd .= "$testenv_vars->{CONFIGURATION} ";
@@ -465,6 +467,7 @@ sub setup_namespaces($$:$$)
 	}
 	$cmd_env .= " KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
 	$cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
+	$cmd_env .= "RESOLV_CONF=\"$localenv->{RESOLV_CONF}\" ";
 
 	my $cmd_config = " $localenv->{CONFIGURATION}";
 
@@ -505,6 +508,7 @@ sub setup_trust($$$$$)
 	}
 	$cmd_env .= " KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
 	$cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
+	$cmd_env .= "RESOLV_CONF=\"$localenv->{RESOLV_CONF}\" ";
 
 	my $cmd_config = " $localenv->{CONFIGURATION}";
 	my $cmd_creds = $cmd_config;
@@ -629,7 +633,9 @@ sub provision_raw_prepare($$$$$$$$$$$$)
 		$ctx->{samba_dnsupdate} = $python_cmd .  $ctx->{samba_dnsupdate};
 		$ctx->{use_resolv_wrapper} = 1;
 	}
-	$ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf";
+
+	my $dns_hub = $self->get_dns_hub_env();
+	$ctx->{resolv_conf} = $dns_hub->{RESOLV_CONF};
 
 	$ctx->{tlsdir} = "$ctx->{privatedir}/tls";
 
@@ -657,6 +663,7 @@ sub provision_raw_prepare($$$$$$$$$$$$)
 	push (@provision_options, "NSS_WRAPPER_HOSTNAME=\"$ctx->{nsswrap_hostname}\"");
 	if (defined($ctx->{use_resolv_wrapper})) {
 		push (@provision_options, "RESOLV_WRAPPER_CONF=\"$ctx->{resolv_conf}\"");
+		push (@provision_options, "RESOLV_CONF=\"$ctx->{resolv_conf}\"");
 	} else {
 		push (@provision_options, "RESOLV_WRAPPER_HOSTS=\"$ctx->{dns_host_file}\"");
 	}
@@ -853,13 +860,6 @@ nogroup:x:65534:nobody
 	}
 	close(HOSTS);
 
-	if (defined($ctx->{resolv_conf})) {
-		open(RESOLV_CONF, ">$ctx->{resolv_conf}");
-		print RESOLV_CONF "nameserver $ctx->{kdc_ipv4}\n";
-		print RESOLV_CONF "nameserver $ctx->{kdc_ipv6}\n";
-		close(RESOLV_CONF);
-	}
-
 	my $configuration = "--configfile=$ctx->{smb_conf}";
 
 #Ensure the config file is valid before we start
@@ -1283,6 +1283,7 @@ rpc_server:tcpip = no
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} member";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD}";
@@ -1365,6 +1366,7 @@ sub provision_rpc_proxy($$$)
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} member";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD}";
@@ -1379,6 +1381,7 @@ sub provision_rpc_proxy($$$)
 	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$dcvars->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
 	$cmd .= "KRB5_CONFIG=\"$dcvars->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$dcvars->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool delegation for-any-protocol '$ret->{NETBIOSNAME}\$' on";
         $cmd .= " $dcvars->{CONFIGURATION}";
         print $cmd;
@@ -1393,6 +1396,7 @@ sub provision_rpc_proxy($$$)
 	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$dcvars->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
 	$cmd .= "KRB5_CONFIG=\"$dcvars->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$dcvars->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool delegation add-service '$ret->{NETBIOSNAME}\$' cifs/$dcvars->{SERVER}";
         $cmd .= " $dcvars->{CONFIGURATION}";
 
@@ -1468,6 +1472,7 @@ sub provision_promoted_dc($$$)
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} MEMBER --realm=$dcvars->{REALM}";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD}";
@@ -1487,6 +1492,7 @@ sub provision_promoted_dc($$$)
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain dcpromo $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD} --use-ntvfs --dns-backend=BIND9_DLZ";
@@ -1570,6 +1576,7 @@ sub provision_vampire_dc($$$)
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} --domain-critical-only";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD} --use-ntvfs";
@@ -1652,6 +1659,7 @@ sub provision_subdom_dc($$$)
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $ctx->{dnsname} subdomain ";
 	$cmd .= "--parent-domain=$dcvars->{REALM} -U$dcvars->{DC_USERNAME}\@$dcvars->{REALM}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD} --use-ntvfs";
@@ -1939,6 +1947,7 @@ sub provision_rodc($$$)
 	}
 	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} RODC";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --server=$dcvars->{DC_SERVER} --use-ntvfs";
@@ -1953,6 +1962,7 @@ sub provision_rodc($$$)
 	my $testallowed_account = "testallowed account";
 	$cmd = "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
 	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
 	$cmd .= "$samba_tool rodc preload '$testallowed_account' $ret->{CONFIGURATION}";
 	$cmd .= " --server=$dcvars->{DC_SERVER}";
 
@@ -2510,6 +2520,7 @@ sub setup_generic_vampire_dc
 		}
 		$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
 		$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+		$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 		$cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}";
 		$cmd .= " $dc_vars->{CONFIGURATION}";
 		$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2537,6 +2548,7 @@ sub setup_generic_vampire_dc
 		}
 		$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
 		$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+		$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 		$cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}";
 		$cmd .= " $dc_vars->{CONFIGURATION}";
 		$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2578,6 +2590,7 @@ sub setup_promoted_dc
 		$cmd = "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\"";
 		$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
 		$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+		$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 		$cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}";
 		$cmd .= " $dc_vars->{CONFIGURATION}";
 		$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2620,6 +2633,7 @@ sub setup_subdom_dc
 		$cmd = "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\"";
 		$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
 		$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+		$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 		$cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SUBDOM_DC_SERVER}";
 		$cmd .= " $dc_vars->{CONFIGURATION}";
 		$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD} --realm=$dc_vars->{DC_REALM}";
@@ -2662,6 +2676,7 @@ sub setup_rodc
 	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\"";
 	$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
 	$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+	$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 	$cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}";
 	$cmd .= " $dc_vars->{CONFIGURATION}";
 	$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
-- 
2.11.0


From f5f0b5740551350c504592b858a0ef4b02894bd4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 3 Jan 2019 16:44:45 +0100
Subject: [PATCH 13/15] samba_dnsupdate: With dns_hub, we don't need
 resolv_wrap

Best viewed with git show -b

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source4/scripting/bin/samba_dnsupdate | 37 ++++++++++++-----------------------
 1 file changed, 12 insertions(+), 25 deletions(-)

diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate
index b22dde8360b..74f10427b4e 100755
--- a/source4/scripting/bin/samba_dnsupdate
+++ b/source4/scripting/bin/samba_dnsupdate
@@ -476,32 +476,19 @@ def call_nsupdate(d, op="add"):
     (tmp_fd, tmpfile) = tempfile.mkstemp()
     f = os.fdopen(tmp_fd, 'w')
 
-    # Getting this line right is really important.  When we are under
-    # resolv_wrapper, then we want to use RESOLV_CONF and the
-    # nameserver therein. The issue is that this parameter forces us
-    # to only ever use that server, and not some other server that the
-    # NS record may point to, even as we get a ticket to that other
-    # server.
-    #
-    # Therefore we must not set this in production, instead we want
-    # to find the name of a SOA for the zone and use that server.
+    resolver = get_resolver(d)
 
-    if os.getenv('RESOLV_CONF') and d.nameservers != []:
-        f.write('server %s\n' % d.nameservers[0])
-    else:
-        resolver = get_resolver(d)
-
-        # Local the zone for this name
-        zone = dns.resolver.zone_for_name(normalised_name,
-                                          resolver=resolver)
-
-        # Now find the SOA, or if we can't get a ticket to the SOA,
-        # any server with an NS record we can get a ticket for.
-        #
-        # Thanks to the Kerberos Credentials cache this is not
-        # expensive inside the loop
-        server = get_krb5_rw_dns_server(creds, zone)
-        f.write('server %s\n' % server)
+    # Local the zone for this name
+    zone = dns.resolver.zone_for_name(normalised_name,
+                                      resolver=resolver)
+
+    # Now find the SOA, or if we can't get a ticket to the SOA,
+    # any server with an NS record we can get a ticket for.
+    #
+    # Thanks to the Kerberos Credentials cache this is not
+    # expensive inside the loop
+    server = get_krb5_rw_dns_server(creds, zone)
+    f.write('server %s\n' % server)
 
     if d.type == "A":
         f.write("update %s %s %u A %s\n" % (op, normalised_name, default_ttl, d.ip))
-- 
2.11.0


From c7dfd9d4217ec898bd4a069fd7fac8c72e2e9e34 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 3 Jan 2018 13:26:54 +0100
Subject: [PATCH 14/15] addns: Async ads_dns_lookup_srv

Use dns_lookup_send/recv to get SRV records. This avoids synchronous libresolv
calls and provides the infrastructure to get dsgetdcname async.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/addns/dnsquery.c    | 347 ++++++++++++++++++++----------------------------
 lib/addns/dnsquery.h    |   9 ++
 lib/addns/wscript_build |   2 +-
 3 files changed, 151 insertions(+), 207 deletions(-)

diff --git a/lib/addns/dnsquery.c b/lib/addns/dnsquery.c
index 30e5a564eec..d91c139d221 100644
--- a/lib/addns/dnsquery.c
+++ b/lib/addns/dnsquery.c
@@ -21,6 +21,9 @@
 #include "includes.h"
 #include "lib/util/util_net.h"
 #include "lib/util/tsort.h"
+#include "librpc/gen_ndr/dns.h"
+#include "libcli/dns/dns_lookup.h"
+#include "lib/util/tevent_ntstatus.h"
 #include "dnsquery.h"
 
 /* AIX resolv.h uses 'class' in struct ns_rr */
@@ -168,59 +171,6 @@ static bool ads_dns_parse_rr( TALLOC_CTX *ctx, uint8_t *start, uint8_t *end,
 /*********************************************************************
 *********************************************************************/
 
-static bool ads_dns_parse_rr_srv( TALLOC_CTX *ctx, uint8_t *start, uint8_t *end,
-                       uint8_t **ptr, struct dns_rr_srv *srv )
-{
-	struct dns_rr rr;
-	uint8_t *p;
-	char dcname[MAX_DNS_NAME_LENGTH];
-	int namelen;
-
-	if ( !start || !end || !srv || !*ptr)
-		return -1;
-
-	/* Parse the RR entry.  Coming out of the this, ptr is at the beginning
-	   of the next record */
-
-	if ( !ads_dns_parse_rr( ctx, start, end, ptr, &rr ) ) {
-		DEBUG(1,("ads_dns_parse_rr_srv: Failed to parse RR record\n"));
-		return false;
-	}
-
-	if ( rr.type != T_SRV ) {
-		DEBUG(1,("ads_dns_parse_rr_srv: Bad answer type (%d)\n",
-					rr.type));
-		return false;
-	}
-
-	p = rr.rdata;
-
-	srv->priority = RSVAL(p, 0);
-	srv->weight   = RSVAL(p, 2);
-	srv->port     = RSVAL(p, 4);
-
-	p += 6;
-
-	namelen = dn_expand( start, end, p, dcname, sizeof(dcname) );
-	if ( namelen < 0 ) {
-		DEBUG(1,("ads_dns_parse_rr_srv: Failed to uncompress name!\n"));
-		return false;
-	}
-
-	srv->hostname = talloc_strdup( ctx, dcname );
-
-	DEBUG(10,("ads_dns_parse_rr_srv: Parsed %s [%u, %u, %u]\n",
-		  srv->hostname,
-		  srv->priority,
-		  srv->weight,
-		  srv->port));
-
-	return true;
-}
-
-/*********************************************************************
-*********************************************************************/
-
 static bool ads_dns_parse_rr_ns( TALLOC_CTX *ctx, uint8_t *start, uint8_t *end,
                        uint8_t **ptr, struct dns_rr_ns *nsrec )
 {
@@ -385,196 +335,181 @@ static NTSTATUS dns_send_req( TALLOC_CTX *ctx, const char *name, int q_type,
 	return last_dns_status;
 }
 
-/*********************************************************************
- Simple wrapper for a DNS SRV query
-*********************************************************************/
+struct ads_dns_lookup_srv_state {
+	struct dns_rr_srv *srvs;
+	size_t num_srvs;
+};
 
-NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
-				const char *name,
-				struct dns_rr_srv **dclist,
-				int *numdcs)
+static void ads_dns_lookup_srv_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_srv_send(TALLOC_CTX *mem_ctx,
+					   struct tevent_context *ev,
+					   const char *name)
 {
-	uint8_t *buffer = NULL;
-	int resp_len = 0;
-	struct dns_rr_srv *dcs = NULL;
-	int query_count, answer_count, auth_count, additional_count;
-	uint8_t *p = buffer;
-	int rrnum;
-	int idx = 0;
-	NTSTATUS status;
+	struct tevent_req *req, *subreq;
+	struct ads_dns_lookup_srv_state *state;
 
-	if ( !ctx || !name || !dclist ) {
-		return NT_STATUS_INVALID_PARAMETER;
+	req = tevent_req_create(mem_ctx, &state,
+				struct ads_dns_lookup_srv_state);
+	if (req == NULL) {
+		return NULL;
 	}
 
-	/* Send the request.  May have to loop several times in case
-	   of large replies */
+	subreq = dns_lookup_send(
+		state,
+		ev,
+		NULL,
+		name,
+		DNS_QCLASS_IN,
+		DNS_QTYPE_SRV);
 
-	status = dns_send_req( ctx, name, T_SRV, &buffer, &resp_len );
-	if ( !NT_STATUS_IS_OK(status) ) {
-		DEBUG(3,("ads_dns_lookup_srv: Failed to send DNS query (%s)\n",
-			nt_errstr(status)));
-		return status;
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
 	}
-	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 */
-
-	/* Pull the answer RR's count from the header.
-	 * Use the NMB ordering macros */
-
-	query_count      = RSVAL( p, 4 );
-	answer_count     = RSVAL( p, 6 );
-	auth_count       = RSVAL( p, 8 );
-	additional_count = RSVAL( p, 10 );
-
-	DEBUG(4,("ads_dns_lookup_srv: "
-		"%d records returned in the answer section.\n",
-		answer_count));
+	tevent_req_set_callback(subreq, ads_dns_lookup_srv_done, req);
+	return req;
+}
 
-	if (answer_count) {
-		if ((dcs = talloc_zero_array(ctx, struct dns_rr_srv,
-						answer_count)) == NULL ) {
-			DEBUG(0,("ads_dns_lookup_srv: "
-				"talloc() failure for %d char*'s\n",
-				answer_count));
-			return NT_STATUS_NO_MEMORY;
-		}
-	} else {
-		dcs = NULL;
+static void ads_dns_lookup_srv_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct ads_dns_lookup_srv_state *state = tevent_req_data(
+		req, struct ads_dns_lookup_srv_state);
+	int ret;
+	struct dns_name_packet *reply;
+	uint16_t i, idx;
+
+	ret = dns_lookup_recv(subreq, state, &reply);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+		return;
 	}
 
-	/* now skip the header */
-
-	p += NS_HFIXEDSZ;
-
-	/* parse the query section */
-
-	for ( rrnum=0; rrnum<query_count; rrnum++ ) {
-		struct dns_query q;
-
-		if (!ads_dns_parse_query(ctx, buffer,
-					buffer+resp_len, &p, &q)) {
-			DEBUG(1,("ads_dns_lookup_srv: "
-				 "Failed to parse query record [%d]!\n", rrnum));
-			return NT_STATUS_UNSUCCESSFUL;
+	for (i=0; i<reply->ancount; i++) {
+		if (reply->answers[i].rr_type == DNS_QTYPE_SRV) {
+			state->num_srvs += 1;
 		}
 	}
 
-	/* now we are at the answer section */
-
-	for ( rrnum=0; rrnum<answer_count; rrnum++ ) {
-		if (!ads_dns_parse_rr_srv(ctx, buffer, buffer+resp_len,
-					&p, &dcs[rrnum])) {
-			DEBUG(1,("ads_dns_lookup_srv: "
-				 "Failed to parse answer recordi [%d]!\n", rrnum));
-			return NT_STATUS_UNSUCCESSFUL;
-		}
+	state->srvs = talloc_array(state, struct dns_rr_srv, state->num_srvs);
+	if (tevent_req_nomem(state->srvs, req)) {
+		return;
 	}
-	idx = rrnum;
 
-	/* Parse the authority section */
-	/* just skip these for now */
+	idx = 0;
 
-	for ( rrnum=0; rrnum<auth_count; rrnum++ ) {
-		struct dns_rr rr;
+	for (i=0; i<reply->ancount; i++) {
+		struct dns_res_rec *an = &reply->answers[i];
+		struct dns_rr_srv *dst = &state->srvs[idx];
+		struct dns_srv_record *src;
 
-		if (!ads_dns_parse_rr( ctx, buffer,
-					buffer+resp_len, &p, &rr)) {
-			DEBUG(1,("ads_dns_lookup_srv: "
-				 "Failed to parse authority record! [%d]\n", rrnum));
-			return NT_STATUS_UNSUCCESSFUL;
+		if (an->rr_type != DNS_QTYPE_SRV) {
+			continue;
 		}
+		src = &an->rdata.srv_record;
+
+		*dst = (struct dns_rr_srv) {
+			.hostname = talloc_move(state->srvs, &src->target),
+			.priority = src->priority,
+			.weight = src->weight,
+			.port = src->port,
+		};
+		idx += 1;
 	}
 
-	/* Parse the additional records section */
-
-	for ( rrnum=0; rrnum<additional_count; rrnum++ ) {
-		struct dns_rr rr;
-		int i;
+	for (i=0; i<reply->arcount; i++) {
+		struct dns_res_rec *ar = &reply->additional[i];
+		struct sockaddr_storage addr;
+		bool ok;
+		size_t j;
 
-		if (!ads_dns_parse_rr(ctx, buffer, buffer+resp_len,
-					&p, &rr)) {
-			DEBUG(1,("ads_dns_lookup_srv: Failed "
-				 "to parse additional records section! [%d]\n", rrnum));
-			return NT_STATUS_UNSUCCESSFUL;
+		ok = dns_res_rec_get_sockaddr(ar, &addr);
+		if (!ok) {
+			continue;
 		}
 
-		/* Only interested in A or AAAA records as a shortcut for having
-		 * to come back later and lookup the name. For multi-homed
-		 * hosts, the number of additional records and exceed the
-		 * number of answer records. */
+		for (j=0; j<state->num_srvs; j++) {
+			struct dns_rr_srv *srv = &state->srvs[j];
+			struct sockaddr_storage *tmp;
 
-		if (rr.type != T_A || rr.rdatalen != 4) {
-#if defined(HAVE_IPV6)
-			/* RFC2874 defines A6 records. This
-			 * requires recusive and horribly complex lookups.
-			 * Bastards. Ignore this for now.... JRA.
-			 * Luckily RFC3363 reprecates A6 records.
-			 */
-			if (rr.type != T_AAAA || rr.rdatalen != 16)
-#endif
+			if (strcmp(srv->hostname, ar->name) != 0) {
 				continue;
-		}
+			}
 
-		for ( i=0; i<idx; i++ ) {
-			if ( strcmp( rr.hostname, dcs[i].hostname ) == 0 ) {
-				int num_ips = dcs[i].num_ips;
-				struct sockaddr_storage *tmp_ss_s;
-
-				/* allocate new memory */
-
-				if (dcs[i].num_ips == 0) {
-					if ((dcs[i].ss_s = talloc_array(dcs,
-						struct sockaddr_storage, 1 ))
-							== NULL ) {
-						return NT_STATUS_NO_MEMORY;
-					}
-				} else {
-					if ((tmp_ss_s = talloc_realloc(dcs,
-							dcs[i].ss_s,
-							struct sockaddr_storage,
-							dcs[i].num_ips+1))
-								== NULL ) {
-						return NT_STATUS_NO_MEMORY;
-					}
-
-					dcs[i].ss_s = tmp_ss_s;
-				}
-				dcs[i].num_ips++;
+			tmp = talloc_realloc(
+				state->srvs,
+				srv->ss_s,
+				struct sockaddr_storage,
+				srv->num_ips+1);
 
-				/* copy the new IP address */
-				if (rr.type == T_A) {
-					struct in_addr ip;
-					memcpy(&ip, rr.rdata, 4);
-					in_addr_to_sockaddr_storage(
-							&dcs[i].ss_s[num_ips],
-							ip);
-				}
-#if defined(HAVE_IPV6)
-				if (rr.type == T_AAAA) {
-					struct in6_addr ip6;
-					memcpy(&ip6, rr.rdata, rr.rdatalen);
-					in6_addr_to_sockaddr_storage(
-							&dcs[i].ss_s[num_ips],
-							ip6);
-				}
-#endif
+			if (tevent_req_nomem(tmp, req)) {
+				return;
 			}
+			srv->ss_s = tmp;
+
+			srv->ss_s[srv->num_ips] = addr;
+			srv->num_ips += 1;
 		}
 	}
 
-	TYPESAFE_QSORT(dcs, idx, dnssrvcmp );
+	TYPESAFE_QSORT(state->srvs, state->num_srvs, dnssrvcmp);
+
+	tevent_req_done(req);
+}
 
-	*dclist = dcs;
-	*numdcs = idx;
+NTSTATUS ads_dns_lookup_srv_recv(struct tevent_req *req,
+				 TALLOC_CTX *mem_ctx,
+				 struct dns_rr_srv **srvs,
+				 size_t *num_srvs)
+{
+	struct ads_dns_lookup_srv_state *state = tevent_req_data(
+		req, struct ads_dns_lookup_srv_state);
+	NTSTATUS status;
 
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	*srvs = talloc_move(mem_ctx, &state->srvs);
+	*num_srvs = state->num_srvs;
+	tevent_req_received(req);
 	return NT_STATUS_OK;
 }
 
 /*********************************************************************
+ Simple wrapper for a DNS SRV query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
+				const char *name,
+				struct dns_rr_srv **dclist,
+				int *numdcs)
+{
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	NTSTATUS status = NT_STATUS_NO_MEMORY;
+	size_t num_srvs;
+
+	ev = samba_tevent_context_init(ctx);
+	if (ev == NULL) {
+		goto fail;
+	}
+	req = ads_dns_lookup_srv_send(ev, ev, name);
+	if (req == NULL) {
+		goto fail;
+	}
+	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+		goto fail;
+	}
+	status = ads_dns_lookup_srv_recv(req, ctx, dclist, &num_srvs);
+	*numdcs = num_srvs;	/* size_t->int */
+fail:
+	TALLOC_FREE(ev);
+	return status;
+}
+
+/*********************************************************************
  Simple wrapper for a DNS NS query
 *********************************************************************/
 
diff --git a/lib/addns/dnsquery.h b/lib/addns/dnsquery.h
index 213ed325a6e..8dc2806f91b 100644
--- a/lib/addns/dnsquery.h
+++ b/lib/addns/dnsquery.h
@@ -20,10 +20,19 @@
 #ifndef _ADS_DNS_H
 #define _ADS_DNS_H
 
+#include "replace.h"
+#include <tevent.h>
 #include "libcli/dns/dns.h"
 
 /* The following definitions come from libads/dns.c  */
 
+struct tevent_req *ads_dns_lookup_srv_send(TALLOC_CTX *mem_ctx,
+					   struct tevent_context *ev,
+					   const char *name);
+NTSTATUS ads_dns_lookup_srv_recv(struct tevent_req *req,
+				 TALLOC_CTX *mem_ctx,
+				 struct dns_rr_srv **srvs,
+				 size_t *num_srvs);
 NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
 				const char *name,
 				struct dns_rr_srv **dclist,
diff --git a/lib/addns/wscript_build b/lib/addns/wscript_build
index b1948ba35b1..c38e93ccc95 100644
--- a/lib/addns/wscript_build
+++ b/lib/addns/wscript_build
@@ -2,6 +2,6 @@
 
 bld.SAMBA_LIBRARY('addns',
                    source='dnsquery.c dnsrecord.c dnsutils.c dnssock.c dnsgss.c dnsmarshall.c error.c',
-                   public_deps='samba-util gssapi ndr resolv',
+                   public_deps='samba-util gssapi ndr resolv dns_lookup',
                    private_library=True,
                    vars=locals())
-- 
2.11.0


From c97b02a1974fec9ab6d4918bf4b941363459db6c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 3 Jan 2018 16:22:24 +0100
Subject: [PATCH 15/15] addns: Async ads_dns_lookup_ns

Use dns_lookup_send/recv to get NS records

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/addns/dnsquery.c | 472 +++++++++++++--------------------------------------
 lib/addns/dnsquery.h |   7 +
 2 files changed, 121 insertions(+), 358 deletions(-)

diff --git a/lib/addns/dnsquery.c b/lib/addns/dnsquery.c
index d91c139d221..e5600367c4b 100644
--- a/lib/addns/dnsquery.c
+++ b/lib/addns/dnsquery.c
@@ -77,140 +77,6 @@
 #endif
 
 /*********************************************************************
-*********************************************************************/
-
-static bool ads_dns_parse_query( TALLOC_CTX *ctx, uint8_t *start, uint8_t *end,
-                          uint8_t **ptr, struct dns_query *q )
-{
-	uint8_t *p = *ptr;
-	char hostname[MAX_DNS_NAME_LENGTH];
-	int namelen;
-
-	ZERO_STRUCTP( q );
-
-	if ( !start || !end || !q || !*ptr)
-		return false;
-
-	/* See RFC 1035 for details. If this fails, then return. */
-
-	namelen = dn_expand( start, end, p, hostname, sizeof(hostname) );
-	if ( namelen < 0 ) {
-		return false;
-	}
-	p += namelen;
-	q->hostname = talloc_strdup( ctx, hostname );
-
-	/* check that we have space remaining */
-
-	if ( PTR_DIFF(p+4, end) > 0 )
-		return false;
-
-	q->type     = RSVAL( p, 0 );
-	q->in_class = RSVAL( p, 2 );
-	p += 4;
-
-	*ptr = p;
-
-	return true;
-}
-
-/*********************************************************************
-*********************************************************************/
-
-static bool ads_dns_parse_rr( TALLOC_CTX *ctx, uint8_t *start, uint8_t *end,
-                       uint8_t **ptr, struct dns_rr *rr )
-{
-	uint8_t *p = *ptr;
-	char hostname[MAX_DNS_NAME_LENGTH];
-	int namelen;
-
-	if ( !start || !end || !rr || !*ptr)
-		return -1;
-
-	ZERO_STRUCTP( rr );
-	/* pull the name from the answer */
-
-	namelen = dn_expand( start, end, p, hostname, sizeof(hostname) );
-	if ( namelen < 0 ) {
-		return -1;
-	}
-	p += namelen;
-	rr->hostname = talloc_strdup( ctx, hostname );
-
-	/* check that we have space remaining */
-
-	if ( PTR_DIFF(p+10, end) > 0 )
-		return false;
-
-	/* 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 ( PTR_DIFF(p+rr->rdatalen, end ) > 0 ) {
-		return false;
-
-	}
-
-	/* save a point to the rdata for this section */
-
-	rr->rdata = p;
-	p += rr->rdatalen;
-
-	*ptr = p;
-
-	return true;
-}
-
-/*********************************************************************
-*********************************************************************/
-
-static bool ads_dns_parse_rr_ns( TALLOC_CTX *ctx, uint8_t *start, uint8_t *end,
-                       uint8_t **ptr, struct dns_rr_ns *nsrec )
-{
-	struct dns_rr rr;
-	uint8_t *p;
-	char nsname[MAX_DNS_NAME_LENGTH];
-	int namelen;
-
-	if ( !start || !end || !nsrec || !*ptr)
-		return -1;
-
-	/* Parse the RR entry.  Coming out of the this, ptr is at the beginning
-	   of the next record */
-
-	if ( !ads_dns_parse_rr( ctx, start, end, ptr, &rr ) ) {
-		DEBUG(1,("ads_dns_parse_rr_ns: Failed to parse RR record\n"));
-		return false;
-	}
-
-	if ( rr.type != T_NS ) {
-		DEBUG(1,("ads_dns_parse_rr_ns: Bad answer type (%d)\n",
-					rr.type));
-		return false;
-	}
-
-	p = rr.rdata;
-
-	/* ame server hostname */
-
-	namelen = dn_expand( start, end, p, nsname, sizeof(nsname) );
-	if ( namelen < 0 ) {
-		DEBUG(1,("ads_dns_parse_rr_ns: Failed to uncompress name!\n"));
-		return false;
-	}
-	nsrec->hostname = talloc_strdup( ctx, nsname );
-
-	return true;
-}
-
-/*********************************************************************
  Sort SRV record list based on weight and priority.  See RFC 2782.
 *********************************************************************/
 
@@ -235,106 +101,6 @@ static int dnssrvcmp( struct dns_rr_srv *a, struct dns_rr_srv *b )
 	return 1;
 }
 
-/*********************************************************************
- Simple wrapper for a DNS query
-*********************************************************************/
-
-#define DNS_FAILED_WAITTIME          30
-
-static NTSTATUS dns_send_req( TALLOC_CTX *ctx, const char *name, int q_type,
-                              uint8_t **buf, int *resp_length )
-{
-	uint8_t *buffer = NULL;
-	size_t buf_len = 0;
-	int resp_len = NS_PACKETSZ;
-	static time_t last_dns_check = 0;
-	static NTSTATUS last_dns_status = NT_STATUS_OK;
-	time_t now = time_mono(NULL);
-
-	/* 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;
-
-	/* 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 ( (NT_STATUS_EQUAL(last_dns_status,NT_STATUS_IO_TIMEOUT) ||
-	      NT_STATUS_EQUAL(last_dns_status,NT_STATUS_CONNECTION_REFUSED)) &&
-	     (last_dns_check+DNS_FAILED_WAITTIME) > now )
-	{
-		DEBUG(10,("dns_send_req: last dns check returning cached status (%s)\n",
-			  nt_errstr(last_dns_status) ));
-		return last_dns_status;
-	}
-
-	/* Send the Query */
-	do {
-		if ( buffer )
-			TALLOC_FREE( buffer );
-
-		buf_len = resp_len * sizeof(uint8_t);
-
-		if (buf_len) {
-			if ((buffer = talloc_array(ctx, uint8_t, buf_len))
-					== NULL ) {
-				DEBUG(0,("dns_send_req: "
-					"talloc() failed!\n"));
-				last_dns_status = NT_STATUS_NO_MEMORY;
-				last_dns_check = time_mono(NULL);
-				return last_dns_status;
-			}
-		}
-
-		if ((resp_len = res_query(name, C_IN, q_type, buffer, buf_len))
-				< 0 ) {
-			DEBUG(3,("dns_send_req: "
-				"Failed to resolve %s (%s)\n",
-				name, strerror(errno)));
-			TALLOC_FREE( buffer );
-			last_dns_status = NT_STATUS_UNSUCCESSFUL;
-
-			if (errno == ETIMEDOUT) {
-				last_dns_status = NT_STATUS_IO_TIMEOUT;
-			}
-			if (errno == ECONNREFUSED) {
-				last_dns_status = NT_STATUS_CONNECTION_REFUSED;
-			}
-			last_dns_check = time_mono(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) {
-				DEBUG(1,("dns_send_req: DNS reply too large when resolving %s\n",
-					name));
-				TALLOC_FREE( buffer );
-				last_dns_status = NT_STATUS_BUFFER_TOO_SMALL;
-				last_dns_check = time_mono(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 );
-
-	*buf = buffer;
-	*resp_length = resp_len;
-
-	last_dns_check = time_mono(NULL);
-	last_dns_status = NT_STATUS_OK;
-	return last_dns_status;
-}
-
 struct ads_dns_lookup_srv_state {
 	struct dns_rr_srv *srvs;
 	size_t num_srvs;
@@ -509,161 +275,151 @@ fail:
 	return status;
 }
 
-/*********************************************************************
- Simple wrapper for a DNS NS query
-*********************************************************************/
+struct ads_dns_lookup_ns_state {
+	struct dns_rr_ns *nss;
+	size_t num_nss;
+};
 
-NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
-				const char *dnsdomain,
-				struct dns_rr_ns **nslist,
-				int *numns)
+static void ads_dns_lookup_ns_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_ns_send(TALLOC_CTX *mem_ctx,
+					  struct tevent_context *ev,
+					  const char *name)
 {
-	uint8_t *buffer = NULL;
-	int resp_len = 0;
-	struct dns_rr_ns *nsarray = NULL;
-	int query_count, answer_count, auth_count, additional_count;
-	uint8_t *p;
-	int rrnum;
-	int idx = 0;
-	NTSTATUS status;
+	struct tevent_req *req, *subreq;
+	struct ads_dns_lookup_ns_state *state;
 
-	if ( !ctx || !dnsdomain || !nslist ) {
-		return NT_STATUS_INVALID_PARAMETER;
+	req = tevent_req_create(mem_ctx, &state,
+				struct ads_dns_lookup_ns_state);
+	if (req == NULL) {
+		return NULL;
 	}
 
-	/* Send the request.  May have to loop several times in case
-	   of large replies */
-
-	status = dns_send_req( ctx, dnsdomain, T_NS, &buffer, &resp_len );
-	if ( !NT_STATUS_IS_OK(status) ) {
-		DEBUG(3,("ads_dns_lookup_ns: Failed to send DNS query (%s)\n",
-			nt_errstr(status)));
-		return status;
-	}
-	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 */
-
-	/* Pull the answer RR's count from the header.
-	 * Use the NMB ordering macros */
-
-	query_count      = RSVAL( p, 4 );
-	answer_count     = RSVAL( p, 6 );
-	auth_count       = RSVAL( p, 8 );
-	additional_count = RSVAL( p, 10 );
-
-	DEBUG(4,("ads_dns_lookup_ns: "
-		"%d records returned in the answer section.\n",
-		answer_count));
-
-	if (answer_count) {
-		if ((nsarray = talloc_array(ctx, struct dns_rr_ns,
-						answer_count)) == NULL ) {
-			DEBUG(0,("ads_dns_lookup_ns: "
-				"talloc() failure for %d char*'s\n",
-				answer_count));
-			return NT_STATUS_NO_MEMORY;
-		}
-	} else {
-		nsarray = NULL;
+	subreq = dns_lookup_send(state, ev, NULL, name, DNS_QCLASS_IN,
+				 DNS_QTYPE_NS);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
 	}
+	tevent_req_set_callback(subreq, ads_dns_lookup_ns_done, req);
+	return req;
+}
 
-	/* now skip the header */
-
-	p += NS_HFIXEDSZ;
-
-	/* parse the query section */
+static void ads_dns_lookup_ns_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct ads_dns_lookup_ns_state *state = tevent_req_data(
+		req, struct ads_dns_lookup_ns_state);
+	int ret;
+	struct dns_name_packet *reply;
+	uint16_t i, idx;
 
-	for ( rrnum=0; rrnum<query_count; rrnum++ ) {
-		struct dns_query q;
+	ret = dns_lookup_recv(subreq, state, &reply);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+		return;
+	}
 
-		if (!ads_dns_parse_query(ctx, buffer, buffer+resp_len,
-					&p, &q)) {
-			DEBUG(1,("ads_dns_lookup_ns: "
-				" Failed to parse query record!\n"));
-			return NT_STATUS_UNSUCCESSFUL;
+	for (i=0; i<reply->ancount; i++) {
+		if (reply->answers[i].rr_type == DNS_QTYPE_NS) {
+			state->num_nss += 1;
 		}
 	}
 
-	/* now we are at the answer section */
-
-	for ( rrnum=0; rrnum<answer_count; rrnum++ ) {
-		if (!ads_dns_parse_rr_ns(ctx, buffer, buffer+resp_len,
-					&p, &nsarray[rrnum])) {
-			DEBUG(1,("ads_dns_lookup_ns: "
-				"Failed to parse answer record!\n"));
-			return NT_STATUS_UNSUCCESSFUL;
-		}
+	state->nss = talloc_array(state, struct dns_rr_ns, state->num_nss);
+	if (tevent_req_nomem(state->nss, req)) {
+		return;
 	}
-	idx = rrnum;
 
-	/* Parse the authority section */
-	/* just skip these for now */
+	idx = 0;
 
-	for ( rrnum=0; rrnum<auth_count; rrnum++ ) {
-		struct dns_rr rr;
+	for (i=0; i<reply->ancount; i++) {
+		struct dns_res_rec *an = &reply->answers[i];
 
-		if ( !ads_dns_parse_rr(ctx, buffer, buffer+resp_len,
-					&p, &rr)) {
-			DEBUG(1,("ads_dns_lookup_ns: "
-				"Failed to parse authority record!\n"));
-			return NT_STATUS_UNSUCCESSFUL;
+		if (an->rr_type != DNS_QTYPE_NS) {
+			continue;
 		}
-	}
 
-	/* Parse the additional records section */
+		state->nss[idx].hostname = talloc_move(state->nss,
+						       &an->rdata.ns_record);
+		idx += 1;
+	}
 
-	for ( rrnum=0; rrnum<additional_count; rrnum++ ) {
-		struct dns_rr rr;
-		int i;
+	for (i=0; i<reply->arcount; i++) {
+		struct dns_res_rec *ar = &reply->additional[i];
+		struct sockaddr_storage addr;
+		bool ok;
+		size_t j;
 
-		if (!ads_dns_parse_rr(ctx, buffer, buffer+resp_len,
-					&p, &rr)) {
-			DEBUG(1,("ads_dns_lookup_ns: Failed "
-				"to parse additional records section!\n"));
-			return NT_STATUS_UNSUCCESSFUL;
+		ok = dns_res_rec_get_sockaddr(ar, &addr);
+		if (!ok) {
+			continue;
 		}
 
-		/* only interested in A records as a shortcut for having to come
-		   back later and lookup the name */
-
-		if (rr.type != T_A || rr.rdatalen != 4) {
-#if defined(HAVE_IPV6)
-			if (rr.type != T_AAAA || rr.rdatalen != 16)
-#endif
-				continue;
-		}
+		for (j=0; j<state->num_nss; j++) {
+			struct dns_rr_ns *ns = &state->nss[j];
 
-		for ( i=0; i<idx; i++ ) {
-			if (strcmp(rr.hostname, nsarray[i].hostname) == 0) {
-				if (rr.type == T_A) {
-					struct in_addr ip;
-					memcpy(&ip, rr.rdata, 4);
-					in_addr_to_sockaddr_storage(
-							&nsarray[i].ss,
-							ip);
-				}
-#if defined(HAVE_IPV6)
-				if (rr.type == T_AAAA) {
-					struct in6_addr ip6;
-					memcpy(&ip6, rr.rdata, rr.rdatalen);
-					in6_addr_to_sockaddr_storage(
-							&nsarray[i].ss,
-							ip6);
-				}
-#endif
+			if (strcmp(ns->hostname, ar->name) == 0) {
+				ns->ss = addr;
 			}
 		}
 	}
 
-	*nslist = nsarray;
-	*numns = idx;
+	tevent_req_done(req);
+}
 
+NTSTATUS ads_dns_lookup_ns_recv(struct tevent_req *req,
+				TALLOC_CTX *mem_ctx,
+				struct dns_rr_ns **nss,
+				size_t *num_nss)
+{
+	struct ads_dns_lookup_ns_state *state = tevent_req_data(
+		req, struct ads_dns_lookup_ns_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	*nss = talloc_move(mem_ctx, &state->nss);
+	*num_nss = state->num_nss;
+	tevent_req_received(req);
 	return NT_STATUS_OK;
 }
 
+/*********************************************************************
+ Simple wrapper for a DNS NS query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
+				const char *dnsdomain,
+				struct dns_rr_ns **nslist,
+				int *numns)
+{
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	NTSTATUS status = NT_STATUS_NO_MEMORY;
+	size_t num_ns = 0;
+
+	ev = samba_tevent_context_init(ctx);
+	if (ev == NULL) {
+		goto fail;
+	}
+	req = ads_dns_lookup_ns_send(ev, ev, dnsdomain);
+	if (req == NULL) {
+		goto fail;
+	}
+	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+		goto fail;
+	}
+	status = ads_dns_lookup_ns_recv(req, ctx, nslist, &num_ns);
+	*numns = num_ns;
+fail:
+	TALLOC_FREE(ev);
+	return status;
+}
+
+
 /********************************************************************
  Query with optional sitename.
 ********************************************************************/
diff --git a/lib/addns/dnsquery.h b/lib/addns/dnsquery.h
index 8dc2806f91b..bb691f5d55a 100644
--- a/lib/addns/dnsquery.h
+++ b/lib/addns/dnsquery.h
@@ -37,6 +37,13 @@ NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
 				const char *name,
 				struct dns_rr_srv **dclist,
 				int *numdcs);
+struct tevent_req *ads_dns_lookup_ns_send(TALLOC_CTX *mem_ctx,
+					  struct tevent_context *ev,
+					  const char *name);
+NTSTATUS ads_dns_lookup_ns_recv(struct tevent_req *req,
+				TALLOC_CTX *mem_ctx,
+				struct dns_rr_ns **nss,
+				size_t *num_nss);
 NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
 				const char *dnsdomain,
 				struct dns_rr_ns **nslist,
-- 
2.11.0



More information about the samba-technical mailing list