1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00

Compare commits

...

7 commits

Author SHA1 Message Date
siaeyy
f5d6e92c4e
Merge 3bc7642dcf into 395628026f 2025-01-20 15:13:19 +01:00
Bartek Iwańczuk
395628026f
fix(ext/os): pass SignalState to web worker (#27741)
Closes https://github.com/denoland/deno/issues/27717

Made a mistake in https://github.com/denoland/deno/pull/27655 and
didn't add the `SignalStore` for web worker.
2025-01-20 19:43:15 +05:30
Divy Srivastava
4f27d7cdc0
fix(ext/node): GCM auth tag check on DechiperIv#final (#27733) 2025-01-20 18:16:44 +05:30
siaeyy
3bc7642dcf test(ext/dns): Test for dns resolving 2025-01-15 05:27:52 +03:00
siaeyy
e9a1b8e83a feat(node/dns): Tts for A and AAAA record queries 2025-01-15 05:26:21 +03:00
siaeyy
f5d1f3f038 feat(ext/net): Dns resolving for tts 2025-01-15 05:25:19 +03:00
siaeyy
406162d7b8 feat(ts/dns): RecordWithTtl interface 2025-01-15 05:23:39 +03:00
10 changed files with 196 additions and 79 deletions

View file

@ -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[]
>;
/**

View file

@ -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(),

View file

@ -226,7 +226,6 @@ deno_core::extension!(deno_node,
ops::crypto::op_node_decipheriv_decrypt,
ops::crypto::op_node_decipheriv_final,
ops::crypto::op_node_decipheriv_set_aad,
ops::crypto::op_node_decipheriv_take,
ops::crypto::op_node_dh_compute_secret,
ops::crypto::op_node_diffie_hellman,
ops::crypto::op_node_ecdh_compute_public_key,

View file

@ -500,6 +500,11 @@ impl Decipher {
auth_tag: &[u8],
) -> Result<(), DecipherError> {
use Decipher::*;
if input.is_empty() && !matches!(self, Aes128Gcm(_) | Aes256Gcm(_)) {
return Ok(());
}
match (self, auto_pad) {
(Aes128Cbc(decryptor), true) => {
assert!(input.len() == 16);

View file

@ -332,17 +332,6 @@ pub fn op_node_decipheriv_decrypt(
true
}
#[op2(fast)]
pub fn op_node_decipheriv_take(
state: &mut OpState,
#[smi] rid: u32,
) -> Result<(), cipher::DecipherContextError> {
let context = state.resource_table.take::<cipher::DecipherContext>(rid)?;
Rc::try_unwrap(context)
.map_err(|_| cipher::DecipherContextError::ContextInUse)?;
Ok(())
}
#[op2]
pub fn op_node_decipheriv_final(
state: &mut OpState,

View file

@ -18,7 +18,6 @@ import {
op_node_decipheriv_decrypt,
op_node_decipheriv_final,
op_node_decipheriv_set_aad,
op_node_decipheriv_take,
op_node_private_decrypt,
op_node_private_encrypt,
op_node_public_encrypt,
@ -352,14 +351,6 @@ export class Decipheriv extends Transform implements Cipher {
}
final(encoding: string = getDefaultEncoding()): Buffer | string {
if (!this.#needsBlockCache || this.#cache.cache.byteLength === 0) {
op_node_decipheriv_take(this.#context);
return encoding === "buffer" ? Buffer.from([]) : "";
}
if (this.#cache.cache.byteLength != 16) {
throw new Error("Invalid final block size");
}
let buf = new Buffer(16);
op_node_decipheriv_final(
this.#context,
@ -369,6 +360,13 @@ export class Decipheriv extends Transform implements Cipher {
this.#authTag || NO_TAG,
);
if (!this.#needsBlockCache || this.#cache.cache.byteLength === 0) {
return encoding === "buffer" ? Buffer.from([]) : "";
}
if (this.#cache.cache.byteLength != 16) {
throw new Error("Invalid final block size");
}
buf = buf.subarray(0, 16 - buf.at(-1)); // Padded in Pkcs7 mode
return encoding === "buffer" ? buf : buf.toString(encoding);
}

View file

@ -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;

View file

@ -116,6 +116,12 @@ deno_core::extension!(
"op_exit" | "op_set_exit_code" | "op_get_exit_code" =>
op.with_implementation_from(&deno_core::op_void_sync()),
_ => op,
},
state = |state| {
#[cfg(unix)]
{
state.put(ops::signal::SignalState::default());
}
}
);

24
tests/unit/dns_test.ts Normal file
View 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);
},
});

View file

@ -4,7 +4,7 @@ import crypto from "node:crypto";
import { Buffer } from "node:buffer";
import testVectors128 from "./gcmEncryptExtIV128.json" with { type: "json" };
import testVectors256 from "./gcmEncryptExtIV256.json" with { type: "json" };
import { assertEquals } from "@std/assert";
import { assertEquals, assertThrows } from "@std/assert";
const aesGcm = (bits: string, key: Uint8Array) => {
const ALGO = bits == "128" ? `aes-128-gcm` : `aes-256-gcm`;
@ -123,7 +123,7 @@ Deno.test({
// Issue #27441
// https://github.com/denoland/deno/issues/27441
Deno.test({
name: "aes-256-gcm supports IV of non standard length",
name: "aes-256-gcm supports IV of non standard length and auth tag check",
fn() {
const decipher = crypto.createDecipheriv(
"aes-256-gcm",
@ -136,6 +136,10 @@ Deno.test({
"utf-8",
);
assertEquals(decrypted, "this is a secret");
decipher.final();
assertThrows(
() => decipher.final(),
TypeError,
"Failed to authenticate data",
);
},
});