bool get_nameservers; /* get a new NS rrset at
* zone cut? */
bool resend; /* resend this query? */
+ bool secured; /* message was signed or had a valid cookie */
bool nextitem; /* invalid response; keep
* listening for the correct one */
bool truncated; /* response was truncated */
resquery_t *query = rctx->query;
/*
- * If TSIG signed, sent via TCP, or cookie present,
- * no need to continue.
+ * If the message was secured or TCP is already in the
+ * retry flags, no need to continue.
*/
- if (dns_message_gettsig(query->rmessage, NULL) != NULL ||
- query->rmessage->cc_ok || query->rmessage->cc_bad ||
- (rctx->retryopts & DNS_FETCHOPT_TCP) != 0)
- {
+ if (rctx->secured || (rctx->retryopts & DNS_FETCHOPT_TCP) != 0) {
return ISC_R_SUCCESS;
}
return ISC_R_SUCCESS;
}
+static bool
+rctx_need_tcpretry(respctx_t *rctx) {
+ resquery_t *query = rctx->query;
+ if ((rctx->retryopts & DNS_FETCHOPT_TCP) != 0) {
+ /* TCP is already in the retry flags */
+ return false;
+ }
+
+ /*
+ * If the message was secured, no need to continue.
+ */
+ if (rctx->secured) {
+ return false;
+ }
+
+ /*
+ * Currently the only extra reason why we might need to
+ * retry a UDP response over TCP is a DNAME in the message.
+ */
+ if (dns_message_hasdname(query->rmessage)) {
+ return true;
+ }
+
+ return false;
+}
+
+static isc_result_t
+rctx_tcpretry(respctx_t *rctx) {
+ /*
+ * Do we need to retry a UDP response over TCP?
+ */
+ if (rctx_need_tcpretry(rctx)) {
+ rctx->retryopts |= DNS_FETCHOPT_TCP;
+ rctx->resend = true;
+ rctx_done(rctx, ISC_R_SUCCESS);
+ return ISC_R_COMPLETE;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
static void
resquery_response_continue(void *arg, isc_result_t result) {
respctx_t *rctx = arg;
goto cleanup;
}
+ /*
+ * Remember whether this message was signed or had a
+ * valid client cookie; if not, we may need to retry over
+ * TCP later.
+ */
+ if (query->rmessage->cc_ok || query->rmessage->tsig != NULL ||
+ query->rmessage->sig0 != NULL)
+ {
+ rctx->secured = true;
+ }
+
/*
* The dispatcher should ensure we only get responses with QR
* set.
INSIST((query->rmessage->flags & DNS_MESSAGEFLAG_QR) != 0);
/*
- * Check for cookie issues.
+ * Check for cookie issues; if found, maybe retry over TCP.
*/
result = rctx_cookiecheck(rctx);
if (result == ISC_R_COMPLETE) {
goto cleanup;
}
+ /*
+ * Check whether we need to retry over TCP for some other reason.
+ */
+ result = rctx_tcpretry(rctx);
+ if (result == ISC_R_COMPLETE) {
+ goto cleanup;
+ }
+
/*
* Check for EDNS issues.
*/
}
/*
- * Cache records in the authority section, if
- * there are any suitable for caching.
+ * Cache records in the authority section, if there are
+ * any suitable for caching.
*/
rctx_authority_positive(rctx);
/*
* rctx_authority_positive():
- * Examine the records in the authority section (if there are any) for a
- * positive answer. We expect the names for all rdatasets in this
- * section to be subdomains of the domain being queried; any that are
- * not are skipped. We expect to find only *one* owner name; any names
- * after the first one processed are ignored. We expect to find only
- * rdatasets of type NS; all others are ignored. Whatever remains can
- * be cached at trust level authauthority or additional
- * (depending on whether the AA bit was set on the answer).
+ * If a positive answer was received over TCP or secured with a cookie
+ * or TSIG, examine the authority section. We expect names for all
+ * rdatasets in this section to be subdomains of the domain being queried;
+ * any that are not are skipped. We expect to find only *one* owner name;
+ * any names after the first one processed are ignored. We expect to find
+ * only rdatasets of type NS; all others are ignored. Whatever remains can
+ * be cached at trust level authauthority or additional (depending on
+ * whether the AA bit was set on the answer).
*/
static void
rctx_authority_positive(respctx_t *rctx) {
fetchctx_t *fctx = rctx->fctx;
-
dns_message_t *msg = rctx->query->rmessage;
+
+ /* If it's spoofable, don't cache it. */
+ if (!rctx->secured && (rctx->query->options & DNS_FETCHOPT_TCP) == 0) {
+ return;
+ }
+
MSG_SECTION_FOREACH(msg, DNS_SECTION_AUTHORITY, name) {
if (!name_external(name, dns_rdatatype_ns, rctx) &&
dns_name_issubdomain(fctx->name, name))