Recursor: handle NS responses with a different type and no SOA

e.g. `dig m.wikipedia.org. NS` replies with `m.wikipedia.org. CNAME
dyna.wikimedia.org` and NO SOA record. the CNAME is worthless, but it's
evidence that the nameserver we queried is in fact authoritative for
`m.wikipedia.org.`.
This commit is contained in:
2024-07-17 15:12:15 +00:00
parent 2f6f1eab4a
commit b4c553e79c

View File

@@ -357,6 +357,7 @@ impl<P: ConnectionProvider + Default> Recursor<P> {
Ok(r) => {
let mut r = r.into_message();
info!("response: {}", r.header());
let mut got_answer_of_any_type = false;
let mut soa = None;
let records = r
.take_answers()
@@ -374,6 +375,9 @@ impl<P: ConnectionProvider + Default> Recursor<P> {
true
}
}).map(|x| {
if x.name() == query.name() {
got_answer_of_any_type = true;
}
if x.record_type().is_soa() {
soa = Some(x.name().clone());
}
@@ -381,27 +385,31 @@ impl<P: ConnectionProvider + Default> Recursor<P> {
});
let lookup = self.record_cache.insert_records(query.clone(), records, now);
let lookup = lookup.or_else(|| {
if query.query_type().is_ns() || query.query_type().is_cname() || query.query_type().is_any() {
None
} else {
let lookup = match lookup {
Some(q) => Some(Ok(q)),
None => if query.query_type().is_ns() {
debug!("no records for {:?}: try SOA {:?}", &query, &soa);
if let Some(soa_name) = soa {
Some(Err(Error::from(ErrorKind::Forward(soa_name))))
} else if got_answer_of_any_type {
// if we query `@ns1 foo.example.org. NS` and get a response for
// `foo.example.org.` of a different type (e.g. CNAME), we learn that
// `ns1` is authoritative for the zone to which `foo.example.org.`
// records (of other types) belong.
Some(Err(Error::from(ErrorKind::Forward(ns.zone().clone()))))
} else {
None
}
} else if !(query.query_type().is_ns() || query.query_type().is_cname() || query.query_type().is_any()) {
debug!("no records for {:?}: checking for cached CNAME", &query);
let mut cname_query = query.clone();
cname_query.set_query_type(RecordType::CNAME);
self.record_cache.get(&cname_query, now).and_then(Result::ok)
self.record_cache.get(&cname_query, now).and_then(|r| Some(r)).map(|m| m.map_err(Error::from))
} else {
None
}
});
lookup.ok_or_else(|| {
if query.query_type().is_ns() {
debug!("no records for {:?}: returning SOA {:?}", &query, &soa);
if let Some(soa_name) = soa {
return Error::from(ErrorKind::Forward(soa_name));
}
}
Error::from("no records found")
})
};
lookup.unwrap_or_else(|| Err(Error::from("no records found")))
}
Err(e) => {
warn!("lookup error: {e}");