From 832d2f0b684f8f2f618a070dbf6e7edf8cf59926 Mon Sep 17 00:00:00 2001 From: Craig Morten Date: Mon, 16 May 2022 10:20:41 +0100 Subject: [PATCH] feat(ext/net): add `CAA` DNS record support in Deno.resolveDns() API (#14624) --- cli/dts/lib.deno.ns.d.ts | 15 ++++++++ cli/tests/testdata/resolve_dns.ts | 33 +++++++++-------- cli/tests/testdata/resolve_dns.ts.out | 2 ++ cli/tests/testdata/resolve_dns.zone.in | 5 +++ ext/net/ops.rs | 49 ++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 14 deletions(-) diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index 3777aa56bdf85b..019ab240b4a52b 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -2953,6 +2953,7 @@ declare namespace Deno { | "A" | "AAAA" | "ANAME" + | "CAA" | "CNAME" | "MX" | "NAPTR" @@ -2974,6 +2975,13 @@ declare namespace Deno { }; } + /** If `resolveDns` is called with "CAA" record type specified, it will return an array of this interface. */ + export interface CAARecord { + critical: boolean; + tag: string; + value: string; + } + /** If `resolveDns` is called with "MX" record type specified, it will return an array of this interface. */ export interface MXRecord { preference: number; @@ -3015,6 +3023,12 @@ declare namespace Deno { options?: ResolveDnsOptions, ): Promise; + export function resolveDns( + query: string, + recordType: "CAA", + options?: ResolveDnsOptions, + ): Promise; + export function resolveDns( query: string, recordType: "MX", @@ -3068,6 +3082,7 @@ declare namespace Deno { options?: ResolveDnsOptions, ): Promise< | string[] + | CAARecord[] | MXRecord[] | NAPTRRecord[] | SOARecord[] diff --git a/cli/tests/testdata/resolve_dns.ts b/cli/tests/testdata/resolve_dns.ts index 317828a5e58fd9..bde479c9ae4a58 100644 --- a/cli/tests/testdata/resolve_dns.ts +++ b/cli/tests/testdata/resolve_dns.ts @@ -1,19 +1,21 @@ const nameServer = { nameServer: { ipAddr: "127.0.0.1", port: 4553 } }; -const [a, aaaa, aname, cname, mx, naptr, 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("alias.example.com", "CNAME", nameServer), - Deno.resolveDns("example.com", "MX", nameServer), - Deno.resolveDns("example.com", "NAPTR", 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), - ]); +const [a, aaaa, aname, caa, cname, mx, naptr, 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("example.com", "CAA", nameServer), + Deno.resolveDns("alias.example.com", "CNAME", nameServer), + Deno.resolveDns("example.com", "MX", nameServer), + Deno.resolveDns("example.com", "NAPTR", 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"); console.log(JSON.stringify(a)); @@ -24,6 +26,9 @@ console.log(JSON.stringify(aaaa)); console.log("ANAME"); console.log(JSON.stringify(aname)); +console.log("CAA"); +console.log(JSON.stringify(caa)); + console.log("CNAME"); console.log(JSON.stringify(cname)); diff --git a/cli/tests/testdata/resolve_dns.ts.out b/cli/tests/testdata/resolve_dns.ts.out index 2f4c87c4c94e37..2fe10cb9aaf15c 100644 --- a/cli/tests/testdata/resolve_dns.ts.out +++ b/cli/tests/testdata/resolve_dns.ts.out @@ -4,6 +4,8 @@ AAAA ["1:2:3:4:5:6:7:8"] ANAME ["aname.example.com."] +CAA +[{"critical":false,"tag":"issue","value":"ca.example.net"},{"critical":false,"tag":"issue","value":"ca2.example.net; account=123456"},{"critical":false,"tag":"issuewild","value":";"},{"critical":false,"tag":"iodef","value":"mailto:security@example.com"},{"critical":true,"tag":"tbs","value":"Unknown"}] CNAME ["cname.example.com."] MX diff --git a/cli/tests/testdata/resolve_dns.zone.in b/cli/tests/testdata/resolve_dns.zone.in index 456a8d8fbf1c69..c351a1de9dd357 100644 --- a/cli/tests/testdata/resolve_dns.zone.in +++ b/cli/tests/testdata/resolve_dns.zone.in @@ -4,6 +4,11 @@ 600 ; RETRY 3600000; EXPIRE 60) ; MINIMUM +@ IN CAA 0 issue "ca.example.net" +@ IN CAA 0 issue "ca2.example.net; account=123456" +@ IN CAA 0 issuewild ";" +@ IN CAA 0 iodef "mailto:security@example.com" +@ IN CAA 128 tbs "Unknown" NS ns1.ns.com. NS ns2.ns.com. NS ns3.ns.com. diff --git a/ext/net/ops.rs b/ext/net/ops.rs index ec3be091a678d3..3522a919ce6df5 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -34,6 +34,7 @@ use std::rc::Rc; use tokio::net::TcpListener; use tokio::net::TcpStream; use tokio::net::UdpSocket; +use trust_dns_proto::rr::rdata::caa::Value; use trust_dns_proto::rr::record_data::RData; use trust_dns_proto::rr::record_type::RecordType; use trust_dns_resolver::config::NameServerConfigGroup; @@ -574,6 +575,11 @@ pub enum DnsReturnRecord { A(String), Aaaa(String), Aname(String), + Caa { + critical: bool, + tag: String, + value: String, + }, Cname(String), Mx { preference: u16, @@ -740,6 +746,29 @@ fn rdata_to_return_record( .as_aname() .map(ToString::to_string) .map(DnsReturnRecord::Aname), + CAA => r.as_caa().map(|caa| DnsReturnRecord::Caa { + critical: caa.issuer_critical(), + tag: caa.tag().to_string(), + value: match caa.value() { + Value::Issuer(name, key_values) => { + let mut s = String::new(); + + if let Some(name) = name { + s.push_str(&format!("{}", name)); + } else if name.is_none() && key_values.is_empty() { + s.push(';'); + } + + for key_value in key_values { + s.push_str(&format!("; {}", key_value)); + } + + s + } + Value::Url(url) => url.to_string(), + Value::Unknown(data) => String::from_utf8(data.to_vec()).unwrap(), + }, + }), CNAME => r .as_cname() .map(ToString::to_string) @@ -803,6 +832,8 @@ mod tests { use std::net::Ipv4Addr; use std::net::Ipv6Addr; use std::path::Path; + use trust_dns_proto::rr::rdata::caa::KeyValue; + use trust_dns_proto::rr::rdata::caa::CAA; use trust_dns_proto::rr::rdata::mx::MX; use trust_dns_proto::rr::rdata::naptr::NAPTR; use trust_dns_proto::rr::rdata::srv::SRV; @@ -835,6 +866,24 @@ mod tests { assert_eq!(func(&rdata), Some(DnsReturnRecord::Aname("".to_string()))); } + #[test] + fn rdata_to_return_record_caa() { + let func = rdata_to_return_record(RecordType::CAA); + let rdata = RData::CAA(CAA::new_issue( + false, + Some(Name::parse("example.com", None).unwrap()), + vec![KeyValue::new("account", "123456")], + )); + assert_eq!( + func(&rdata), + Some(DnsReturnRecord::Caa { + critical: false, + tag: "issue".to_string(), + value: "example.com; account=123456".to_string(), + }) + ); + } + #[test] fn rdata_to_return_record_cname() { let func = rdata_to_return_record(RecordType::CNAME);