@@ -19,6 +19,7 @@ use std::{
1919} ;
2020use telio_dns:: { LocalNameServer , NameServer , Records } ;
2121use telio_model:: features:: TtlValue ;
22+ use tokio:: task:: JoinHandle ;
2223use tokio:: time:: sleep;
2324use 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+
602653macro_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