recursor: define the bare minimum integration test
This commit is contained in:
parent
591a4a9fb2
commit
ec4e22817a
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -966,6 +966,7 @@ dependencies = [
|
|||
"futures",
|
||||
"hickory-client",
|
||||
"hickory-proto",
|
||||
"hickory-recursor",
|
||||
"hickory-resolver",
|
||||
"hickory-server",
|
||||
"once_cell",
|
||||
|
|
|
@ -28,7 +28,7 @@ use crate::{
|
|||
dns_lru::{DnsLru, TtlConfig},
|
||||
error::ResolveError,
|
||||
lookup::Lookup,
|
||||
name_server::{GenericNameServerPool, TokioRuntimeProvider},
|
||||
name_server::{ConnectionProvider, GenericConnector, GenericNameServerPool, NameServerPool, TokioRuntimeProvider},
|
||||
Name,
|
||||
},
|
||||
Error, ErrorKind,
|
||||
|
@ -40,13 +40,13 @@ type NameServerCache<P> = LruCache<Name, RecursorPool<P>>;
|
|||
/// A top down recursive resolver which operates off a list of roots for initial recursive requests.
|
||||
///
|
||||
/// This is the well known root nodes, referred to as hints in RFCs. See the IANA [Root Servers](https://www.iana.org/domains/root/servers) list.
|
||||
pub struct Recursor {
|
||||
roots: RecursorPool<TokioRuntimeProvider>,
|
||||
name_server_cache: Mutex<NameServerCache<TokioRuntimeProvider>>,
|
||||
pub struct Recursor<P: ConnectionProvider> {
|
||||
roots: RecursorPool<P>,
|
||||
name_server_cache: Mutex<NameServerCache<P>>,
|
||||
record_cache: DnsLru,
|
||||
}
|
||||
|
||||
impl Recursor {
|
||||
impl Recursor<GenericConnector<TokioRuntimeProvider>> {
|
||||
/// Construct a new recursor using the list of NameServerConfigs for the root node list
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -62,11 +62,24 @@ impl Recursor {
|
|||
|
||||
assert!(!roots.is_empty(), "roots must not be empty");
|
||||
|
||||
debug!("Using cache sizes {}/{}", ns_cache_size, record_cache_size);
|
||||
let opts = recursor_opts();
|
||||
let roots =
|
||||
GenericNameServerPool::from_config(roots, opts, TokioConnectionProvider::default());
|
||||
|
||||
Self::new_with_pool(roots, ns_cache_size, record_cache_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ConnectionProvider> Recursor<P> {
|
||||
/// Construct a new recursor using a custom name server pool.
|
||||
/// You likely want to use `new` instead.
|
||||
pub fn new_with_pool(
|
||||
roots: NameServerPool<P>,
|
||||
ns_cache_size: usize,
|
||||
record_cache_size: usize,
|
||||
) -> Result<Self, ResolveError> {
|
||||
let roots = RecursorPool::from(Name::root(), roots);
|
||||
debug!("Using cache sizes {}/{}", ns_cache_size, record_cache_size);
|
||||
let name_server_cache = Mutex::new(NameServerCache::new(ns_cache_size));
|
||||
let record_cache = DnsLru::new(record_cache_size, TtlConfig::default());
|
||||
|
||||
|
@ -76,7 +89,9 @@ impl Recursor {
|
|||
record_cache,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ConnectionProvider + Default> Recursor<P> {
|
||||
/// Perform a recursive resolution
|
||||
///
|
||||
/// [RFC 1034](https://datatracker.ietf.org/doc/html/rfc1034#section-5.3.3), Domain Concepts and Facilities, November 1987
|
||||
|
@ -287,7 +302,7 @@ impl Recursor {
|
|||
async fn lookup(
|
||||
&self,
|
||||
query: Query,
|
||||
ns: RecursorPool<TokioRuntimeProvider>,
|
||||
ns: RecursorPool<P>,
|
||||
now: Instant,
|
||||
) -> Result<Lookup, Error> {
|
||||
if let Some(lookup) = self.record_cache.get(&query, now) {
|
||||
|
@ -337,7 +352,7 @@ impl Recursor {
|
|||
&self,
|
||||
zone: Name,
|
||||
request_time: Instant,
|
||||
) -> Result<RecursorPool<TokioRuntimeProvider>, Error> {
|
||||
) -> Result<RecursorPool<P>, Error> {
|
||||
// TODO: need to check TTLs here.
|
||||
if let Some(ns) = self.name_server_cache.lock().get_mut(&zone) {
|
||||
return Ok(ns.clone());
|
||||
|
@ -465,10 +480,10 @@ impl Recursor {
|
|||
}
|
||||
|
||||
// now construct a namesever pool based off the NS and glue records
|
||||
let ns = GenericNameServerPool::from_config(
|
||||
let ns = NameServerPool::from_config(
|
||||
config_group,
|
||||
recursor_opts(),
|
||||
TokioConnectionProvider::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let ns = RecursorPool::from(zone.clone(), ns);
|
||||
|
||||
|
|
|
@ -18,10 +18,9 @@ use hickory_proto::{
|
|||
xfer::{DnsRequestOptions, DnsResponse},
|
||||
DnsHandle,
|
||||
};
|
||||
use hickory_resolver::name_server::{RuntimeProvider, TokioRuntimeProvider};
|
||||
use hickory_resolver::{
|
||||
error::{ResolveError, ResolveErrorKind},
|
||||
name_server::GenericNameServerPool,
|
||||
name_server::{ConnectionProvider, NameServerPool},
|
||||
Name,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
|
@ -50,14 +49,14 @@ impl Future for SharedLookup {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RecursorPool<P: RuntimeProvider + Send + 'static> {
|
||||
pub(crate) struct RecursorPool<P: ConnectionProvider> {
|
||||
zone: Name,
|
||||
ns: GenericNameServerPool<P>,
|
||||
ns: NameServerPool<P>,
|
||||
active_requests: Arc<Mutex<ActiveRequests>>,
|
||||
}
|
||||
|
||||
impl RecursorPool<TokioRuntimeProvider> {
|
||||
pub(crate) fn from(zone: Name, ns: GenericNameServerPool<TokioRuntimeProvider>) -> Self {
|
||||
impl<P: ConnectionProvider> RecursorPool<P> {
|
||||
pub(crate) fn from(zone: Name, ns: NameServerPool<P>) -> Self {
|
||||
let active_requests = Arc::new(Mutex::new(ActiveRequests::default()));
|
||||
|
||||
Self {
|
||||
|
@ -68,10 +67,7 @@ impl RecursorPool<TokioRuntimeProvider> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P> RecursorPool<P>
|
||||
where
|
||||
P: RuntimeProvider + Send + 'static,
|
||||
{
|
||||
impl<P: ConnectionProvider> RecursorPool<P> {
|
||||
pub(crate) fn zone(&self) -> &Name {
|
||||
&self.zone
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ tokio = { workspace = true, features = ["time", "rt"] }
|
|||
tracing.workspace = true
|
||||
hickory-client.workspace = true
|
||||
hickory-proto = { workspace = true, features = ["testing"] }
|
||||
hickory-recursor = { workspace = true }
|
||||
hickory-resolver = { workspace = true, features = ["tokio-runtime"] }
|
||||
hickory-server = { workspace = true, features = ["testing"] }
|
||||
webpki-roots = { workspace = true, optional = true }
|
||||
|
|
|
@ -14,7 +14,7 @@ use futures::stream::{once, Stream};
|
|||
use futures::{future, AsyncRead, AsyncWrite, Future};
|
||||
|
||||
use hickory_client::op::{Message, Query};
|
||||
use hickory_client::rr::rdata::{CNAME, SOA};
|
||||
use hickory_client::rr::rdata::{CNAME, NS, SOA};
|
||||
use hickory_client::rr::{Name, RData, Record};
|
||||
use hickory_proto::error::ProtoError;
|
||||
use hickory_proto::tcp::DnsTcpStream;
|
||||
|
@ -127,7 +127,7 @@ impl RuntimeProvider for MockRuntimeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct MockConnProvider<O: OnSend + Unpin> {
|
||||
pub on_send: O,
|
||||
}
|
||||
|
@ -211,6 +211,10 @@ pub fn soa_record(name: Name, mname: Name) -> Record {
|
|||
Record::from_rdata(name, 86400, RData::SOA(soa))
|
||||
}
|
||||
|
||||
pub fn ns_record(name: Name, ns: Name) -> Record {
|
||||
Record::from_rdata(name, 86400, RData::NS(NS(ns)))
|
||||
}
|
||||
|
||||
pub fn message(
|
||||
query: Query,
|
||||
answers: Vec<Record>,
|
||||
|
@ -245,7 +249,7 @@ pub trait OnSend: Clone + Send + Sync + 'static {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DefaultOnSend;
|
||||
|
||||
impl OnSend for DefaultOnSend {}
|
||||
|
|
|
@ -8,6 +8,50 @@
|
|||
//! Integration tests for the recursor. These integration tests setup scenarios to verify that the recursor is able
|
||||
//! to recursively resolve various real world scenarios. As new scenarios are discovered, they should be added here.
|
||||
|
||||
use std::net::*;
|
||||
use std::str::FromStr as _;
|
||||
use std::time::Instant;
|
||||
use futures::executor::block_on;
|
||||
|
||||
use hickory_client::op::Query;
|
||||
use hickory_proto::error::ProtoError;
|
||||
use hickory_proto::xfer::DnsResponse;
|
||||
use hickory_integration::mock_client::*;
|
||||
use hickory_recursor::Recursor;
|
||||
use hickory_resolver::name_server::{NameServer, NameServerPool};
|
||||
use hickory_resolver::config::*;
|
||||
use hickory_client::rr::{Name, RecordType};
|
||||
|
||||
|
||||
const DEFAULT_SERVER_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
||||
|
||||
type MockedNameServer<O> = NameServer<MockConnProvider<O>>;
|
||||
|
||||
|
||||
fn mock_nameserver(
|
||||
messages: Vec<Result<DnsResponse, ProtoError>>,
|
||||
) -> MockedNameServer<DefaultOnSend> {
|
||||
let conn_provider = MockConnProvider {
|
||||
on_send: DefaultOnSend,
|
||||
};
|
||||
let client = MockClientHandle::mock_on_send(messages, DefaultOnSend);
|
||||
|
||||
NameServer::from_conn(
|
||||
NameServerConfig {
|
||||
socket_addr: SocketAddr::new(DEFAULT_SERVER_ADDR, 0),
|
||||
protocol: Protocol::Udp,
|
||||
tls_dns_name: None,
|
||||
trust_negative_responses: false,
|
||||
#[cfg(any(feature = "dns-over-rustls", feature = "dns-over-https-rustls"))]
|
||||
tls_config: None,
|
||||
bind_addr: None,
|
||||
},
|
||||
Default::default(),
|
||||
client,
|
||||
conn_provider,
|
||||
)
|
||||
}
|
||||
|
||||
/// Tests a basic recursive resolution `a.recursive.test.` , `.` -> `test.` -> `recursive.test.` -> `a.recursive.test.`
|
||||
///
|
||||
/// There are three authorities needed for this test `.` which contains the `test` nameserver, `recursive.test` which is
|
||||
|
@ -16,3 +60,26 @@
|
|||
fn test_basic_recursion() {
|
||||
// TBD
|
||||
}
|
||||
|
||||
/// Query the root for its nameserver. This is a single DNS request, no recursion necessary.
|
||||
#[test]
|
||||
fn test_root_ns() {
|
||||
let ns_query = Query::query(Name::from_str(".").unwrap(), RecordType::NS);
|
||||
let ns_record = ns_record(Name::from_str(".").unwrap(), Name::from_str("a.root-servers.net.").unwrap());
|
||||
let ns_message = message(ns_query.clone(), vec![ns_record.clone()], vec![], vec![]);
|
||||
let nameserver = mock_nameserver(
|
||||
vec![Ok(DnsResponse::from_message(ns_message).unwrap())],
|
||||
);
|
||||
|
||||
let roots = NameServerPool::from_nameservers(
|
||||
Default::default(),
|
||||
vec![nameserver],
|
||||
vec![],
|
||||
);
|
||||
let recursor = Recursor::new_with_pool(roots, 1024, 1048576).unwrap();
|
||||
|
||||
let now = Instant::now();
|
||||
let lookup = block_on(recursor.resolve(ns_query, now)).unwrap();
|
||||
|
||||
assert_eq!(lookup.records()[0], ns_record);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user