#define DNS_FETCHOPT_NOEDNS0 0x00000008 /*%< Do not use EDNS. */
#define DNS_FETCHOPT_FORWARDONLY 0x00000010 /*%< Only use forwarders. */
#define DNS_FETCHOPT_NOVALIDATE 0x00000020 /*%< Disable validation. */
-#define DNS_FETCHOPT_OBSOLETE1 0x00000040 /*%< Obsolete */
-#define DNS_FETCHOPT_WANTNSID 0x00000080 /*%< Request NSID */
-#define DNS_FETCHOPT_PREFETCH 0x00000100 /*%< Do prefetch */
-#define DNS_FETCHOPT_NOCDFLAG 0x00000200 /*%< Don't set CD flag. */
-#define DNS_FETCHOPT_NONTA 0x00000400 /*%< Ignore NTA table. */
+#define DNS_FETCHOPT_VALIDATING \
+ 0x00000040 /*%< This fetch was created by a \
+ * validator; its 'arg' is a \
+ * dns_validator_t object, and \
+ * can be stored and used for \
+ * validator loop detection. */
+#define DNS_FETCHOPT_WANTNSID 0x00000080 /*%< Request NSID */
+#define DNS_FETCHOPT_PREFETCH 0x00000100 /*%< Do prefetch */
+#define DNS_FETCHOPT_NOCDFLAG 0x00000200 /*%< Don't set CD flag. */
+#define DNS_FETCHOPT_NONTA 0x00000400 /*%< Ignore NTA table. */
/* RESERVED ECS 0x00000000 */
/* RESERVED ECS 0x00001000 */
/* RESERVED ECS 0x00002000 */
dns_resolver_setfuzzing(void);
#endif /* ifdef ENABLE_AFL */
+dns_validator_t *
+dns_fetch_validator(dns_fetch_t *fetch);
+/*%<
+ * If a fetch was created with DNS_FETCHOPT_VALIDATING, then the
+ * validator that called it will have been passed in as the 'arg'
+ * parameter to dns_resolver_createfetch(). When another fetch is
+ * joined to the original fetch, this function allows the new caller
+ * to retrieve a pointer to the original caller; this is used in the
+ * validator to detect dependency loops (i.e., conditions in which
+ * a validator creates a fetch which cannot succeed because it's
+ * waiting on the same validator).
+ *
+ * Requires:
+ * \li 'fetch' is valid.
+ */
ISC_LANG_ENDDECLS
*/
dns_rdatatype_t origtype;
+ /*
+ * Original fetch that triggered this validation.
+ */
+ dns_fetch_t *fetch;
+
/*
* Rdata and RRSIG (if any) for positive responses.
*/
unsigned int options;
unsigned int attributes;
dns_validatorevent_t *event;
+ dns_fetch_t *caller;
dns_fetch_t *fetch;
dns_validator_t *subvalidator;
dns_validator_t *parent;
dns_rdatatype_t origtype, dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset, dns_message_t *message,
unsigned int options, isc_task_t *task,
- isc_taskaction_t action, void *arg,
+ isc_taskaction_t action, void *arg, dns_fetch_t *fetch,
dns_validator_t **validatorp);
/*%<
* Start a DNSSEC validation.
*
* The validation is performed in the context of 'view'.
*
+ * If set, 'fetch' references the resolver fetch that triggered
+ * this validation.
+ *
* When the validation finishes, a dns_validatorevent_t with
* the given 'action' and 'arg' are sent to 'task'.
* Its 'result' field will be ISC_R_SUCCESS iff the
/*% Not locked. */
unsigned int magic;
dns_resolver_t *res;
+ dns_fetch_t *fetch;
dns_fixedname_t fname;
dns_name_t *name;
dns_rdatatype_t type;
isc_mem_t *mctx;
isc_stdtime_t now;
isc_task_t *task;
+ dns_validator_t *pval;
/* Atomic */
isc_refcount_t references;
if (type == dns_rdatatype_cname) {
origtype = fctx->type;
}
- result = dns_validator_create(
- fctx->res->view, name, type, origtype, rdataset, sigrdataset,
- message, valoptions, task, validated, valarg, &validator);
+ result = dns_validator_create(fctx->res->view, name, type, origtype,
+ rdataset, sigrdataset, message,
+ valoptions, task, validated, valarg,
+ fctx->fetch, &validator);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (result == ISC_R_SUCCESS) {
inc_stats(fctx->res, dns_resstatscounter_val);
dns_rdatatype_t type, const dns_name_t *domain,
dns_rdataset_t *nameservers, const isc_sockaddr_t *client,
unsigned int options, unsigned int bucketnum, unsigned int depth,
- isc_counter_t *qc, fetchctx_t **fctxp) {
+ isc_counter_t *qc, dns_fetch_t *fetch, dns_validator_t *pval,
+ fetchctx_t **fctxp) {
fetchctx_t *fctx = NULL;
isc_result_t result;
isc_result_t iresult;
fctx = isc_mem_get(res->mctx, sizeof(*fctx));
*fctx = (fetchctx_t){
+ .fetch = fetch,
.type = type,
.qmintype = type,
.options = options,
+ .pval = pval,
.task = task,
.bucketnum = bucketnum,
.dbucketnum = RES_NOBUCKET,
}
if (fctx == NULL) {
+ void *validator = NULL;
+
+ if ((options & DNS_FETCHOPT_VALIDATING) != 0) {
+ options &= ~DNS_FETCHOPT_VALIDATING;
+ validator = arg;
+ }
+
result = fctx_create(res, task, name, type, domain, nameservers,
client, options, bucketnum, depth, qc,
- &fctx);
+ fetch, validator, &fctx);
if (result != ISC_R_SUCCESS) {
goto unlock;
}
resolver->nonbackofftries = tries;
}
+
+dns_validator_t *
+dns_fetch_validator(dns_fetch_t *fetch) {
+ fetchctx_t *fctx = NULL;
+
+ REQUIRE(DNS_FETCH_VALID(fetch));
+ fctx = fetch->private;
+ REQUIRE(VALID_FCTX(fctx));
+
+ return (fctx->pval);
+}
dns_validator_t *parent = NULL;
for (parent = val; parent != NULL; parent = parent->parent) {
- if (parent->event == NULL) {
- continue;
- }
-
- if ((parent->event->type == type ||
+ if (parent->event != NULL &&
+ (parent->event->type == type ||
(parent->event->type == dns_rdatatype_cname &&
parent->event->origtype == type)) &&
dns_name_equal(parent->event->name, name) &&
"deadlock: aborting validation");
return (true);
}
+
+ if (parent->caller != NULL) {
+ dns_validator_t *pv =
+ dns_fetch_validator(parent->caller);
+
+ /*
+ * If the fetch that started this validator
+ * was started by another validator, we need to
+ * recursively check for a possible deadlock with
+ * that one (and its parents) too.
+ */
+ if (pv != NULL &&
+ check_deadlock(pv, name, type, NULL, NULL)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "fetch/validator "
+ "loop detected: "
+ "aborting validation");
+ return (true);
+ }
+ }
}
return (false);
}
static inline isc_result_t
create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
isc_taskaction_t callback, const char *caller) {
- unsigned int fopts = 0;
+ isc_result_t result;
+ unsigned int fopts = DNS_FETCHOPT_VALIDATING;
disassociate_rdatasets(val);
}
validator_logcreate(val, name, type, caller, "fetch");
- return (dns_resolver_createfetch(
+ result = dns_resolver_createfetch(
val->view->resolver, name, type, NULL, NULL, NULL, NULL, 0,
fopts, 0, NULL, val->event->ev_sender, callback, val,
- &val->frdataset, &val->fsigrdataset, &val->fetch));
+ &val->frdataset, &val->fsigrdataset, &val->fetch);
+
+ return (result);
}
/*%
validator_logcreate(val, name, type, caller, "validator");
result = dns_validator_create(val->view, name, type, dns_rdatatype_none,
rdataset, sig, NULL, vopts, val->task,
- action, val, &val->subvalidator);
+ action, val, NULL, &val->subvalidator);
if (result == ISC_R_SUCCESS) {
val->subvalidator->parent = val;
val->subvalidator->depth = val->depth + 1;
dns_rdatatype_t origtype, dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset, dns_message_t *message,
unsigned int options, isc_task_t *task,
- isc_taskaction_t action, void *arg,
+ isc_taskaction_t action, void *arg, dns_fetch_t *fetch,
dns_validator_t **validatorp) {
isc_result_t result = ISC_R_FAILURE;
dns_validator_t *val;
val = isc_mem_get(view->mctx, sizeof(*val));
*val = (dns_validator_t){ .event = event,
.options = options,
+ .caller = fetch,
.task = task,
.action = action,
.arg = arg };