mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
Compare commits
5 commits
b9e4c6fe2a
...
f5d6e92c4e
Author | SHA1 | Date | |
---|---|---|---|
|
f5d6e92c4e | ||
|
3bc7642dcf | ||
|
e9a1b8e83a | ||
|
f5d1f3f038 | ||
|
406162d7b8 |
4 changed files with 171 additions and 55 deletions
48
cli/tsc/dts/lib.deno.ns.d.ts
vendored
48
cli/tsc/dts/lib.deno.ns.d.ts
vendored
|
@ -4674,6 +4674,22 @@ declare namespace Deno {
|
|||
* and the promise returned will be rejected with an AbortError.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
/** */
|
||||
ttl?: boolean;
|
||||
}
|
||||
|
||||
export interface RecordWithTtl {
|
||||
/** */
|
||||
data:
|
||||
| string
|
||||
| CaaRecord
|
||||
| MxRecord
|
||||
| NaptrRecord
|
||||
| SoaRecord
|
||||
| SrvRecord
|
||||
| string[];
|
||||
/** */
|
||||
ttl: number;
|
||||
}
|
||||
|
||||
/** If {@linkcode Deno.resolveDns} is called with `"CAA"` record type
|
||||
|
@ -4748,6 +4764,37 @@ declare namespace Deno {
|
|||
target: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs DNS resolution against the given query, returning resolved
|
||||
* records with ttl values.
|
||||
*
|
||||
* Fails in the cases such as:
|
||||
*
|
||||
* - the query is in invalid format.
|
||||
* - the options have an invalid parameter. For example `nameServer.port` is
|
||||
* beyond the range of 16-bit unsigned integer.
|
||||
* - the request timed out.
|
||||
*
|
||||
* ```ts
|
||||
* const a = await Deno.resolveDns("example.com", "A", { ttl: true });
|
||||
*
|
||||
* const aaaa = await Deno.resolveDns("example.com", "AAAA", {
|
||||
* nameServer: { ipAddr: "8.8.8.8", port: 53 },
|
||||
* ttl: true,
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Requires `allow-net` permission.
|
||||
*
|
||||
* @tags allow-net
|
||||
* @category Network
|
||||
*/
|
||||
export function resolveDns(
|
||||
query: string,
|
||||
recordType: RecordType,
|
||||
options?: { ttl: true } & ResolveDnsOptions,
|
||||
): Promise<RecordWithTtl[]>;
|
||||
|
||||
/**
|
||||
* Performs DNS resolution against the given query, returning resolved
|
||||
* records.
|
||||
|
@ -4994,6 +5041,7 @@ declare namespace Deno {
|
|||
| SoaRecord[]
|
||||
| SrvRecord[]
|
||||
| string[][]
|
||||
| RecordWithTtl[]
|
||||
>;
|
||||
|
||||
/**
|
||||
|
|
114
ext/net/ops.rs
114
ext/net/ops.rs
|
@ -561,7 +561,7 @@ where
|
|||
|
||||
#[derive(Serialize, Eq, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
pub enum DnsReturnRecord {
|
||||
pub enum DnsRecordData {
|
||||
A(String),
|
||||
Aaaa(String),
|
||||
Aname(String),
|
||||
|
@ -603,6 +603,20 @@ pub enum DnsReturnRecord {
|
|||
Txt(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Eq, PartialEq, Debug)]
|
||||
#[serde()]
|
||||
pub struct DnsRecordWithTtl {
|
||||
pub data: DnsRecordData,
|
||||
pub ttl: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Eq, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
pub enum DnsReturnRecord {
|
||||
WithoutTtl(DnsRecordData),
|
||||
WithTtl(DnsRecordWithTtl),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResolveAddrArgs {
|
||||
|
@ -616,6 +630,7 @@ pub struct ResolveAddrArgs {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResolveDnsOption {
|
||||
name_server: Option<NameServer>,
|
||||
ttl: Option<bool>,
|
||||
}
|
||||
|
||||
fn default_port() -> u16 {
|
||||
|
@ -662,6 +677,8 @@ where
|
|||
system_conf::read_system_conf()?
|
||||
};
|
||||
|
||||
let ttl = options.as_ref().and_then(|o| o.ttl).unwrap_or(false);
|
||||
|
||||
{
|
||||
let mut s = state.borrow_mut();
|
||||
let perm = s.borrow_mut::<NP>();
|
||||
|
@ -720,8 +737,20 @@ where
|
|||
}
|
||||
_ => NetError::Dns(e),
|
||||
})?
|
||||
.records()
|
||||
.iter()
|
||||
.filter_map(|rdata| rdata_to_return_record(record_type)(rdata).transpose())
|
||||
.filter_map(|rec| {
|
||||
let data = format_rdata(record_type)(rec.data()).unwrap();
|
||||
if ttl {
|
||||
Some(DnsReturnRecord::WithTtl(DnsRecordWithTtl {
|
||||
data: data?,
|
||||
ttl: rec.ttl(),
|
||||
}))
|
||||
} else {
|
||||
Some(DnsReturnRecord::WithoutTtl(data?))
|
||||
}
|
||||
})
|
||||
.map(Ok)
|
||||
.collect::<Result<Vec<DnsReturnRecord>, NetError>>()
|
||||
}
|
||||
|
||||
|
@ -765,22 +794,22 @@ pub fn op_set_keepalive_inner(
|
|||
resource.set_keepalive(keepalive).map_err(NetError::Map)
|
||||
}
|
||||
|
||||
fn rdata_to_return_record(
|
||||
fn format_rdata(
|
||||
ty: RecordType,
|
||||
) -> impl Fn(&RData) -> Result<Option<DnsReturnRecord>, NetError> {
|
||||
) -> impl Fn(&RData) -> Result<Option<DnsRecordData>, NetError> {
|
||||
use RecordType::*;
|
||||
move |r: &RData| -> Result<Option<DnsReturnRecord>, NetError> {
|
||||
move |r: &RData| -> Result<Option<DnsRecordData>, NetError> {
|
||||
let record = match ty {
|
||||
A => r.as_a().map(ToString::to_string).map(DnsReturnRecord::A),
|
||||
A => r.as_a().map(ToString::to_string).map(DnsRecordData::A),
|
||||
AAAA => r
|
||||
.as_aaaa()
|
||||
.map(ToString::to_string)
|
||||
.map(DnsReturnRecord::Aaaa),
|
||||
.map(DnsRecordData::Aaaa),
|
||||
ANAME => r
|
||||
.as_aname()
|
||||
.map(ToString::to_string)
|
||||
.map(DnsReturnRecord::Aname),
|
||||
CAA => r.as_caa().map(|caa| DnsReturnRecord::Caa {
|
||||
.map(DnsRecordData::Aname),
|
||||
CAA => r.as_caa().map(|caa| DnsRecordData::Caa {
|
||||
critical: caa.issuer_critical(),
|
||||
tag: caa.tag().to_string(),
|
||||
value: match caa.value() {
|
||||
|
@ -807,12 +836,12 @@ fn rdata_to_return_record(
|
|||
CNAME => r
|
||||
.as_cname()
|
||||
.map(ToString::to_string)
|
||||
.map(DnsReturnRecord::Cname),
|
||||
MX => r.as_mx().map(|mx| DnsReturnRecord::Mx {
|
||||
.map(DnsRecordData::Cname),
|
||||
MX => r.as_mx().map(|mx| DnsRecordData::Mx {
|
||||
preference: mx.preference(),
|
||||
exchange: mx.exchange().to_string(),
|
||||
}),
|
||||
NAPTR => r.as_naptr().map(|naptr| DnsReturnRecord::Naptr {
|
||||
NAPTR => r.as_naptr().map(|naptr| DnsRecordData::Naptr {
|
||||
order: naptr.order(),
|
||||
preference: naptr.preference(),
|
||||
flags: String::from_utf8(naptr.flags().to_vec()).unwrap(),
|
||||
|
@ -820,12 +849,9 @@ fn rdata_to_return_record(
|
|||
regexp: String::from_utf8(naptr.regexp().to_vec()).unwrap(),
|
||||
replacement: naptr.replacement().to_string(),
|
||||
}),
|
||||
NS => r.as_ns().map(ToString::to_string).map(DnsReturnRecord::Ns),
|
||||
PTR => r
|
||||
.as_ptr()
|
||||
.map(ToString::to_string)
|
||||
.map(DnsReturnRecord::Ptr),
|
||||
SOA => r.as_soa().map(|soa| DnsReturnRecord::Soa {
|
||||
NS => r.as_ns().map(ToString::to_string).map(DnsRecordData::Ns),
|
||||
PTR => r.as_ptr().map(ToString::to_string).map(DnsRecordData::Ptr),
|
||||
SOA => r.as_soa().map(|soa| DnsRecordData::Soa {
|
||||
mname: soa.mname().to_string(),
|
||||
rname: soa.rname().to_string(),
|
||||
serial: soa.serial(),
|
||||
|
@ -834,7 +860,7 @@ fn rdata_to_return_record(
|
|||
expire: soa.expire(),
|
||||
minimum: soa.minimum(),
|
||||
}),
|
||||
SRV => r.as_srv().map(|srv| DnsReturnRecord::Srv {
|
||||
SRV => r.as_srv().map(|srv| DnsRecordData::Srv {
|
||||
priority: srv.priority(),
|
||||
weight: srv.weight(),
|
||||
port: srv.port(),
|
||||
|
@ -848,7 +874,7 @@ fn rdata_to_return_record(
|
|||
bytes.iter().map(|&b| b as char).collect::<String>()
|
||||
})
|
||||
.collect();
|
||||
DnsReturnRecord::Txt(texts)
|
||||
DnsRecordData::Txt(texts)
|
||||
}),
|
||||
_ => return Err(NetError::UnsupportedRecordType),
|
||||
};
|
||||
|
@ -891,37 +917,37 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rdata_to_return_record_a() {
|
||||
let func = rdata_to_return_record(RecordType::A);
|
||||
let func = format_rdata(RecordType::A);
|
||||
let rdata = RData::A(A(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::A("127.0.0.1".to_string()))
|
||||
Some(DnsRecordData::A("127.0.0.1".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rdata_to_return_record_aaaa() {
|
||||
let func = rdata_to_return_record(RecordType::AAAA);
|
||||
let func = format_rdata(RecordType::AAAA);
|
||||
let rdata = RData::AAAA(AAAA(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Aaaa("::1".to_string()))
|
||||
Some(DnsRecordData::Aaaa("::1".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rdata_to_return_record_aname() {
|
||||
let func = rdata_to_return_record(RecordType::ANAME);
|
||||
let func = format_rdata(RecordType::ANAME);
|
||||
let rdata = RData::ANAME(ANAME(Name::new()));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Aname("".to_string()))
|
||||
Some(DnsRecordData::Aname("".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rdata_to_return_record_caa() {
|
||||
let func = rdata_to_return_record(RecordType::CAA);
|
||||
let func = format_rdata(RecordType::CAA);
|
||||
let rdata = RData::CAA(CAA::new_issue(
|
||||
false,
|
||||
Some(Name::parse("example.com", None).unwrap()),
|
||||
|
@ -929,7 +955,7 @@ mod tests {
|
|||
));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Caa {
|
||||
Some(DnsRecordData::Caa {
|
||||
critical: false,
|
||||
tag: "issue".to_string(),
|
||||
value: "example.com; account=123456".to_string(),
|
||||
|
@ -939,21 +965,21 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rdata_to_return_record_cname() {
|
||||
let func = rdata_to_return_record(RecordType::CNAME);
|
||||
let func = format_rdata(RecordType::CNAME);
|
||||
let rdata = RData::CNAME(CNAME(Name::new()));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Cname("".to_string()))
|
||||
Some(DnsRecordData::Cname("".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rdata_to_return_record_mx() {
|
||||
let func = rdata_to_return_record(RecordType::MX);
|
||||
let func = format_rdata(RecordType::MX);
|
||||
let rdata = RData::MX(MX::new(10, Name::new()));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Mx {
|
||||
Some(DnsRecordData::Mx {
|
||||
preference: 10,
|
||||
exchange: "".to_string()
|
||||
})
|
||||
|
@ -962,7 +988,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rdata_to_return_record_naptr() {
|
||||
let func = rdata_to_return_record(RecordType::NAPTR);
|
||||
let func = format_rdata(RecordType::NAPTR);
|
||||
let rdata = RData::NAPTR(NAPTR::new(
|
||||
1,
|
||||
2,
|
||||
|
@ -973,7 +999,7 @@ mod tests {
|
|||
));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Naptr {
|
||||
Some(DnsRecordData::Naptr {
|
||||
order: 1,
|
||||
preference: 2,
|
||||
flags: "".to_string(),
|
||||
|
@ -986,27 +1012,27 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rdata_to_return_record_ns() {
|
||||
let func = rdata_to_return_record(RecordType::NS);
|
||||
let func = format_rdata(RecordType::NS);
|
||||
let rdata = RData::NS(NS(Name::new()));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Ns("".to_string()))
|
||||
Some(DnsRecordData::Ns("".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rdata_to_return_record_ptr() {
|
||||
let func = rdata_to_return_record(RecordType::PTR);
|
||||
let func = format_rdata(RecordType::PTR);
|
||||
let rdata = RData::PTR(PTR(Name::new()));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Ptr("".to_string()))
|
||||
Some(DnsRecordData::Ptr("".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rdata_to_return_record_soa() {
|
||||
let func = rdata_to_return_record(RecordType::SOA);
|
||||
let func = format_rdata(RecordType::SOA);
|
||||
let rdata = RData::SOA(SOA::new(
|
||||
Name::new(),
|
||||
Name::new(),
|
||||
|
@ -1018,7 +1044,7 @@ mod tests {
|
|||
));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Soa {
|
||||
Some(DnsRecordData::Soa {
|
||||
mname: "".to_string(),
|
||||
rname: "".to_string(),
|
||||
serial: 0,
|
||||
|
@ -1032,11 +1058,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rdata_to_return_record_srv() {
|
||||
let func = rdata_to_return_record(RecordType::SRV);
|
||||
let func = format_rdata(RecordType::SRV);
|
||||
let rdata = RData::SRV(SRV::new(1, 2, 3, Name::new()));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Srv {
|
||||
Some(DnsRecordData::Srv {
|
||||
priority: 1,
|
||||
weight: 2,
|
||||
port: 3,
|
||||
|
@ -1047,7 +1073,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rdata_to_return_record_txt() {
|
||||
let func = rdata_to_return_record(RecordType::TXT);
|
||||
let func = format_rdata(RecordType::TXT);
|
||||
let rdata = RData::TXT(TXT::from_bytes(vec![
|
||||
"foo".as_bytes(),
|
||||
"bar".as_bytes(),
|
||||
|
@ -1056,7 +1082,7 @@ mod tests {
|
|||
]));
|
||||
assert_eq!(
|
||||
func(&rdata).unwrap(),
|
||||
Some(DnsReturnRecord::Txt(vec![
|
||||
Some(DnsRecordData::Txt(vec![
|
||||
"foo".to_string(),
|
||||
"bar".to_string(),
|
||||
"£".to_string(),
|
||||
|
|
|
@ -186,20 +186,22 @@ export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery {
|
|||
this.#tries = tries;
|
||||
}
|
||||
|
||||
async #query(query: string, recordType: Deno.RecordType) {
|
||||
// TODO(@bartlomieju): TTL logic.
|
||||
|
||||
async #query(query: string, recordType: Deno.RecordType, ttl?: boolean) {
|
||||
let code: number;
|
||||
let ret: Awaited<ReturnType<typeof Deno.resolveDns>>;
|
||||
|
||||
const resolveOptions: Deno.ResolveDnsOptions = {
|
||||
...ttl !== undefined ? { ttl } : {},
|
||||
};
|
||||
|
||||
if (this.#servers.length) {
|
||||
for (const [ipAddr, port] of this.#servers) {
|
||||
const resolveOptions = {
|
||||
Object.assign(resolveOptions, {
|
||||
nameServer: {
|
||||
ipAddr,
|
||||
port,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
({ code, ret } = await this.#resolve(
|
||||
query,
|
||||
|
@ -212,7 +214,7 @@ export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
({ code, ret } = await this.#resolve(query, recordType));
|
||||
({ code, ret } = await this.#resolve(query, recordType, resolveOptions));
|
||||
}
|
||||
|
||||
return { code: code!, ret: ret! };
|
||||
|
@ -351,18 +353,34 @@ export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery {
|
|||
}
|
||||
|
||||
queryA(req: QueryReqWrap, name: string): number {
|
||||
this.#query(name, "A").then(({ code, ret }) => {
|
||||
req.oncomplete(code, ret);
|
||||
this.#query(name, "A", req.ttl).then(({ code, ret }) => {
|
||||
let recordsWithTtl;
|
||||
if (req.ttl) {
|
||||
recordsWithTtl = (ret as Deno.RecordWithTtl[]).map((val) => ({
|
||||
address: val?.data,
|
||||
ttl: val?.ttl,
|
||||
}));
|
||||
}
|
||||
|
||||
req.oncomplete(code, recordsWithTtl ?? ret);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
queryAaaa(req: QueryReqWrap, name: string): number {
|
||||
this.#query(name, "AAAA").then(({ code, ret }) => {
|
||||
const records = (ret as string[]).map((record) => compressIPv6(record));
|
||||
this.#query(name, "AAAA", req.ttl).then(({ code, ret }) => {
|
||||
let recordsWithTtl;
|
||||
if (req.ttl) {
|
||||
recordsWithTtl = (ret as Deno.RecordWithTtl[]).map((val) => ({
|
||||
address: compressIPv6(val?.data as string),
|
||||
ttl: val?.ttl,
|
||||
}));
|
||||
} else {
|
||||
ret = (ret as string[]).map((record) => compressIPv6(record));
|
||||
}
|
||||
|
||||
req.oncomplete(code, records);
|
||||
req.oncomplete(code, recordsWithTtl ?? ret);
|
||||
});
|
||||
|
||||
return 0;
|
||||
|
|
24
tests/unit/dns_test.ts
Normal file
24
tests/unit/dns_test.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
import { resolve4, resolve6 } from "node:dns/promises";
|
||||
import { assertEquals } from "@std/assert/equals";
|
||||
|
||||
Deno.test({
|
||||
name: "Dns resolving for ttl values, A and AAAA records",
|
||||
async fn() {
|
||||
const ARecord = "34.120.54.55";
|
||||
const AAAARecord = "2600:1901::6d85::";
|
||||
|
||||
const ARes1 = await Deno.resolveDns("deno.com", "A", { ttl: true });
|
||||
const ARes2 = await resolve4("deno.com", { ttl: true });
|
||||
|
||||
assertEquals(ARes1[0].data, ARecord);
|
||||
assertEquals(ARes2[0].address, ARecord);
|
||||
|
||||
const AAAARes1 = await Deno.resolveDns("deno.com", "AAAA", { ttl: true });
|
||||
const AAAARes2 = await resolve6("deno.com", { ttl: true });
|
||||
|
||||
assertEquals(AAAARes1[0].data, AAAARecord);
|
||||
assertEquals(AAAARes2[0].address, AAAARecord);
|
||||
},
|
||||
});
|
Loading…
Add table
Reference in a new issue