diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs index 1538e5d4df..758cda01b4 100644 --- a/cli/diagnostics.rs +++ b/cli/diagnostics.rs @@ -31,6 +31,7 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[ "Metrics", "OpMetrics", "RecordType", + "SOARecord", "SRVRecord", "SetRawOptions", "SignalStream", diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index 8d7f5f269d..f7dc778c39 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -2957,6 +2957,7 @@ declare namespace Deno { | "MX" | "NS" | "PTR" + | "SOA" | "SRV" | "TXT"; @@ -2978,6 +2979,12 @@ declare namespace Deno { exchange: string; } + /** If `resolveDns` is called with "SOA" record type specified, it will return an array of this interface. */ + export interface SOARecord { + mname: string; + rname: string; + } + /** If `resolveDns` is called with "SRV" record type specified, it will return an array of this interface. */ export interface SRVRecord { priority: number; @@ -2998,6 +3005,12 @@ declare namespace Deno { options?: ResolveDnsOptions, ): Promise; + export function resolveDns( + query: string, + recordType: "SOA", + options?: ResolveDnsOptions, + ): Promise; + export function resolveDns( query: string, recordType: "SRV", diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs index 3de33b381f..d1684e3ef3 100644 --- a/cli/tests/integration/mod.rs +++ b/cli/tests/integration/mod.rs @@ -15,6 +15,8 @@ use std::sync::Arc; use test_util as util; use test_util::TempDir; use tokio::task::LocalSet; +use trust_dns_client::serialize::txt::Lexer; +use trust_dns_client::serialize::txt::Parser; #[macro_export] macro_rules! itest( @@ -811,9 +813,6 @@ fn set_raw_should_not_panic_on_no_tty() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_resolve_dns() { - use std::collections::BTreeMap; - use std::net::Ipv4Addr; - use std::net::Ipv6Addr; use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; @@ -821,19 +820,9 @@ async fn test_resolve_dns() { use tokio::net::TcpListener; use tokio::net::UdpSocket; use tokio::sync::oneshot; - use trust_dns_client::rr::LowerName; - use trust_dns_client::rr::RecordType; - use trust_dns_client::rr::RrKey; use trust_dns_server::authority::Catalog; use trust_dns_server::authority::ZoneType; - use trust_dns_server::proto::rr::rdata::mx::MX; - use trust_dns_server::proto::rr::rdata::soa::SOA; - use trust_dns_server::proto::rr::rdata::srv::SRV; - use trust_dns_server::proto::rr::rdata::txt::TXT; - use trust_dns_server::proto::rr::record_data::RData; - use trust_dns_server::proto::rr::resource::Record; use trust_dns_server::proto::rr::Name; - use trust_dns_server::proto::rr::RecordSet; use trust_dns_server::store::in_memory::InMemoryAuthority; use trust_dns_server::ServerFuture; @@ -841,137 +830,25 @@ async fn test_resolve_dns() { // Setup DNS server for testing async fn run_dns_server(tx: oneshot::Sender<()>) { - let catalog = { - let records = { - let mut map = BTreeMap::new(); - let lookup_name = "www.example.com".parse::().unwrap(); - let lookup_name_lower = LowerName::new(&lookup_name); - - // Inserts SOA record - let soa = SOA::new( - Name::from_str("net").unwrap(), - Name::from_str("example").unwrap(), - 0, - i32::MAX, - i32::MAX, - i32::MAX, - 0, - ); - let rdata = RData::SOA(soa); - let record = Record::from_rdata(Name::new(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map - .insert(RrKey::new(Name::root().into(), RecordType::SOA), record_set); - - // Inserts A record - let rdata = RData::A(Ipv4Addr::new(1, 2, 3, 4)); - let record = Record::from_rdata(lookup_name.clone(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::A), - record_set, - ); - - // Inserts AAAA record - let rdata = RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)); - let record = Record::from_rdata(lookup_name.clone(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::AAAA), - record_set, - ); - - // Inserts ANAME record - let rdata = RData::ANAME(Name::from_str("aname.com").unwrap()); - let record = Record::from_rdata(lookup_name.clone(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::ANAME), - record_set, - ); - - // Inserts CNAME record - let rdata = RData::CNAME(Name::from_str("cname.com").unwrap()); - let record = - Record::from_rdata(Name::from_str("foo").unwrap(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::CNAME), - record_set, - ); - - // Inserts MX record - let rdata = RData::MX(MX::new(0, Name::from_str("mx.com").unwrap())); - let record = Record::from_rdata(lookup_name.clone(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::MX), - record_set, - ); - - // Inserts NS record - let rdata = RData::NS(Name::from_str("ns1.ns.com").unwrap()); - let record = Record::from_rdata(lookup_name.clone(), u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::NS), - record_set, - ); - - // Inserts PTR record - let rdata = RData::PTR(Name::from_str("ptr.com").unwrap()); - let record = Record::from_rdata( - Name::from_str("5.6.7.8").unwrap(), - u32::MAX, - rdata, - ); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new("5.6.7.8".parse().unwrap(), RecordType::PTR), - record_set, - ); - - // Inserts SRV record - let rdata = RData::SRV(SRV::new( - 0, - 100, - 1234, - Name::from_str("srv.com").unwrap(), - )); - let record = Record::from_rdata( - Name::from_str("_Service._TCP.example.com").unwrap(), - u32::MAX, - rdata, - ); - let record_set = RecordSet::from(record); - map.insert( - RrKey::new(lookup_name_lower.clone(), RecordType::SRV), - record_set, - ); - - // Inserts TXT record - let rdata = - RData::TXT(TXT::new(vec!["foo".to_string(), "bar".to_string()])); - let record = Record::from_rdata(lookup_name, u32::MAX, rdata); - let record_set = RecordSet::from(record); - map.insert(RrKey::new(lookup_name_lower, RecordType::TXT), record_set); - - map - }; - - let authority = Box::new(Arc::new( - InMemoryAuthority::new( - Name::from_str("com").unwrap(), - records, - ZoneType::Primary, - false, - ) + let zone_file = + fs::read_to_string(util::testdata_path().join("resolve_dns.zone.in")) + .unwrap(); + let lexer = Lexer::new(&zone_file); + let records = Parser::new().parse( + lexer, + Some(Name::from_str("example.com").unwrap()), + None, + ); + if records.is_err() { + panic!("failed to parse: {:?}", records.err()) + } + let (origin, records) = records.unwrap(); + let authority = Box::new(Arc::new( + InMemoryAuthority::new(origin, records, ZoneType::Primary, false) .unwrap(), - )); - let mut c = Catalog::new(); - c.upsert(Name::root().into(), authority); - c - }; + )); + let mut catalog: Catalog = Catalog::new(); + catalog.upsert(Name::root().into(), authority); let mut server_fut = ServerFuture::new(catalog); let socket_addr = SocketAddr::from(([127, 0, 0, 1], DNS_PORT)); @@ -1011,6 +888,7 @@ async fn test_resolve_dns() { .unwrap(); let err = String::from_utf8_lossy(&output.stderr); let out = String::from_utf8_lossy(&output.stdout); + println!("{}", err); assert!(output.status.success()); assert!(err.starts_with("Check file")); diff --git a/cli/tests/testdata/resolve_dns.ts b/cli/tests/testdata/resolve_dns.ts index 9b43643117..1e0fa08b88 100644 --- a/cli/tests/testdata/resolve_dns.ts +++ b/cli/tests/testdata/resolve_dns.ts @@ -1,15 +1,16 @@ const nameServer = { nameServer: { ipAddr: "127.0.0.1", port: 4553 } }; -const [a, aaaa, aname, cname, mx, ns, ptr, srv, txt] = await Promise.all([ +const [a, aaaa, aname, cname, mx, ns, ptr, soa, srv, txt] = await Promise.all([ Deno.resolveDns("www.example.com", "A", nameServer), Deno.resolveDns("www.example.com", "AAAA", nameServer), Deno.resolveDns("www.example.com", "ANAME", nameServer), - Deno.resolveDns("foo", "CNAME", nameServer), - Deno.resolveDns("www.example.com", "MX", nameServer), - Deno.resolveDns("www.example.com", "NS", nameServer), - Deno.resolveDns("5.6.7.8", "PTR", nameServer), - Deno.resolveDns("_Service._TCP.example.com", "SRV", nameServer), - Deno.resolveDns("www.example.com", "TXT", nameServer), + Deno.resolveDns("alias.example.com", "CNAME", nameServer), + Deno.resolveDns("example.com", "MX", nameServer), + Deno.resolveDns("example.com", "NS", nameServer), + Deno.resolveDns("1.2.3.4.IN-ADDR.ARPA.", "PTR", nameServer), + Deno.resolveDns("example.com", "SOA", nameServer), + Deno.resolveDns("_service._tcp.example.com", "SRV", nameServer), + Deno.resolveDns("example.com", "TXT", nameServer), ]); console.log("A"); @@ -33,6 +34,9 @@ console.log(JSON.stringify(ns)); console.log("PTR"); console.log(JSON.stringify(ptr)); +console.log("SOA"); +console.log(JSON.stringify(soa)); + console.log("SRV"); console.log(JSON.stringify(srv)); diff --git a/cli/tests/testdata/resolve_dns.ts.out b/cli/tests/testdata/resolve_dns.ts.out index 2b56d72ae0..487b55546c 100644 --- a/cli/tests/testdata/resolve_dns.ts.out +++ b/cli/tests/testdata/resolve_dns.ts.out @@ -1,19 +1,21 @@ A -["1.2.3.4"] +["1.2.3.4","5.6.7.8"] AAAA ["1:2:3:4:5:6:7:8"] ANAME -["aname.com."] +["aname.example.com."] CNAME -["cname.com."] +["cname.example.com."] MX -[{"preference":0,"exchange":"mx.com."}] +[{"preference":10,"exchange":"mx1.com."},{"preference":20,"exchange":"mx2.com."}] NS -["ns1.ns.com."] +["ns1.ns.com.","ns2.ns.com.","ns3.ns.com."] PTR -["ptr.com."] +["www.example.com.","alias.example.com."] +SOA +[{"mname":"net.example.com.","rname":"admin\\.domain.example.com."}] SRV -[{"priority":0,"weight":100,"port":1234,"target":"srv.com."}] +[{"priority":0,"weight":100,"port":1234,"target":"srv.example.com."}] TXT -[["foo","bar"]] +[["I","am","a","txt","record"],["I","am","another","txt","record"],["I am a different","txt record"],["key=val"]] Error NotFound thrown for not-found-example.com diff --git a/cli/tests/testdata/resolve_dns.zone.in b/cli/tests/testdata/resolve_dns.zone.in new file mode 100644 index 0000000000..6b611bafae --- /dev/null +++ b/cli/tests/testdata/resolve_dns.zone.in @@ -0,0 +1,24 @@ +@ IN SOA net admin\.domain ( + 20 ; SERIAL + 7200 ; REFRESH + 600 ; RETRY + 3600000; EXPIRE + 60) ; MINIMUM + NS ns1.ns.com. + NS ns2.ns.com. + NS ns3.ns.com. + MX 10 mx1.com. + MX 20 mx2.com. + TXT I am a txt record + TXT I am another txt record + TXT "I am a different" "txt record" + TXT key=val +www A 1.2.3.4 + A 5.6.7.8 + ANAME aname +www AAAA 1:2:3:4:5:6:7:8 +alias CNAME cname + +1.2.3.4.IN-ADDR.ARPA. PTR www + PTR alias +_service._tcp SRV 0 100 1234 srv \ No newline at end of file diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 4fef389853..2478d09847 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -581,6 +581,10 @@ pub enum DnsReturnRecord { }, Ns(String), Ptr(String), + Soa { + mname: String, + rname: String, + }, Srv { priority: u16, weight: u16, @@ -736,6 +740,10 @@ fn rdata_to_return_record( .as_ptr() .map(ToString::to_string) .map(DnsReturnRecord::Ptr), + SOA => r.as_soa().map(|soa| DnsReturnRecord::Soa { + mname: soa.mname().to_string(), + rname: soa.rname().to_string(), + }), SRV => r.as_srv().map(|srv| DnsReturnRecord::Srv { priority: srv.priority(), weight: srv.weight(), @@ -772,6 +780,7 @@ mod tests { use trust_dns_proto::rr::rdata::mx::MX; use trust_dns_proto::rr::rdata::srv::SRV; use trust_dns_proto::rr::rdata::txt::TXT; + use trust_dns_proto::rr::rdata::SOA; use trust_dns_proto::rr::record_data::RData; use trust_dns_proto::rr::Name; @@ -833,6 +842,27 @@ mod tests { assert_eq!(func(&rdata), Some(DnsReturnRecord::Ptr("".to_string()))); } + #[test] + fn rdata_to_return_record_soa() { + let func = rdata_to_return_record(RecordType::SOA); + let rdata = RData::SOA(SOA::new( + Name::new(), + Name::new(), + 0, + i32::MAX, + i32::MAX, + i32::MAX, + 0, + )); + assert_eq!( + func(&rdata), + Some(DnsReturnRecord::Soa { + mname: "".to_string(), + rname: "".to_string() + }) + ); + } + #[test] fn rdata_to_return_record_srv() { let func = rdata_to_return_record(RecordType::SRV);