Skip to content

Commit 214691c

Browse files
committed
Integration tests use stub upstream resolver
1 parent 770a1aa commit 214691c

2 files changed

Lines changed: 177 additions & 5 deletions

File tree

crates/telio-dns/src/forwarder.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ impl RawForwarder {
106106
Ok(())
107107
}
108108

109+
#[allow(dead_code)]
109110
/// Update the timeout for upstream DNS queries
110111
pub async fn set_timeout(&self, timeout: Duration) {
111112
*self.timeout.lock().await = timeout;
@@ -200,7 +201,6 @@ mod tests {
200201

201202
enum StubBehavior {
202203
Reply(Vec<u8>),
203-
DelayedReply { delay: Duration, response: Vec<u8> },
204204
BlackHole,
205205
}
206206

@@ -217,10 +217,6 @@ mod tests {
217217
StubBehavior::Reply(response) => {
218218
socket.send_to(&response, src).await.unwrap();
219219
}
220-
StubBehavior::DelayedReply { delay, response } => {
221-
tokio::time::sleep(delay).await;
222-
socket.send_to(&response, src).await.unwrap();
223-
}
224220
StubBehavior::BlackHole => {}
225221
}
226222
received

crates/telio-dns/tests/nameserver.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::{
1919
};
2020
use telio_dns::{LocalNameServer, NameServer, Records};
2121
use telio_model::features::TtlValue;
22+
use tokio::task::JoinHandle;
2223
use tokio::time::sleep;
2324
use tokio::{
2425
self,
@@ -599,6 +600,56 @@ async fn init_client_and_server(
599600
(client, server_peer)
600601
}
601602

603+
enum UpstreamStubBehavior {
604+
Reply(Ipv4Addr),
605+
BlackHole,
606+
}
607+
608+
// Helper to build query response bytes
609+
fn build_upstream_dns_response(query_bytes: &[u8], answer_ip: Ipv4Addr) -> Vec<u8> {
610+
let parsed = dns_parser::Packet::parse(query_bytes).expect("Failed to parse incoming query");
611+
let qname = parsed.questions[0].qname.to_string();
612+
613+
let mut builder = Builder::new_query(parsed.header.id, true);
614+
builder.add_question(&qname, false, QueryType::A, QueryClass::IN);
615+
let mut buf = builder.build().unwrap();
616+
617+
buf[2] = 0x81;
618+
buf[3] = 0x80;
619+
buf[6..8].copy_from_slice(&1_u16.to_be_bytes());
620+
621+
buf.extend_from_slice(&[0xC0, 0x0C]);
622+
buf.extend_from_slice(&1_u16.to_be_bytes());
623+
buf.extend_from_slice(&1_u16.to_be_bytes());
624+
buf.extend_from_slice(&300_u32.to_be_bytes());
625+
buf.extend_from_slice(&4_u16.to_be_bytes());
626+
buf.extend_from_slice(&answer_ip.octets());
627+
628+
buf
629+
}
630+
631+
// Helper for stub upstream resolver
632+
async fn spawn_upstream_dns_stub(behavior: UpstreamStubBehavior) -> (SocketAddr, JoinHandle<()>) {
633+
let socket = tokio::net::UdpSocket::bind("127.0.0.1:0")
634+
.await
635+
.expect("Failed to bind stub socket");
636+
let addr = socket.local_addr().unwrap();
637+
638+
let handle = tokio::spawn(async move {
639+
let mut buf = vec![0u8; 4096];
640+
let (n, src) = socket.recv_from(&mut buf).await.unwrap();
641+
match behavior {
642+
UpstreamStubBehavior::Reply(answer_ip) => {
643+
let response = build_upstream_dns_response(&buf[..n], answer_ip);
644+
socket.send_to(&response, src).await.unwrap();
645+
}
646+
UpstreamStubBehavior::BlackHole => {}
647+
}
648+
});
649+
650+
(addr, handle)
651+
}
652+
602653
macro_rules! assert_no_data {
603654
($resp:expr) => {{
604655
let response = $resp;
@@ -1231,3 +1282,128 @@ async fn test_timers_updated() {
12311282
"Client should not expire due to keepalives"
12321283
);
12331284
}
1285+
1286+
#[tokio::test]
1287+
async fn dns_request_forward_to_stub_upstream() {
1288+
let expected_ip = Ipv4Addr::new(93, 184, 216, 34);
1289+
let (stub_addr, _stub_handle) =
1290+
spawn_upstream_dns_stub(UpstreamStubBehavior::Reply(expected_ip)).await;
1291+
1292+
let nameserver = LocalNameServer::new(&[])
1293+
.await
1294+
.expect("Failed to create a LocalNameServer");
1295+
nameserver
1296+
.forward_to_addrs(&[stub_addr])
1297+
.await
1298+
.expect("Failed to set stub upstream");
1299+
1300+
let response = timeout(
1301+
Duration::from_secs(60),
1302+
dns_test_with_server(
1303+
"example.com",
1304+
DnsTestType::CorrectIpv4,
1305+
None,
1306+
nameserver,
1307+
TtlValue(60),
1308+
),
1309+
)
1310+
.await
1311+
.expect("Test timeout")
1312+
.expect("Expected some DNS response");
1313+
1314+
assert!(response.no_error());
1315+
assert_eq!(response.a_addrs(), vec![expected_ip]);
1316+
}
1317+
1318+
#[tokio::test]
1319+
async fn dns_request_forward_fallback_to_second_stub() {
1320+
let expected_ip = Ipv4Addr::new(10, 0, 0, 1);
1321+
let (blackhole_addr, _bh_handle) =
1322+
spawn_upstream_dns_stub(UpstreamStubBehavior::BlackHole).await;
1323+
let (stub_addr, _stub_handle) =
1324+
spawn_upstream_dns_stub(UpstreamStubBehavior::Reply(expected_ip)).await;
1325+
1326+
let nameserver = LocalNameServer::new(&[])
1327+
.await
1328+
.expect("Failed to create a LocalNameServer");
1329+
nameserver
1330+
.forward_to_addrs(&[blackhole_addr, stub_addr])
1331+
.await
1332+
.expect("Failed to set stub upstreams");
1333+
1334+
let response = timeout(
1335+
Duration::from_secs(60),
1336+
dns_test_with_server(
1337+
"example.com",
1338+
DnsTestType::CorrectIpv4,
1339+
None,
1340+
nameserver,
1341+
TtlValue(60),
1342+
),
1343+
)
1344+
.await
1345+
.expect("Test timeout")
1346+
.expect("Expected some DNS response");
1347+
1348+
assert!(response.no_error());
1349+
assert_eq!(response.a_addrs(), vec![expected_ip]);
1350+
}
1351+
1352+
#[tokio::test]
1353+
async fn dns_request_forward_timeout_returns_no_response() {
1354+
let (blackhole_addr, _bh_handle) =
1355+
spawn_upstream_dns_stub(UpstreamStubBehavior::BlackHole).await;
1356+
1357+
let nameserver = LocalNameServer::new(&[])
1358+
.await
1359+
.expect("Failed to create a LocalNameServer");
1360+
nameserver
1361+
.forward_to_addrs(&[blackhole_addr])
1362+
.await
1363+
.expect("Failed to set stub upstream");
1364+
1365+
let result = dns_test_with_server(
1366+
"example.com",
1367+
DnsTestType::NonRespondingForwardServer,
1368+
None,
1369+
nameserver,
1370+
TtlValue(60),
1371+
)
1372+
.await;
1373+
1374+
assert!(result.is_none());
1375+
}
1376+
1377+
#[tokio::test]
1378+
async fn dns_request_nord_bypasses_forwarder() {
1379+
let (blackhole_addr, _bh_handle) =
1380+
spawn_upstream_dns_stub(UpstreamStubBehavior::BlackHole).await;
1381+
1382+
let nameserver = LocalNameServer::new(&[])
1383+
.await
1384+
.expect("Failed to create a LocalNameServer");
1385+
nameserver
1386+
.forward_to_addrs(&[blackhole_addr])
1387+
.await
1388+
.expect("Failed to set stub upstream");
1389+
1390+
let mut records = Records::new();
1391+
let expected_ip = Ipv4Addr::new(100, 64, 0, 1);
1392+
records.insert(String::from("test.nord."), vec![IpAddr::V4(expected_ip)]);
1393+
1394+
let response = timeout(
1395+
Duration::from_secs(60),
1396+
dns_test_with_server(
1397+
"test.nord",
1398+
DnsTestType::CorrectIpv4,
1399+
Some(("nord".into(), records)),
1400+
nameserver,
1401+
TtlValue(60),
1402+
),
1403+
)
1404+
.await
1405+
.expect("Test timeout")
1406+
.expect("Expected some DNS response");
1407+
1408+
assert_a_records!(response, vec![IpAddr::V4(expected_ip)]);
1409+
}

0 commit comments

Comments
 (0)