Skip to content

Commit

Permalink
Add resolver fallback to the null resolver (getaddrinfo)
Browse files Browse the repository at this point in the history
This enables a best-effort, two-step resolution when a resolver is
configured:
- if the DNS resolver returns any domain, that record is used with its
  corresponding TTL
- otherwise, the module tries resolving the name with the default, NULL
  resolver, which is just a wrapper over getaddrinfo
  * if the resolution succeeds, this was likely a domain in /etc/hosts
    or an IP literal, which was not known by the DNS server. The
    associated record has no TTL value and uses the default director
    TTL.
  * if the resolution fails, this was likely a bad domain. No records
    are stored and a new request is made after the default director TTL,
    as was done previously.
  • Loading branch information
delthas committed Jul 25, 2023
1 parent 2d51da7 commit a84e31f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 30 deletions.
27 changes: 27 additions & 0 deletions src/tests/resolver/fallback.vtc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
varnishtest "falling back from a resolver to getaddrinfo"

server s1 {
rxreq
txresp
} -start

varnish v1 -vcl+backend {
import ${vmod_dynamic};

sub vcl_init {
new r1 = dynamic.resolver();
new d1 = dynamic.director(
resolver = r1.use());
d1.debug(true);
}

sub vcl_recv {
set req.backend_hint = d1.backend(host = "img.localhost", port = "${s1_port}");
}
} -start

client c1 {
txreq
rxresp
expect resp.status == 200
} -run
75 changes: 45 additions & 30 deletions src/vmod_dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ dom_lookup_thread(void *priv)
struct vrt_ctx ctx;
vtim_real lookup, results, update;
const struct res_cb *res;
struct vmod_dynamic_resolver *res_instance;
void *res_priv = NULL;
int ret;

Expand All @@ -783,49 +784,63 @@ dom_lookup_thread(void *priv)

obj = dom->obj;
res = obj->resolver;
res_instance = obj->resolver_inst;

Lck_Lock(&dom->mtx);
assert(dom->status == DYNAMIC_ST_STARTING);
while (dom->status <= DYNAMIC_ST_ACTIVE) {
Lck_Unlock(&dom->mtx);

lookup = VTIM_real();
if (lookup > dom->expires) {
Lck_Lock(&obj->domains_mtx);
while (1) { // Loop run at most twice: one for the resolve, and one for the fallaback gai resolver
lookup = VTIM_real();
if (lookup > dom->expires) {
LOG(NULL, SLT_VCL_Log, dom, "%s", "timeout");
dom->expires = HUGE_VAL;
VRBT_REMOVE(dom_tree_head, &obj->ref_domains,
dom);
VTAILQ_INSERT_TAIL(&obj->unref_domains, dom,
link.list);
Lck_Lock(&obj->domains_mtx);
if (lookup > dom->expires) {
LOG(NULL, SLT_VCL_Log, dom, "%s", "timeout");
dom->expires = HUGE_VAL;
VRBT_REMOVE(dom_tree_head, &obj->ref_domains,
dom);
VTAILQ_INSERT_TAIL(&obj->unref_domains, dom,
link.list);
}
Lck_Unlock(&obj->domains_mtx);
}
Lck_Unlock(&obj->domains_mtx);
}

dynamic_timestamp(dom, "Lookup", lookup, 0., 0.);
dynamic_timestamp(dom, "Lookup", lookup, 0., 0.);

ret = res->lookup(obj->resolver_inst, dom->addr,
dom_port(dom), &res_priv);
ret = res->lookup(res_instance, dom->addr,
dom_port(dom), &res_priv);

results = VTIM_real();
dynamic_timestamp(dom, "Results", results, results - lookup,
results - lookup);
results = VTIM_real();
dynamic_timestamp(dom, "Results", results, results - lookup,
results - lookup);

if (ret == 0) {
dom_update(dom, res, res_priv, results);
update = VTIM_real();
dynamic_timestamp(dom, "Update", update,
update - lookup, update - results);
} else {
LOG(&ctx, SLT_Error, dom, "%s %d (%s)",
res->name, ret, res->strerror(ret));
dom->deadline = results + obj->retry_after;
dbg_res_details(NULL, dom->obj, res, res_priv);
}
if (ret == 0) {
dom_update(dom, res, res_priv, results);
update = VTIM_real();
dynamic_timestamp(dom, "Update", update,
update - lookup, update - results);
} else {
LOG(&ctx, SLT_Error, dom, "%s %d (%s)",
res->name, ret, res->strerror(ret));
dbg_res_details(NULL, dom->obj, res, res_priv);
}

res->fini(&res_priv);
AZ(res_priv);

res->fini(&res_priv);
AZ(res_priv);
if (ret == 0) {
break;
}
if (res == &res_gai) {
/* Failed with fallback getaddrinfo resolver: update deadline with default TTL and break */
dom->deadline = results + obj->retry_after;
break;
}
/* Failed with custom resolver: try again with fallback getaddrinfo resolver */
res = &res_gai;
res_instance = NULL;
}

Lck_Lock(&dom->mtx);

Expand Down
3 changes: 3 additions & 0 deletions src/vmod_dynamic.vcc
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ Parameters:
The argument to the *resolver* parameter must be the return
value of the `xresolver.use()`_ method.

If the resolver does not find any addresses, a fallback
attempt to resolve the address without the resolver is made.

.. _ttl_from:

- *ttl_from*
Expand Down

0 comments on commit a84e31f

Please sign in to comment.