tangerine 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -35
- package/index.d.ts +31 -1
- package/index.js +38 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -237,6 +237,26 @@ tangerine.resolve('forwardemail.net').then(console.log);
|
|
|
237
237
|
|
|
238
238
|
### `tangerine.resolve(hostname[, rrtype, options, abortController])`
|
|
239
239
|
|
|
240
|
+
Tangerine supports the following additional properties in the `options` Object argument:
|
|
241
|
+
|
|
242
|
+
* `ecsSubnet` (String) - EDNS Client Subnet (ECS) option for geolocation-aware DNS responses.
|
|
243
|
+
* `purgeCache` (Boolean) - If `true`, bypass and refresh the cached result.
|
|
244
|
+
* `dnssecSecure` (Boolean) - If `true`, set the EDNS0 DO (DNSSEC OK) flag in the outgoing DoH query and return a `{ secure, answers }` object instead of the normal result array. The `secure` property is `true` when the upstream resolver (Cloudflare/Google) has validated the response via DNSSEC (i.e. the AD flag is set). This is useful for [RFC 7672 Section 2.2.2](https://datatracker.ietf.org/doc/html/rfc7672#section-2.2.2) DANE implementations that need to check whether an MX host's zone is DNSSEC-signed before attempting TLSA lookups.
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
const tangerine = new Tangerine();
|
|
248
|
+
|
|
249
|
+
// Check if a domain's zone is DNSSEC-signed
|
|
250
|
+
const result = await tangerine.resolve('cloudflare.com', 'A', { dnssecSecure: true });
|
|
251
|
+
console.log(result);
|
|
252
|
+
// { secure: true, answers: [{ name: 'cloudflare.com', type: 'A', ... }] }
|
|
253
|
+
|
|
254
|
+
// Non-DNSSEC zone
|
|
255
|
+
const result2 = await tangerine.resolve('google.com', 'A', { dnssecSecure: true });
|
|
256
|
+
console.log(result2);
|
|
257
|
+
// { secure: false, answers: [{ name: 'google.com', type: 'A', ... }] }
|
|
258
|
+
```
|
|
259
|
+
|
|
240
260
|
### `tangerine.resolve4(hostname[, options, abortController])`
|
|
241
261
|
|
|
242
262
|
Tangerine supports a new `ecsSubnet` property in the `options` Object argument.
|
|
@@ -534,25 +554,27 @@ We have written extensive benchmarks to show that :tangerine: Tangerine is as fa
|
|
|
534
554
|
|
|
535
555
|
#### Latest Automated Benchmark Results
|
|
536
556
|
|
|
537
|
-
**Last Updated:** 2026-
|
|
557
|
+
**Last Updated:** 2026-03-07
|
|
538
558
|
|
|
539
559
|
| Node Version | Platform | Arch | Timestamp |
|
|
540
560
|
| ------------ | -------- | ---- | ------------ |
|
|
541
561
|
| v18.20.8 | linux | x64 | Dec 21, 2025 |
|
|
542
|
-
| v20.19.6 | linux | x64 | Jan
|
|
543
|
-
| v20.20.0 | linux | x64 | Feb
|
|
562
|
+
| v20.19.6 | linux | x64 | Jan 21, 2026 |
|
|
563
|
+
| v20.20.0 | linux | x64 | Feb 24, 2026 |
|
|
544
564
|
| v22.21.1 | linux | x64 | Dec 21, 2025 |
|
|
545
|
-
| v22.22.0 | linux | x64 | Jan
|
|
565
|
+
| v22.22.0 | linux | x64 | Jan 22, 2026 |
|
|
546
566
|
| v24.12.0 | linux | x64 | Dec 21, 2025 |
|
|
547
|
-
| v24.13.0 | linux | x64 | Feb
|
|
548
|
-
| v24.13.1 | linux | x64 |
|
|
567
|
+
| v24.13.0 | linux | x64 | Feb 18, 2026 |
|
|
568
|
+
| v24.13.1 | linux | x64 | Mar 3, 2026 |
|
|
569
|
+
| v24.14.0 | linux | x64 | Mar 6, 2026 |
|
|
549
570
|
| v25.2.1 | linux | x64 | Dec 21, 2025 |
|
|
550
|
-
| v25.3.0 | linux | x64 | Jan
|
|
551
|
-
| v25.4.0 | linux | x64 | Jan
|
|
552
|
-
| v25.5.0 | linux | x64 | Jan
|
|
553
|
-
| v25.6.0 | linux | x64 | Feb
|
|
554
|
-
| v25.6.1 | linux | x64 | Feb
|
|
555
|
-
| v25.7.0 | linux | x64 | Feb
|
|
571
|
+
| v25.3.0 | linux | x64 | Jan 13, 2026 |
|
|
572
|
+
| v25.4.0 | linux | x64 | Jan 19, 2026 |
|
|
573
|
+
| v25.5.0 | linux | x64 | Jan 26, 2026 |
|
|
574
|
+
| v25.6.0 | linux | x64 | Feb 3, 2026 |
|
|
575
|
+
| v25.6.1 | linux | x64 | Feb 10, 2026 |
|
|
576
|
+
| v25.7.0 | linux | x64 | Feb 24, 2026 |
|
|
577
|
+
| v25.8.0 | linux | x64 | Mar 3, 2026 |
|
|
556
578
|
|
|
557
579
|
<details>
|
|
558
580
|
<summary>Click to expand detailed benchmark results</summary>
|
|
@@ -857,12 +879,12 @@ Fastest without caching is: tangerine.reverse GET without caching
|
|
|
857
879
|
|
|
858
880
|
```text
|
|
859
881
|
Started: lookup
|
|
860
|
-
tangerine.lookup POST with caching using Cloudflare x
|
|
861
|
-
tangerine.lookup POST without caching using Cloudflare x
|
|
862
|
-
tangerine.lookup GET with caching using Cloudflare x
|
|
863
|
-
tangerine.lookup GET without caching using Cloudflare x
|
|
864
|
-
dns.promises.lookup with caching using Cloudflare x
|
|
865
|
-
dns.promises.lookup without caching using Cloudflare x 2,
|
|
882
|
+
tangerine.lookup POST with caching using Cloudflare x 333,315 ops/sec ±1.61% (85 runs sampled)
|
|
883
|
+
tangerine.lookup POST without caching using Cloudflare x 227 ops/sec ±1.93% (82 runs sampled)
|
|
884
|
+
tangerine.lookup GET with caching using Cloudflare x 320,901 ops/sec ±0.68% (90 runs sampled)
|
|
885
|
+
tangerine.lookup GET without caching using Cloudflare x 262 ops/sec ±2.60% (81 runs sampled)
|
|
886
|
+
dns.promises.lookup with caching using Cloudflare x 10,104,926 ops/sec ±1.20% (84 runs sampled)
|
|
887
|
+
dns.promises.lookup without caching using Cloudflare x 2,172 ops/sec ±0.39% (86 runs sampled)
|
|
866
888
|
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
867
889
|
```
|
|
868
890
|
|
|
@@ -870,32 +892,77 @@ Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
|
870
892
|
|
|
871
893
|
```text
|
|
872
894
|
Started: resolve
|
|
873
|
-
tangerine.resolve POST with caching using Cloudflare x 1,
|
|
874
|
-
tangerine.resolve POST without caching using Cloudflare x
|
|
875
|
-
tangerine.resolve GET with caching using Cloudflare x 1,
|
|
876
|
-
tangerine.resolve GET without caching using Cloudflare x
|
|
877
|
-
tangerine.resolve POST with caching using Google x
|
|
878
|
-
tangerine.resolve POST without caching using Google x
|
|
879
|
-
tangerine.resolve GET with caching using Google x 1,127,
|
|
880
|
-
tangerine.resolve GET without caching using Google x
|
|
881
|
-
resolver.resolve with caching using Cloudflare x 8,
|
|
882
|
-
resolver.resolve without caching using Cloudflare x
|
|
883
|
-
Fastest without caching is:
|
|
895
|
+
tangerine.resolve POST with caching using Cloudflare x 1,155,993 ops/sec ±0.68% (89 runs sampled)
|
|
896
|
+
tangerine.resolve POST without caching using Cloudflare x 238 ops/sec ±1.44% (82 runs sampled)
|
|
897
|
+
tangerine.resolve GET with caching using Cloudflare x 1,130,246 ops/sec ±0.43% (89 runs sampled)
|
|
898
|
+
tangerine.resolve GET without caching using Cloudflare x 271 ops/sec ±1.13% (85 runs sampled)
|
|
899
|
+
tangerine.resolve POST with caching using Google x 534 ops/sec ±195.91% (89 runs sampled)
|
|
900
|
+
tangerine.resolve POST without caching using Google x 180 ops/sec ±23.75% (71 runs sampled)
|
|
901
|
+
tangerine.resolve GET with caching using Google x 1,127,220 ops/sec ±0.26% (91 runs sampled)
|
|
902
|
+
tangerine.resolve GET without caching using Google x 194 ops/sec ±16.84% (63 runs sampled)
|
|
903
|
+
resolver.resolve with caching using Cloudflare x 8,475,317 ops/sec ±0.93% (83 runs sampled)
|
|
904
|
+
resolver.resolve without caching using Cloudflare x 345 ops/sec ±1.00% (78 runs sampled)
|
|
905
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare
|
|
884
906
|
```
|
|
885
907
|
|
|
886
908
|
**reverse:**
|
|
887
909
|
|
|
888
910
|
```text
|
|
889
911
|
Started: reverse
|
|
890
|
-
tangerine.reverse GET with caching x
|
|
891
|
-
tangerine.reverse GET without caching x
|
|
892
|
-
resolver.reverse with caching x
|
|
893
|
-
resolver.reverse without caching x
|
|
894
|
-
dns.promises.reverse with caching x 8,
|
|
895
|
-
dns.promises.reverse without caching x 0.
|
|
912
|
+
tangerine.reverse GET with caching x 331,840 ops/sec ±0.57% (89 runs sampled)
|
|
913
|
+
tangerine.reverse GET without caching x 241 ops/sec ±1.30% (80 runs sampled)
|
|
914
|
+
resolver.reverse with caching x 8,769,670 ops/sec ±0.60% (87 runs sampled)
|
|
915
|
+
resolver.reverse without caching x 24.95 ops/sec ±193.07% (22 runs sampled)
|
|
916
|
+
dns.promises.reverse with caching x 8,895,968 ops/sec ±0.71% (88 runs sampled)
|
|
917
|
+
dns.promises.reverse without caching x 0.05 ops/sec ±112.55% (5 runs sampled)
|
|
896
918
|
Fastest without caching is: tangerine.reverse GET without caching
|
|
897
919
|
```
|
|
898
920
|
|
|
921
|
+
##### Node.js v24.14.0
|
|
922
|
+
|
|
923
|
+
**lookup:**
|
|
924
|
+
|
|
925
|
+
```text
|
|
926
|
+
Started: lookup
|
|
927
|
+
tangerine.lookup POST with caching using Cloudflare x 1,326 ops/sec ±195.20% (85 runs sampled)
|
|
928
|
+
tangerine.lookup POST without caching using Cloudflare x 59.26 ops/sec ±6.44% (74 runs sampled)
|
|
929
|
+
tangerine.lookup GET with caching using Cloudflare x 314,095 ops/sec ±0.32% (90 runs sampled)
|
|
930
|
+
tangerine.lookup GET without caching using Cloudflare x 63.80 ops/sec ±2.69% (77 runs sampled)
|
|
931
|
+
dns.promises.lookup with caching using Cloudflare x 10,223,830 ops/sec ±0.86% (85 runs sampled)
|
|
932
|
+
dns.promises.lookup without caching using Cloudflare x 2,175 ops/sec ±0.84% (87 runs sampled)
|
|
933
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
**resolve:**
|
|
937
|
+
|
|
938
|
+
```text
|
|
939
|
+
Started: resolve
|
|
940
|
+
tangerine.resolve POST with caching using Cloudflare x 1,293 ops/sec ±195.78% (88 runs sampled)
|
|
941
|
+
tangerine.resolve POST without caching using Cloudflare x 70.18 ops/sec ±3.02% (84 runs sampled)
|
|
942
|
+
tangerine.resolve GET with caching using Cloudflare x 1,118,050 ops/sec ±0.39% (90 runs sampled)
|
|
943
|
+
tangerine.resolve GET without caching using Cloudflare x 62.96 ops/sec ±2.78% (76 runs sampled)
|
|
944
|
+
tangerine.resolve POST with caching using Google x 1,652 ops/sec ±195.71% (90 runs sampled)
|
|
945
|
+
tangerine.resolve POST without caching using Google x 58.58 ops/sec ±8.14% (78 runs sampled)
|
|
946
|
+
tangerine.resolve GET with caching using Google x 1,103,516 ops/sec ±1.21% (87 runs sampled)
|
|
947
|
+
tangerine.resolve GET without caching using Google x 61.12 ops/sec ±3.21% (75 runs sampled)
|
|
948
|
+
resolver.resolve with caching using Cloudflare x 8,524,784 ops/sec ±0.95% (87 runs sampled)
|
|
949
|
+
resolver.resolve without caching using Cloudflare x 70.34 ops/sec ±1.22% (68 runs sampled)
|
|
950
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
**reverse:**
|
|
954
|
+
|
|
955
|
+
```text
|
|
956
|
+
Started: reverse
|
|
957
|
+
tangerine.reverse GET with caching x 1,298 ops/sec ±195.25% (90 runs sampled)
|
|
958
|
+
tangerine.reverse GET without caching x 70.67 ops/sec ±2.91% (84 runs sampled)
|
|
959
|
+
resolver.reverse with caching x 8,799,672 ops/sec ±0.94% (85 runs sampled)
|
|
960
|
+
resolver.reverse without caching x 71.99 ops/sec ±1.45% (70 runs sampled)
|
|
961
|
+
dns.promises.reverse with caching x 8,834,313 ops/sec ±0.77% (85 runs sampled)
|
|
962
|
+
dns.promises.reverse without caching x 70.74 ops/sec ±1.11% (70 runs sampled)
|
|
963
|
+
Fastest without caching is: resolver.reverse without caching, dns.promises.reverse without caching, tangerine.reverse GET without caching
|
|
964
|
+
```
|
|
965
|
+
|
|
899
966
|
##### Node.js v25.2.1
|
|
900
967
|
|
|
901
968
|
**lookup:**
|
|
@@ -1211,6 +1278,51 @@ dns.promises.reverse without caching x 67.14 ops/sec ±0.80% (79 runs sampled)
|
|
|
1211
1278
|
Fastest without caching is: dns.promises.reverse without caching, resolver.reverse without caching
|
|
1212
1279
|
```
|
|
1213
1280
|
|
|
1281
|
+
##### Node.js v25.8.0
|
|
1282
|
+
|
|
1283
|
+
**lookup:**
|
|
1284
|
+
|
|
1285
|
+
```text
|
|
1286
|
+
Started: lookup
|
|
1287
|
+
tangerine.lookup POST with caching using Cloudflare x 348,234 ops/sec ±1.43% (89 runs sampled)
|
|
1288
|
+
tangerine.lookup POST without caching using Cloudflare x 245 ops/sec ±1.75% (83 runs sampled)
|
|
1289
|
+
tangerine.lookup GET with caching using Cloudflare x 340,115 ops/sec ±0.72% (89 runs sampled)
|
|
1290
|
+
tangerine.lookup GET without caching using Cloudflare x 223 ops/sec ±2.25% (81 runs sampled)
|
|
1291
|
+
dns.promises.lookup with caching using Cloudflare x 1,759 ops/sec ±195.97% (88 runs sampled)
|
|
1292
|
+
dns.promises.lookup without caching using Cloudflare x 2,223 ops/sec ±0.55% (87 runs sampled)
|
|
1293
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
**resolve:**
|
|
1297
|
+
|
|
1298
|
+
```text
|
|
1299
|
+
Started: resolve
|
|
1300
|
+
tangerine.resolve POST with caching using Cloudflare x 1,224,642 ops/sec ±0.58% (87 runs sampled)
|
|
1301
|
+
tangerine.resolve POST without caching using Cloudflare x 248 ops/sec ±1.54% (83 runs sampled)
|
|
1302
|
+
tangerine.resolve GET with caching using Cloudflare x 1,168,649 ops/sec ±0.55% (85 runs sampled)
|
|
1303
|
+
tangerine.resolve GET without caching using Cloudflare x 264 ops/sec ±1.04% (82 runs sampled)
|
|
1304
|
+
tangerine.resolve POST with caching using Google x 1,411 ops/sec ±195.76% (87 runs sampled)
|
|
1305
|
+
tangerine.resolve POST without caching using Google x 178 ops/sec ±24.83% (67 runs sampled)
|
|
1306
|
+
tangerine.resolve GET with caching using Google x 1,188,395 ops/sec ±0.40% (88 runs sampled)
|
|
1307
|
+
tangerine.resolve GET without caching using Google x 237 ops/sec ±14.93% (74 runs sampled)
|
|
1308
|
+
resolver.resolve with caching using Cloudflare x 8.39 ops/sec ±196.00% (84 runs sampled)
|
|
1309
|
+
resolver.resolve without caching using Cloudflare x 109 ops/sec ±113.88% (68 runs sampled)
|
|
1310
|
+
Fastest without caching is: tangerine.resolve GET without caching using Cloudflare, tangerine.resolve POST without caching using Google
|
|
1311
|
+
```
|
|
1312
|
+
|
|
1313
|
+
**reverse:**
|
|
1314
|
+
|
|
1315
|
+
```text
|
|
1316
|
+
Started: reverse
|
|
1317
|
+
tangerine.reverse GET with caching x 346,448 ops/sec ±4.35% (86 runs sampled)
|
|
1318
|
+
tangerine.reverse GET without caching x 259 ops/sec ±1.42% (83 runs sampled)
|
|
1319
|
+
resolver.reverse with caching x 9,199,217 ops/sec ±0.52% (88 runs sampled)
|
|
1320
|
+
resolver.reverse without caching x 13.67 ops/sec ±188.06% (45 runs sampled)
|
|
1321
|
+
dns.promises.reverse with caching x 9,227,637 ops/sec ±0.33% (89 runs sampled)
|
|
1322
|
+
dns.promises.reverse without caching x 0.07 ops/sec ±133.35% (5 runs sampled)
|
|
1323
|
+
Fastest without caching is: tangerine.reverse GET without caching
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1214
1326
|
</details>
|
|
1215
1327
|
|
|
1216
1328
|
<!-- BENCHMARK_RESULTS_END -->
|
package/index.d.ts
CHANGED
|
@@ -326,6 +326,35 @@ export type AnyRecord = {
|
|
|
326
326
|
|
|
327
327
|
export type ResolveOptions = {
|
|
328
328
|
ttl?: boolean;
|
|
329
|
+
/**
|
|
330
|
+
* If true, set the EDNS0 DO (DNSSEC OK) flag in the outgoing DoH query
|
|
331
|
+
* and return a `{ secure, answers }` object instead of the normal result.
|
|
332
|
+
* The `secure` property is true when the upstream resolver has validated
|
|
333
|
+
* the response via DNSSEC (i.e. the AD flag is set).
|
|
334
|
+
*/
|
|
335
|
+
dnssecSecure?: boolean;
|
|
336
|
+
/**
|
|
337
|
+
* EDNS Client Subnet (ECS) option for geolocation-aware DNS responses.
|
|
338
|
+
*/
|
|
339
|
+
ecsSubnet?: string;
|
|
340
|
+
/**
|
|
341
|
+
* If true, bypass and refresh the cached result.
|
|
342
|
+
*/
|
|
343
|
+
purgeCache?: boolean;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export type DnssecResult = {
|
|
347
|
+
/** Whether the DNS response was DNSSEC-validated (AD flag set). */
|
|
348
|
+
secure: boolean;
|
|
349
|
+
/** The DNS answer records from the response. */
|
|
350
|
+
answers: Array<{
|
|
351
|
+
name: string;
|
|
352
|
+
type: string;
|
|
353
|
+
ttl: number;
|
|
354
|
+
class: string;
|
|
355
|
+
flush: boolean;
|
|
356
|
+
data: unknown;
|
|
357
|
+
}>;
|
|
329
358
|
};
|
|
330
359
|
|
|
331
360
|
export type RecordWithTtl = {
|
|
@@ -647,13 +676,14 @@ declare class Tangerine extends Resolver {
|
|
|
647
676
|
|
|
648
677
|
/**
|
|
649
678
|
* Resolve DNS records of a specific type.
|
|
679
|
+
* When `options.dnssecSecure` is true, returns `DnssecResult` instead.
|
|
650
680
|
*/
|
|
651
681
|
resolve(
|
|
652
682
|
name: string,
|
|
653
683
|
rrtype?: DnsRecordType,
|
|
654
684
|
options?: ResolveOptions,
|
|
655
685
|
abortController?: AbortController
|
|
656
|
-
): Promise<unknown>;
|
|
686
|
+
): Promise<unknown | DnssecResult>;
|
|
657
687
|
}
|
|
658
688
|
|
|
659
689
|
export default Tangerine;
|
package/index.js
CHANGED
|
@@ -1141,13 +1141,14 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1141
1141
|
|
|
1142
1142
|
// <https://github.com/hildjj/dohdec/tree/main/pkg/dohdec>
|
|
1143
1143
|
|
|
1144
|
-
async #query(name, rrtype = 'A', ecsSubnet, abortController) {
|
|
1144
|
+
async #query(name, rrtype = 'A', ecsSubnet, abortController, dnssec) {
|
|
1145
1145
|
if (!dohdec) await pWaitFor(() => Boolean(dohdec));
|
|
1146
1146
|
debug('query', {
|
|
1147
1147
|
name,
|
|
1148
1148
|
nameToASCII: toASCII(name),
|
|
1149
1149
|
rrtype,
|
|
1150
1150
|
ecsSubnet,
|
|
1151
|
+
dnssec,
|
|
1151
1152
|
abortController
|
|
1152
1153
|
});
|
|
1153
1154
|
// <https://github.com/hildjj/dohdec/blob/43564118c40f2127af871bdb4d40f615409d4b9c/pkg/dohdec/lib/dnsUtils.js#L161>
|
|
@@ -1160,7 +1161,13 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1160
1161
|
// mirrors dns module behavior
|
|
1161
1162
|
name: toASCII(name),
|
|
1162
1163
|
// <https://github.com/mafintosh/dns-packet/pull/47#issuecomment-1435818437>
|
|
1163
|
-
ecsSubnet
|
|
1164
|
+
ecsSubnet,
|
|
1165
|
+
// When dnssec is true, set the AD flag in the query and the DO
|
|
1166
|
+
// (DNSSEC OK) flag in the EDNS0 OPT record so the upstream resolver
|
|
1167
|
+
// returns DNSSEC validation status via the AD flag in the response.
|
|
1168
|
+
// <https://datatracker.ietf.org/doc/html/rfc3225>
|
|
1169
|
+
// <https://datatracker.ietf.org/doc/html/rfc4035#section-3.2.1>
|
|
1170
|
+
dnssec
|
|
1164
1171
|
});
|
|
1165
1172
|
try {
|
|
1166
1173
|
// mirror the behavior as noted in built-in DNS
|
|
@@ -1824,6 +1831,12 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1824
1831
|
delete options.ecsSubnet;
|
|
1825
1832
|
}
|
|
1826
1833
|
|
|
1834
|
+
// dnssecSecure support: set the EDNS0 DO (DNSSEC OK) flag in the
|
|
1835
|
+
// outgoing DoH query so the upstream resolver returns the AD flag.
|
|
1836
|
+
// NOTE: do not delete options.dnssecSecure here — it is checked
|
|
1837
|
+
// after #query() to decide the return format.
|
|
1838
|
+
const dnssec = Boolean(options?.dnssecSecure);
|
|
1839
|
+
|
|
1827
1840
|
const key = (
|
|
1828
1841
|
ecsSubnet ? `${rrtype}:${ecsSubnet}:${name}` : `${rrtype}:${name}`
|
|
1829
1842
|
).toLowerCase();
|
|
@@ -1932,7 +1945,13 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1932
1945
|
|
|
1933
1946
|
try {
|
|
1934
1947
|
// setImmediate(() => this.cancel());
|
|
1935
|
-
result = await this.#query(
|
|
1948
|
+
result = await this.#query(
|
|
1949
|
+
name,
|
|
1950
|
+
rrtype,
|
|
1951
|
+
ecsSubnet,
|
|
1952
|
+
abortController,
|
|
1953
|
+
dnssec
|
|
1954
|
+
);
|
|
1936
1955
|
} finally {
|
|
1937
1956
|
if (mustReleaseAbortController) {
|
|
1938
1957
|
this.#releaseAbortController(abortController);
|
|
@@ -2028,6 +2047,22 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
2028
2047
|
if (result.answers.length === 0 && !options.noThrowOnNODATA)
|
|
2029
2048
|
throw this.constructor.createError(name, rrtype, dns.NODATA);
|
|
2030
2049
|
|
|
2050
|
+
//
|
|
2051
|
+
// RFC 7672 Section 2.2.2: Expose DNSSEC validation status (AD flag)
|
|
2052
|
+
// When options.dnssecSecure is true, return an object with the answers
|
|
2053
|
+
// and a boolean indicating whether the response was DNSSEC-validated.
|
|
2054
|
+
// The AD flag is set by the upstream DoH resolver (Cloudflare/Google)
|
|
2055
|
+
// when the zone is DNSSEC-signed and validation succeeds.
|
|
2056
|
+
// This allows callers (e.g. mx-connect DANE) to check if the MX host's
|
|
2057
|
+
// zone is signed before attempting TLSA lookups.
|
|
2058
|
+
//
|
|
2059
|
+
if (options?.dnssecSecure) {
|
|
2060
|
+
return {
|
|
2061
|
+
secure: Boolean(result.flag_ad),
|
|
2062
|
+
answers: result.answers
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2031
2066
|
// filter the answers for the same type
|
|
2032
2067
|
result.answers = result.answers.filter((answer) => answer.type === rrtype);
|
|
2033
2068
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tangerine",
|
|
3
3
|
"description": "Tangerine is the best Node.js drop-in replacement for dns.promises.Resolver using DNS over HTTPS (\"DoH\") via undici with built-in retries, timeouts, smart server rotation, AbortControllers, and caching support for multiple backends (with TTL and purge support).",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.2",
|
|
5
5
|
"author": "Forward Email (https://forwardemail.net)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/forwardemail/nodejs-dns-over-https-tangerine/issues"
|