On Wed, Jul 8, 2015 at 11:11 PM, Poul-Henning Kamp <[email protected]> wrote:
> --------
> In message 
> <CABoVN9CP=yzv73d5vhu-5racuufsy1ad18m-zqamhkjakvb...@mail.gmail.com>, Dridi 
> Boukelmoune writes:
>
>>>>Subject: [PATCH 2/5] Keep track of backends references and connections
>>>
>>> I've done this entirely differently.
>>>
>>> Fixes #1755
>>
>>Please revisit this one, my patch also fixes two panics I just got while
>>rebasing against the current trunk. See attached test logs.
>
> I tried releaseing the lock as we talked about on IRC and that solves
> the problem.  Committed.

Ack.

However, I see a flaw in the VBE_Poll implementation. If you have
deleted backends with a zero n_conn, but the first one is still being
used, nothing's deleted. It makes it less predictable than my
(convoluted) delete-when-deletable approach. It doesn't hurt the test
suite, which *should be* predictable anyway. And I have updated the
test suite to trigger a VBE_Poll before assertions on the n_backend
counter.

>>>> Subject: [PATCH 5/5] Add more information to a backend panic message
>>>
>>> This doesn't work with the way I did #2/5 above.
>>
>>Do I send a patch or simply commit?
>
> Just commit it.

Done.

I have attached a new patch set which would have been ready before
my rehearsal if it weren't for a last-minute phone call (let's call the
culprit Dave). I have rearranged the patches against the current
master hoping that they'll be reviewed this week.

The first patch is my initial fix for 1755 and dynamic-backends-related
issues, reworked after the recent changes in master (probe "deleted"
and VBE_Poll). I rely on refcount to avoid dangling struct backend
pointers, which can now happen with dynamic backends.

The second patch is the actual test suite.

The last two bonus patches are a first stab at a DNS director for 4.1,
but requests fail with IPv6 backends.

Cheers,
Dridi
From 6b2654bdf577bbc38e875d546356faaea6fdbf2b Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <[email protected]>
Date: Mon, 6 Jul 2015 08:19:43 +0200
Subject: [PATCH 1/5] Keep track of backends references and connections

---
 bin/varnishd/cache/cache_backend.c     | 24 +++++++++++++++++++-----
 bin/varnishd/cache/cache_backend.h     |  2 +-
 bin/varnishd/cache/cache_backend_cfg.c | 13 +++++++++++--
 3 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/bin/varnishd/cache/cache_backend.c b/bin/varnishd/cache/cache_backend.c
index 611c089..11aae0d 100644
--- a/bin/varnishd/cache/cache_backend.c
+++ b/bin/varnishd/cache/cache_backend.c
@@ -71,12 +71,17 @@ vbe_dir_getfd(struct worker *wrk, struct backend *bp, struct busyobj *bo)
 	CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
 	AN(bp->vsc);
 
+	Lck_Lock(&bp->mtx);
+	bp->refcount++;
+	Lck_Unlock(&bp->mtx);
+
 	if (!VBE_Healthy(bp, NULL)) {
 		// XXX: per backend stats ?
 		VSC_C_main->backend_unhealthy++;
 		return (NULL);
 	}
 
+	assert(bp->n_conn >= 0);
 	if (bp->max_connections > 0 && bp->n_conn >= bp->max_connections) {
 		// XXX: per backend stats ?
 		VSC_C_main->backend_busy++;
@@ -103,7 +108,6 @@ vbe_dir_getfd(struct worker *wrk, struct backend *bp, struct busyobj *bo)
 	AN(vc->addr);
 
 	Lck_Lock(&bp->mtx);
-	bp->refcount++;
 	bp->n_conn++;
 	bp->vsc->conn++;
 	Lck_Unlock(&bp->mtx);
@@ -165,12 +169,15 @@ vbe_dir_finish(const struct director *d, struct worker *wrk,
 		VSC_C_main->backend_recycle++;
 		VBT_Recycle(wrk, bp->tcp_pool, &vbc);
 	}
-	assert(bp->n_conn > 0);
-	bp->n_conn--;
 #define ACCT(foo)	bp->vsc->foo += bo->acct.foo;
 #include "tbl/acct_fields_bereq.h"
 #undef ACCT
+
+	assert(bp->n_conn > 0);
+	bp->n_conn--;
+	bp->refcount--;
 	Lck_Unlock(&bp->mtx);
+
 	bo->htc = NULL;
 }
 
@@ -199,7 +206,10 @@ vbe_dir_gethdrs(const struct director *d, struct worker *wrk,
 		vbc = vbe_dir_getfd(wrk, bp, bo);
 		if (vbc == NULL) {
 			VSLb(bo->vsl, SLT_FetchError, "no backend connection");
-			return (-1);
+			Lck_Lock(&bp->mtx);
+			bp->refcount--;
+			Lck_Unlock(&bp->mtx);
+			break;
 		}
 		AN(bo->htc);
 		if (vbc->state != VBC_STATE_STOLEN)
@@ -295,6 +305,10 @@ vbe_dir_http1pipe(const struct director *d, struct req *req, struct busyobj *bo)
 	if (vbc == NULL) {
 		VSLb(bo->vsl, SLT_FetchError, "no backend connection");
 		SES_Close(req->sp, SC_RX_TIMEOUT);
+		V1P_Charge(req, &v1a, bp->vsc);
+		Lck_Lock(&bp->mtx);
+		bp->refcount--;
+		Lck_Unlock(&bp->mtx);
 	} else {
 		i = V1F_SendReq(req->wrk, bo, &v1a.bereq, 1);
 		VSLb_ts_req(req, "Pipe", W_TIM_real(req->wrk));
@@ -305,9 +319,9 @@ vbe_dir_http1pipe(const struct director *d, struct req *req, struct busyobj *bo)
 		VSLb_ts_req(req, "PipeSess", W_TIM_real(req->wrk));
 		SES_Close(req->sp, SC_TX_PIPE);
 		bo->htc->doclose = SC_TX_PIPE;
+		V1P_Charge(req, &v1a, bp->vsc);
 		vbe_dir_finish(d, req->wrk, bo);
 	}
-	V1P_Charge(req, &v1a, bp->vsc);
 }
 
 /*--------------------------------------------------------------------*/
diff --git a/bin/varnishd/cache/cache_backend.h b/bin/varnishd/cache/cache_backend.h
index 97e70c1..e5af949 100644
--- a/bin/varnishd/cache/cache_backend.h
+++ b/bin/varnishd/cache/cache_backend.h
@@ -107,7 +107,7 @@ unsigned VBE_Healthy(const struct backend *b, double *changed);
 #ifdef VCL_MET_MAX
 void VBE_Event(struct backend *, enum vcl_event_e);
 #endif
-void VBE_Delete(struct backend *be);
+void VBE_Delete(struct backend *);
 
 /* cache_backend_probe.c */
 void VBP_Insert(struct backend *b, struct vrt_backend_probe const *p,
diff --git a/bin/varnishd/cache/cache_backend_cfg.c b/bin/varnishd/cache/cache_backend_cfg.c
index a9199d3..0d54495 100644
--- a/bin/varnishd/cache/cache_backend_cfg.c
+++ b/bin/varnishd/cache/cache_backend_cfg.c
@@ -140,10 +140,11 @@ VRT_delete_backend(VRT_CTX, struct director **dp)
 	*dp = NULL;
 	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
 	CAST_OBJ_NOTNULL(be, d->priv, BACKEND_MAGIC);
+
 	Lck_Lock(&be->mtx);
 	be->admin_health = vbe_ah_deleted;
 	be->health_changed = VTIM_real();
-	r = be->n_conn;
+	r = be->refcount;
 	if (r > 0) {
 		/* move to front of list for fast access */
 		VTAILQ_REMOVE(&backends, be, list);
@@ -189,9 +190,17 @@ VBE_Delete(struct backend *be)
 {
 	CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
 
+	if (be->vsc) {
+		/* deleted dynamic backend is not referenced anymore */
+		VBE_Event(be, VCL_EVENT_COLD);
+	}
+
 	if (be->probe != NULL)
 		VBP_Remove(be);
 
+	AZ(be->refcount);
+	AZ(be->n_conn);
+
 	Lck_Lock(&backends_mtx);
 	VTAILQ_REMOVE(&backends, be, list);
 	VSC_C_main->n_backend--;
@@ -434,7 +443,7 @@ VBE_Poll(void)
 			break;
 		if (be->admin_health != vbe_ah_deleted)
 			break;
-		if (be->n_conn > 0)
+		if (be->refcount > 0)
 			break;
 		Lck_Unlock(&backends_mtx);
 		VCL_DelBackend(be);
-- 
2.1.0

From d97b1e6db82448f145a9b7527502fe69ea03554f Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <[email protected]>
Date: Mon, 6 Jul 2015 08:31:06 +0200
Subject: [PATCH 2/5] Add a test suite for dynamic backends

---
 bin/varnishtest/tests/d00007.vtc   |  28 +++++
 bin/varnishtest/tests/d00008.vtc   |  52 ++++++++++
 bin/varnishtest/tests/d00009.vtc   |  58 +++++++++++
 bin/varnishtest/tests/d00010.vtc   |  59 +++++++++++
 bin/varnishtest/tests/d00011.vtc   |  56 ++++++++++
 bin/varnishtest/tests/d00012.vtc   |  77 ++++++++++++++
 bin/varnishtest/tests/d00013.vtc   |  53 ++++++++++
 lib/libvmod_debug/Makefile.am      |   5 +-
 lib/libvmod_debug/vmod.vcc         |  12 +++
 lib/libvmod_debug/vmod_debug_dyn.c | 206 +++++++++++++++++++++++++++++++++++++
 10 files changed, 604 insertions(+), 2 deletions(-)
 create mode 100644 bin/varnishtest/tests/d00007.vtc
 create mode 100644 bin/varnishtest/tests/d00008.vtc
 create mode 100644 bin/varnishtest/tests/d00009.vtc
 create mode 100644 bin/varnishtest/tests/d00010.vtc
 create mode 100644 bin/varnishtest/tests/d00011.vtc
 create mode 100644 bin/varnishtest/tests/d00012.vtc
 create mode 100644 bin/varnishtest/tests/d00013.vtc
 create mode 100644 lib/libvmod_debug/vmod_debug_dyn.c

diff --git a/bin/varnishtest/tests/d00007.vtc b/bin/varnishtest/tests/d00007.vtc
new file mode 100644
index 0000000..298fbf7
--- /dev/null
+++ b/bin/varnishtest/tests/d00007.vtc
@@ -0,0 +1,28 @@
+varnishtest "Test dynamic backends"
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
diff --git a/bin/varnishtest/tests/d00008.vtc b/bin/varnishtest/tests/d00008.vtc
new file mode 100644
index 0000000..2427185
--- /dev/null
+++ b/bin/varnishtest/tests/d00008.vtc
@@ -0,0 +1,52 @@
+varnishtest "Test dynamic backends hot swap"
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	txresp
+} -start
+
+server s2 {
+	rxreq
+	expect req.url == "/bar"
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.hot_swap(req.http.X-Addr ,req.http.X-Port);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+
+	txreq -req "SWAP" -hdr "X-Addr: ${s2_addr}" -hdr "X-Port: ${s2_port}"
+	rxresp
+	expect resp.status == 200
+
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+delay 1
+
+varnish v1 -cli backend.list
+varnish v1 -expect MAIN.n_backend == 2
diff --git a/bin/varnishtest/tests/d00009.vtc b/bin/varnishtest/tests/d00009.vtc
new file mode 100644
index 0000000..f257d75
--- /dev/null
+++ b/bin/varnishtest/tests/d00009.vtc
@@ -0,0 +1,58 @@
+varnishtest "Test dynamic backends hot swap while being used"
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	sema r1 sync 2
+	sema r2 sync 2
+	txresp
+} -start
+
+server s2 {
+	rxreq
+	expect req.url == "/bar"
+	sema r2 sync 2
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.hot_swap(req.http.X-Addr ,req.http.X-Port);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+} -start
+
+client c2 {
+	sema r1 sync 2
+	txreq -req "SWAP" -hdr "X-Addr: ${s2_addr}" -hdr "X-Port: ${s2_port}"
+	rxresp
+	expect resp.status == 200
+
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+client c1 -wait
+
+varnish v1 -cli backend.list
+varnish v1 -expect MAIN.n_backend == 2
diff --git a/bin/varnishtest/tests/d00010.vtc b/bin/varnishtest/tests/d00010.vtc
new file mode 100644
index 0000000..77b2221
--- /dev/null
+++ b/bin/varnishtest/tests/d00010.vtc
@@ -0,0 +1,59 @@
+varnishtest "Test dynamic backends hot swap during a pipe"
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	sema r1 sync 2
+	sema r2 sync 2
+	txresp
+} -start
+
+server s2 {
+	rxreq
+	expect req.url == "/bar"
+	sema r2 sync 2
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.hot_swap(req.http.X-Addr ,req.http.X-Port);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+		return (pipe);
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+} -start
+
+client c2 {
+	sema r1 sync 2
+	txreq -req "SWAP" -hdr "X-Addr: ${s2_addr}" -hdr "X-Port: ${s2_port}"
+	rxresp
+	expect resp.status == 200
+
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+client c1 -wait
+
+varnish v1 -cli backend.list
+varnish v1 -expect MAIN.n_backend == 2
diff --git a/bin/varnishtest/tests/d00011.vtc b/bin/varnishtest/tests/d00011.vtc
new file mode 100644
index 0000000..1af7e5e
--- /dev/null
+++ b/bin/varnishtest/tests/d00011.vtc
@@ -0,0 +1,56 @@
+varnishtest "Test a dynamic backend hot swap after it was picked by a bereq"
+
+server s1 {
+} -start
+
+server s2 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.hot_swap(req.http.X-Addr ,req.http.X-Port);
+			return (synth(200));
+		}
+	}
+
+	sub vcl_backend_fetch {
+		set bereq.backend = s1.backend();
+		# hot swap should happen while we sleep
+		debug.sleep(2s);
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq
+	sema r2 sync 2
+	rxresp
+	expect resp.status == 200
+}
+
+client c2 {
+	sema r2 sync 2
+	delay 0.1
+	txreq -req "SWAP" -hdr "X-Addr: ${s2_addr}" -hdr "X-Port: ${s2_port}"
+	rxresp
+	expect resp.status == 200
+}
+
+client c1 -start
+client c2 -run
+client c1 -wait
+
+varnish v1 -cli backend.list
+varnish v1 -expect MAIN.n_backend == 2
diff --git a/bin/varnishtest/tests/d00012.vtc b/bin/varnishtest/tests/d00012.vtc
new file mode 100644
index 0000000..fc5c783
--- /dev/null
+++ b/bin/varnishtest/tests/d00012.vtc
@@ -0,0 +1,77 @@
+varnishtest "Test a dynamic backend discard during a request"
+
+# vcl.discard testing inspired by v00006.vtc from commit e1f7207
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	sema r1 sync 2
+	txresp
+} -start
+
+varnish v1 -arg "-p thread_pools=1" -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+# expected: vcl1.dummy, vcl1.s1
+
+server s2 {
+	rxreq
+	expect req.url == "/bar"
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s2 = debug.dyn("${s2_addr}", "${s2_port}");
+	}
+
+	sub vcl_recv {
+		set req.backend_hint = s2.backend();
+	}
+}
+
+varnish v1 -cli "vcl.discard vcl1"
+sema r1 sync 2
+
+client c1 -wait
+delay 2
+
+varnish v1 -expect MAIN.n_backend == 4
+# expected: vcl1.dummy, vcl1.s1, vcl2.dummy, vcl2.s2
+
+varnish v1 -expect n_vcl_avail == 1
+varnish v1 -expect n_vcl_discard == 1
+
+client c1 {
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+varnish v1 -cli "vcl.list"
+varnish v1 -expect MAIN.n_backend == 2
+# expected: vcl2.dummy, vcl2.s2
+varnish v1 -expect n_vcl_avail == 1
+varnish v1 -expect n_vcl_discard == 0
diff --git a/bin/varnishtest/tests/d00013.vtc b/bin/varnishtest/tests/d00013.vtc
new file mode 100644
index 0000000..d620e27
--- /dev/null
+++ b/bin/varnishtest/tests/d00013.vtc
@@ -0,0 +1,53 @@
+varnishtest "Test a dynamic backend hot swap after it was hinted to a req"
+
+server s1 {
+} -start
+
+server s2 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import ${vmod_debug};
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn("${s1_addr}", "${s1_port}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.hot_swap(req.http.X-Addr ,req.http.X-Port);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+		# hot swap should happen while we sleep
+		debug.sleep(2s);
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq
+	sema r2 sync 2
+	rxresp
+	expect resp.status == 200
+}
+
+client c2 {
+	sema r2 sync 2
+	delay 0.1
+	txreq -req "SWAP" -hdr "X-Addr: ${s2_addr}" -hdr "X-Port: ${s2_port}"
+	rxresp
+	expect resp.status == 200
+}
+
+client c1 -start
+client c2 -run
+client c1 -wait
+
+varnish v1 -cli backend.list
+varnish v1 -expect MAIN.n_backend == 2
diff --git a/lib/libvmod_debug/Makefile.am b/lib/libvmod_debug/Makefile.am
index d189102..9115713 100644
--- a/lib/libvmod_debug/Makefile.am
+++ b/lib/libvmod_debug/Makefile.am
@@ -17,14 +17,15 @@ libvmod_debug_la_LDFLAGS = $(AM_LDFLAGS) -module -export-dynamic -avoid-version
 
 libvmod_debug_la_SOURCES = \
 	vmod_debug.c \
-	vmod_debug_obj.c
+	vmod_debug_obj.c \
+	vmod_debug_dyn.c
 
 nodist_libvmod_debug_la_SOURCES = \
 	vcc_if.c \
 	vcc_if.h
 
 # BUILT_SOURCES is only a hack and dependency tracking does not help for the first build
-vmod_debug.lo vmod_debug_obj.lo: vcc_if.h
+vmod_debug.lo vmod_debug_obj.lo vmod_debug_dyn.lo: vcc_if.h
 
 vcc_if.c vcc_if.h vmod_debug.rst vmod_debug.man.rst: $(vmodtool) $(vmod_srcdir)/vmod.vcc
 	@PYTHON@ $(vmodtool) $(vmodtoolargs) $(vmod_srcdir)/vmod.vcc
diff --git a/lib/libvmod_debug/vmod.vcc b/lib/libvmod_debug/vmod.vcc
index 305deaa..464b223 100644
--- a/lib/libvmod_debug/vmod.vcc
+++ b/lib/libvmod_debug/vmod.vcc
@@ -109,3 +109,15 @@ Function to fail vcl_init{}
 $Function VOID sleep(DURATION)
 
 Block the current worker thread.
+
+$Object dyn(STRING addr, STRING port)
+
+Dynamically create a single-backend director
+
+$Method BACKEND .backend()
+
+Return the dynamic backend.
+
+$Method VOID .hot_swap(STRING addr, STRING port)
+
+Dynamically replace the backend by a new one.
diff --git a/lib/libvmod_debug/vmod_debug_dyn.c b/lib/libvmod_debug/vmod_debug_dyn.c
new file mode 100644
index 0000000..7fe7ce1
--- /dev/null
+++ b/lib/libvmod_debug/vmod_debug_dyn.c
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dridi Boukelmoune <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "vcl.h"
+#include "vrt.h"
+
+#include "cache/cache.h"
+#include "cache/cache_director.h"
+#include "cache/cache_backend.h"
+
+#include "vsa.h"
+#include "vcc_if.h"
+
+struct vmod_debug_dyn {
+	unsigned		magic;
+#define VMOD_DEBUG_DYN_MAGIC	0x9b77ccbd
+	pthread_mutex_t		mtx;
+	char			*vcl_name;
+	struct director		*dir;
+	struct director		dyn_dir;
+};
+
+static unsigned __match_proto__(vdi_healthy_f)
+dyn_dir_healthy(const struct director *d, const struct busyobj *bo,
+    double *changed)
+{
+	struct vmod_debug_dyn *dyn;
+	unsigned healthy;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(dyn, d->priv, VMOD_DEBUG_DYN_MAGIC);
+
+	AZ(pthread_mutex_lock(&dyn->mtx));
+
+	AN(dyn->dir);
+	healthy = dyn->dir->healthy(dyn->dir, bo, changed);
+
+	AZ(pthread_mutex_unlock(&dyn->mtx));
+
+	return (healthy);
+}
+
+static const struct director * __match_proto__(vdi_resolve_f)
+dyn_dir_resolve(const struct director *d, struct worker *wrk,
+    struct busyobj *bo)
+{
+	struct vmod_debug_dyn *dyn;
+	struct director *simple;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(dyn, d->priv, VMOD_DEBUG_DYN_MAGIC);
+
+	AZ(pthread_mutex_lock(&dyn->mtx));
+
+	AN(dyn->dir);
+	simple = dyn->dir;
+
+	AZ(pthread_mutex_unlock(&dyn->mtx));
+
+	return (simple);
+}
+
+static void
+dyn_dir_init(VRT_CTX, struct vmod_debug_dyn *dyn,
+    VCL_STRING addr, VCL_STRING port)
+{
+	struct addrinfo hints, *res = NULL;
+	struct suckaddr *sa;
+	struct director *dir;
+	struct vrt_backend vrt;
+
+	CHECK_OBJ_NOTNULL(dyn, VMOD_DEBUG_DYN_MAGIC);
+	AN(addr);
+	AN(port);
+
+	INIT_OBJ(&vrt, VRT_BACKEND_MAGIC);
+	vrt.ipv4_addr = addr;
+	vrt.port = port;
+	vrt.vcl_name = dyn->vcl_name;
+	vrt.hosthdr = vrt.ipv4_addr;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET;
+	hints.ai_socktype = SOCK_STREAM;
+	AZ(getaddrinfo(vrt.ipv4_addr, vrt.port, &hints, &res));
+	AZ(res->ai_next);
+
+	sa = malloc(vsa_suckaddr_len);
+	AN(sa);
+	AN(VSA_Build(sa, res->ai_addr, res->ai_addrlen));
+	vrt.ipv4_suckaddr = sa;
+
+	freeaddrinfo(res);
+
+	dir = VRT_new_backend(ctx, &vrt);
+	AN(dir);
+
+	AZ(pthread_mutex_lock(&dyn->mtx));
+	if (dir != NULL && dyn->dir != NULL)
+		VRT_delete_backend(ctx, &dyn->dir);
+
+	if (dir != NULL) {
+		AZ(dyn->dir);
+		dyn->dir = dir;
+	}
+	AZ(pthread_mutex_unlock(&dyn->mtx));
+
+	free(sa);
+}
+
+VCL_VOID
+vmod_dyn__init(VRT_CTX, struct vmod_debug_dyn **dynp,
+    const char *vcl_name, VCL_STRING addr, VCL_STRING port)
+{
+	struct vmod_debug_dyn *dyn;
+
+	ASSERT_CLI();
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	AN(dynp);
+	AZ(*dynp);
+	AN(vcl_name);
+
+	ALLOC_OBJ(dyn, VMOD_DEBUG_DYN_MAGIC);
+	AN(dyn);
+	REPLACE(dyn->vcl_name, vcl_name);
+
+	INIT_OBJ(&dyn->dyn_dir, DIRECTOR_MAGIC);
+	dyn->dyn_dir.name = "debug.dyn";
+	dyn->dyn_dir.vcl_name = dyn->vcl_name;
+	dyn->dyn_dir.healthy = &dyn_dir_healthy;
+	dyn->dyn_dir.resolve = &dyn_dir_resolve;
+	dyn->dyn_dir.priv = dyn;
+
+	AZ(pthread_mutex_init(&dyn->mtx, NULL));
+
+	dyn_dir_init(ctx, dyn, addr, port);
+	if (dyn->dir)
+		*dynp = dyn;
+}
+
+VCL_VOID
+vmod_dyn__fini(struct vmod_debug_dyn **dynp)
+{
+	struct vmod_debug_dyn *dyn;
+
+	AN(dynp);
+	CAST_OBJ_NOTNULL(dyn, *dynp, VMOD_DEBUG_DYN_MAGIC);
+	/* at this point all backends will be deleted by the vcl */
+	free(dyn->vcl_name);
+	AZ(pthread_mutex_destroy(&dyn->mtx));
+	FREE_OBJ(dyn);
+	*dynp = NULL;
+}
+
+VCL_BACKEND __match_proto__()
+vmod_dyn_backend(VRT_CTX, struct vmod_debug_dyn *dyn)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(dyn, VMOD_DEBUG_DYN_MAGIC);
+	assert(dyn->dyn_dir.priv == dyn);
+	AN(dyn->dir);
+	return (&dyn->dyn_dir);
+}
+
+VCL_VOID
+vmod_dyn_hot_swap(VRT_CTX, struct vmod_debug_dyn *dyn,
+    VCL_STRING addr, VCL_STRING port)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(dyn, VMOD_DEBUG_DYN_MAGIC);
+	dyn_dir_init(ctx, dyn, addr, port);
+}
-- 
2.1.0

From 14738c029ad1129493c07d12d525b0d7e0fc0112 Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <[email protected]>
Date: Tue, 7 Jul 2015 22:31:50 +0200
Subject: [PATCH 3/5] Resurrect VSA_Len

Partial revert of 0848a1fe471c40a49e77df941c8e2cc133170ff7.
---
 include/vsa.h        |  1 +
 lib/libvarnish/vsa.c | 15 +++++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/include/vsa.h b/include/vsa.h
index 185be03..996afde 100644
--- a/include/vsa.h
+++ b/include/vsa.h
@@ -34,6 +34,7 @@ struct suckaddr;
 extern const int vsa_suckaddr_len;
 
 int VSA_Sane(const struct suckaddr *);
+socklen_t VSA_Len(const struct suckaddr *);
 unsigned VSA_Port(const struct suckaddr *);
 int VSA_Compare(const struct suckaddr *, const struct suckaddr *);
 struct suckaddr *VSA_Clone(const struct suckaddr *sua);
diff --git a/lib/libvarnish/vsa.c b/lib/libvarnish/vsa.c
index 43d9a86..fc25cdd 100644
--- a/lib/libvarnish/vsa.c
+++ b/lib/libvarnish/vsa.c
@@ -306,6 +306,21 @@ VSA_Sane(const struct suckaddr *sua)
 	}
 }
 
+socklen_t
+VSA_Len(const struct suckaddr *sua)
+{
+       CHECK_OBJ_NOTNULL(sua, SUCKADDR_MAGIC);
+
+       switch(sua->sa.sa_family) {
+               case PF_INET:
+                       return (sizeof(sua->sa4));
+               case PF_INET6:
+                       return (sizeof(sua->sa6));
+               default:
+                       return (0);
+       }
+}
+
 int
 VSA_Compare(const struct suckaddr *sua1, const struct suckaddr *sua2)
 {
-- 
2.1.0

From 5224cfb8a96f4a25ea6f3861a8b341b6fadc5588 Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <[email protected]>
Date: Wed, 8 Jul 2015 19:49:14 +0200
Subject: [PATCH 4/5] Resurrect the DNS director

It doesn't seem IPv6-ready, always yielding a 503 error.
---
 lib/libvmod_directors/Makefile.am |   3 +-
 lib/libvmod_directors/dns.c       | 384 ++++++++++++++++++++++++++++++++++++++
 lib/libvmod_directors/vmod.vcc    |  43 +++++
 3 files changed, 429 insertions(+), 1 deletion(-)
 create mode 100644 lib/libvmod_directors/dns.c

diff --git a/lib/libvmod_directors/Makefile.am b/lib/libvmod_directors/Makefile.am
index 64b72a8..bc0261f 100644
--- a/lib/libvmod_directors/Makefile.am
+++ b/lib/libvmod_directors/Makefile.am
@@ -17,6 +17,7 @@ libvmod_directors_la_LDFLAGS = $(AM_LDFLAGS) -module -export-dynamic -avoid-vers
 libvmod_directors_la_SOURCES = \
 	vdir.c \
 	vdir.h \
+	dns.c \
 	fall_back.c \
 	hash.c \
 	random.c \
@@ -27,7 +28,7 @@ nodist_libvmod_directors_la_SOURCES = \
 	vcc_if.h
 
 # BUILT_SOURCES is only a hack and dependency tracking does not help for the first build
-vdir.lo fall_back.lo hash.lo random.lo round_robin.lo: vcc_if.h
+vdir.lo dns.lo fall_back.lo hash.lo random.lo round_robin.lo: vcc_if.h
 
 vcc_if.c vcc_if.h vmod_directors.rst vmod_directors.man.rst: $(vmodtool) $(vmod_srcdir)/vmod.vcc
 	@PYTHON@ $(vmodtool) $(vmodtoolargs) $(vmod_srcdir)/vmod.vcc
diff --git a/lib/libvmod_directors/dns.c b/lib/libvmod_directors/dns.c
new file mode 100644
index 0000000..b4f21a5
--- /dev/null
+++ b/lib/libvmod_directors/dns.c
@@ -0,0 +1,384 @@
+/*-
+ * Copyright (c) 2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dridi Boukelmoune <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "vrt.h"
+#include "vcl.h"
+
+#include "cache/cache.h"
+#include "cache/cache_director.h"
+#include "cache/cache_backend.h"
+
+#include "vsa.h"
+#include "vtim.h"
+#include "vcc_if.h"
+
+struct vmod_directors_dns;
+
+struct dns_director {
+	struct vmod_directors_dns	*dns;
+	struct vrt_backend		*vrt;
+	struct director			*simple_dir;
+	VTAILQ_ENTRY(dns_director)	list;
+	char				ip_addr[64];
+	unsigned			mark;
+};
+
+struct vmod_directors_dns {
+	unsigned			magic;
+#define VMOD_DIRECTORS_DNS_MAGIC	0x8a3e7fd1
+	pthread_t			thread;
+	pthread_mutex_t			mtx;
+	pthread_cond_t			cond;
+	char				*vcl_name;
+	char				*addr;
+	char				*port;
+	double				ttl;
+	VTAILQ_HEAD(,dns_director)	director_list;
+	struct dns_director		*next;
+	struct director			dir;
+	struct vcl			*vcl;
+	int				active;
+	unsigned			mark;
+};
+
+static void
+vmod_dns_rotate(struct vmod_directors_dns *dns)
+{
+	if (dns->next != NULL)
+		dns->next = dns->next->list.vtqe_next;
+
+	if (dns->next == NULL)
+		dns->next = dns->director_list.vtqh_first;
+}
+
+static int
+vmod_dns_notfound(struct vmod_directors_dns *dns, struct suckaddr *sa)
+{
+	struct dns_director *d;
+
+	AN(sa);
+
+	VTAILQ_FOREACH(d, &dns->director_list, list) {
+		if (d->mark == dns->mark)
+			continue;
+
+		if (d->vrt->ipv4_suckaddr != NULL &&
+		    VSA_Compare(d->vrt->ipv4_suckaddr, sa))
+			continue;
+
+		if (d->vrt->ipv6_suckaddr != NULL &&
+		    VSA_Compare(d->vrt->ipv6_suckaddr, sa))
+			continue;
+
+		d->mark = dns->mark;
+		return (0);
+	}
+
+	return (1);
+}
+
+static unsigned __match_proto__(vdi_healthy_f)
+vmod_dns_healthy(const struct director *d, const struct busyobj *bo,
+    double *changed)
+{
+	struct vmod_directors_dns *dns;
+	unsigned healthy;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(dns, d->priv, VMOD_DIRECTORS_DNS_MAGIC);
+
+	AZ(pthread_mutex_lock(&dns->mtx));
+	(void)changed;
+	healthy = (dns->next != NULL);
+	AZ(pthread_mutex_unlock(&dns->mtx));
+
+	// XXX we can do better than that
+	return (healthy);
+}
+
+static const struct director * __match_proto__(vdi_resolve_f)
+vmod_dns_resolve(const struct director *d, struct worker *wrk,
+    struct busyobj *bo)
+{
+	struct vmod_directors_dns *dns;
+	struct director *dir = NULL;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(dns, d->priv, VMOD_DIRECTORS_DNS_MAGIC);
+
+	pthread_mutex_lock(&dns->mtx);
+	if (dns->next != NULL) {
+		dir = dns->next->simple_dir;
+		vmod_dns_rotate(dns);
+	}
+	AZ(pthread_mutex_unlock(&dns->mtx));
+
+	return (dir);
+}
+
+static void
+vmod_dns_del(VRT_CTX, struct dns_director *dir)
+{
+	struct dns_director *next;
+	struct vmod_directors_dns *dns;
+
+	AN(dir);
+	AN(dir->dns);
+
+	dns = dir->dns;
+	next = dns->next;
+	AN(next);
+
+	if (dir == next) {
+		vmod_dns_rotate(dns);
+		if (next == dns->next)
+			dns->next = NULL;
+	}
+
+	VTAILQ_REMOVE(&dir->dns->director_list, dir, list);
+	if (ctx) {
+		AN(ctx->vcl);
+		VRT_delete_backend(ctx, &dir->simple_dir);
+	}
+	free(dir);
+}
+
+static void
+vmod_dns_add(VRT_CTX, struct vmod_directors_dns *dns,
+    struct suckaddr *sa)
+{
+	struct dns_director *dir;
+	const unsigned char *ptr = NULL;
+	int af;
+	socklen_t len;
+
+	dir = malloc(sizeof *dir);
+	AN(dir);
+	dir->dns = dns;
+	dir->mark = dns->mark;
+
+	ALLOC_OBJ(dir->vrt, VRT_BACKEND_MAGIC);
+	AN(dir->vrt);
+	dir->vrt->port = dns->port;
+	dir->vrt->vcl_name = dns->vcl_name;
+	dir->vrt->hosthdr = dns->addr;
+
+	af = VRT_VSA_GetPtr(sa, &ptr);
+	AN(ptr);
+	len = VSA_Len(sa);
+	AN(inet_ntop(af, ptr, dir->ip_addr, len));
+
+	switch (af) {
+	case AF_INET:
+		dir->vrt->ipv4_suckaddr = sa;
+		dir->vrt->ipv4_addr = dir->ip_addr;
+		break;
+	case AF_INET6:
+		dir->vrt->ipv6_suckaddr = sa;
+		dir->vrt->ipv6_addr = dir->ip_addr;
+		break;
+	default:
+		WRONG("unexpected family");
+	}
+
+	dir->simple_dir = VRT_new_backend(ctx, dir->vrt);
+	AN(dir->simple_dir);
+
+	VTAILQ_INSERT_TAIL(&dns->director_list, dir, list);
+}
+
+static void
+vmod_dns_update(struct vmod_directors_dns *dns, struct addrinfo *addr)
+{
+	struct suckaddr *sa;
+	struct dns_director *d, *d2;
+	struct vrt_ctx ctx;
+
+	AN(addr);
+
+	INIT_OBJ(&ctx, VRT_CTX_MAGIC);
+	ctx.vcl = dns->vcl;
+
+	dns->mark++;
+	while (addr) {
+		switch (addr->ai_family) {
+		case AF_INET:
+		case AF_INET6:
+			sa = malloc(vsa_suckaddr_len);
+			AN(sa);
+			AN(VSA_Build(sa, addr->ai_addr, addr->ai_addrlen));
+			if (vmod_dns_notfound(dns, sa))
+				vmod_dns_add(&ctx, dns, sa);
+		}
+		addr = addr->ai_next;
+	}
+
+	VTAILQ_FOREACH_SAFE(d, &dns->director_list, list, d2)
+		if (d->mark != dns->mark)
+			vmod_dns_del(&ctx, d);
+
+	vmod_dns_rotate(dns);
+}
+
+static void*
+vmod_dns_lookup_thread(void *obj)
+{
+	struct vmod_directors_dns *dns;
+	struct timespec ts;
+	struct addrinfo hints, *res;
+	double deadline;
+	int ret;
+
+	CAST_OBJ_NOTNULL(dns, obj, VMOD_DIRECTORS_DNS_MAGIC);
+
+	memset(&hints, 0, sizeof hints);
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = AF_UNSPEC;
+
+	while (dns->active) {
+		ret = getaddrinfo(dns->addr, dns->port, &hints, &res);
+
+		AZ(pthread_mutex_lock(&dns->mtx));
+
+		if (ret == 0) {
+			vmod_dns_update(dns, res);
+			freeaddrinfo(res);
+		}
+		else
+			VSL(SLT_Error, 0, "DNS lookup failed: %d (%s)",
+			    ret, gai_strerror(ret));
+
+		deadline = VTIM_real() + dns->ttl;
+		ts = VTIM_timespec(deadline);
+		ret = pthread_cond_timedwait(&dns->cond, &dns->mtx, &ts);
+		assert(ret == 0 || ret == ETIMEDOUT);
+
+		AZ(pthread_mutex_unlock(&dns->mtx));
+	}
+
+	return (NULL);
+}
+
+VCL_VOID __match_proto__()
+vmod_dns__init(VRT_CTX, struct vmod_directors_dns **dnsp, const char *vcl_name,
+    VCL_STRING addr, VCL_STRING port)
+{
+	struct vmod_directors_dns *dns;
+
+	ASSERT_CLI();
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	AN(dnsp);
+	AZ(*dnsp);
+	AN(vcl_name);
+	AN(addr);
+	AN(port);
+
+	ALLOC_OBJ(dns, VMOD_DIRECTORS_DNS_MAGIC);
+	AN(dns);
+	VTAILQ_INIT(&dns->director_list);
+	REPLACE(dns->vcl_name, vcl_name);
+	REPLACE(dns->addr, addr);
+	REPLACE(dns->port, port);
+	dns->vcl = ctx->vcl;
+	dns->ttl = 3600;
+	dns->active = 1;
+
+	INIT_OBJ(&dns->dir, DIRECTOR_MAGIC);
+	dns->dir.name = "dns";
+	dns->dir.vcl_name = dns->vcl_name;
+	dns->dir.healthy = vmod_dns_healthy;
+	dns->dir.resolve = vmod_dns_resolve;
+	dns->dir.priv = dns;
+
+	AZ(pthread_mutex_init(&dns->mtx, NULL));
+	AZ(pthread_cond_init(&dns->cond, NULL));
+	AZ(pthread_create(&dns->thread, NULL, &vmod_dns_lookup_thread, dns));
+
+	*dnsp = dns;
+}
+
+VCL_VOID __match_proto__()
+vmod_dns__fini(struct vmod_directors_dns **dnsp)
+{
+	struct vmod_directors_dns *dns;
+
+	AN(dnsp);
+	dns = *dnsp;
+	*dnsp = NULL;
+
+	CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC);
+
+	AZ(pthread_mutex_lock(&dns->mtx));
+	dns->active = 0;
+	AZ(pthread_mutex_unlock(&dns->mtx));
+
+	AZ(pthread_cond_signal(&dns->cond));
+	AZ(pthread_join(dns->thread, NULL));
+
+	/* backends will be deleted by the VCL, pass NULL struct ctx */
+	while (dns->director_list.vtqh_first != NULL)
+		vmod_dns_del(NULL, dns->director_list.vtqh_first);
+
+	AZ(pthread_cond_destroy(&dns->cond));
+	AZ(pthread_mutex_destroy(&dns->mtx));
+	free(dns->vcl_name);
+	free(dns->addr);
+	free(dns->port);
+	FREE_OBJ(dns);
+}
+
+VCL_VOID __match_proto__()
+vmod_dns_set_ttl(VRT_CTX, struct vmod_directors_dns *dns, VCL_DURATION ttl)
+{
+
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC);
+	assert(ttl > 0);
+	dns->ttl = ttl;
+}
+
+VCL_BACKEND __match_proto__()
+vmod_dns_backend(VRT_CTX, struct vmod_directors_dns *dns)
+{
+
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC);
+	return (&dns->dir);
+}
diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc
index 2e958a7..411a87d 100644
--- a/lib/libvmod_directors/vmod.vcc
+++ b/lib/libvmod_directors/vmod.vcc
@@ -180,3 +180,46 @@ Description
 Example
 	# pick a backend based on the cookie header from the client
 	set req.backend_hint = vdir.backend(req.http.cookie);
+
+$Object dns(STRING, STRING)
+
+Description
+	Create a DNS director.
+
+	The director creates backends with DNS lookups and chooses them in a
+	round robin fashion. An ACL (Access Control List) can be used as a
+	white-list to restrict the use of resolved addresses.
+
+Example
+	::
+
+	   acl www_backends {
+	   	"192.168.15.0"/24;
+	   	!"192.168.15.1";
+	   }
+
+	   sub vcl_init {
+	   	new www_dir = directors.dns("www.example.com", "80");
+	   	www_dir.restrict_to(www_backends);
+	   	www_dir.set_ttl(5m);
+	   }
+
+	   sub vcl_recv {
+	   	set req.backend_hint = www_dir.backend();
+	   }
+
+# TODO $Method VOID .restrict_to(ACL)
+#
+#Description
+#	Restrict usage of resolved IP addresses to a named ACL.
+
+$Method VOID .set_ttl(DURATION)
+
+Description
+	Set the DNS lookup TTL (defaults to one hour).
+
+$Method BACKEND .backend()
+
+Description
+	Pick a backend from the director.
+
-- 
2.1.0

_______________________________________________
varnish-dev mailing list
[email protected]
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-dev

Reply via email to