tangerine 1.0.1 → 1.0.3
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 +1 -1
- package/index.js +43 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -255,7 +255,7 @@ Similar to the `options` argument from `new dns.promises.Resolver(options)` invo
|
|
|
255
255
|
| ------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
256
256
|
| `timeout` | `Number` | `5000` | Number of milliseconds for requests to timeout. |
|
|
257
257
|
| `tries` | `Number` | `4` | Number of tries per `server` in `servers` to attempt. |
|
|
258
|
-
| `servers` | `Set`
|
|
258
|
+
| `servers` | `Set` or `Array` | `new Set(['1.1.1.1', '1.0.0.1'])` | A Set or Array of [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6) formatted addresses for DNS queries (matches default Node.js dns module behavior). Duplicates will be removed as this is converted to a `Set` internally. Defaults to Cloudflare's of `1.1.1.1` and `1.0.0.1`. If an `Array` is passed, then it will be converted to a `Set`. |
|
|
259
259
|
| `undici` | `Object` | Defaults to an Object with `undici.method` and `undici.headers` properties and values below | Default options to pass to [undici](https://github.com/nodejs/undici). |
|
|
260
260
|
| `undici.method` | `String` | Defaults to `"GET"` (must be either `"GET"` or `"POST"`). | Default HTTP method to use for DNS over HTTP ("DoH") requests. |
|
|
261
261
|
| `undici.headers` | `Object` | Defaults to `{ 'content-type': 'application/dns-message', 'user-agent': pkg.name + "/" + pkg.version, accept: 'application/dns-message' }`. | Default HTTP headers to use for DNS over HTTP ("DoH") requests. |
|
package/index.js
CHANGED
|
@@ -283,9 +283,36 @@ class Resolver extends dns.promises.Resolver {
|
|
|
283
283
|
options
|
|
284
284
|
);
|
|
285
285
|
|
|
286
|
+
// timeout must be >= 0
|
|
287
|
+
if (!Number.isFinite(this.options.timeout) || this.options.timeout < 0)
|
|
288
|
+
throw new Error('Timeout must be >= 0');
|
|
289
|
+
|
|
290
|
+
// tries must be >= 1
|
|
291
|
+
if (!Number.isFinite(this.options.tries) || this.options.tries < 1)
|
|
292
|
+
throw new Error('Tries must be >= 1');
|
|
293
|
+
|
|
294
|
+
// perform validation by re-using `setServers` method
|
|
295
|
+
this.setServers([...this.options.servers]);
|
|
296
|
+
|
|
297
|
+
if (
|
|
298
|
+
!(this.options.servers instanceof Set) ||
|
|
299
|
+
this.options.servers.size === 0
|
|
300
|
+
)
|
|
301
|
+
throw new Error(
|
|
302
|
+
'Servers must be an Array or Set with at least one server'
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
if (!['http', 'https'].includes(this.options.protocol))
|
|
306
|
+
throw new Error('Protocol must be http or https');
|
|
307
|
+
|
|
308
|
+
if (!['verbatim', 'ipv4first'].includes(this.options.dnsOrder))
|
|
309
|
+
throw new Error('DNS order must be either verbatim or ipv4first');
|
|
310
|
+
|
|
286
311
|
// if `cache: false` then caching is disabled
|
|
287
312
|
// but note that this doesn't disable `got` dnsCache which is separate
|
|
288
313
|
// so to turn that off, you need to supply `dnsCache: undefined` in `got` object (?)
|
|
314
|
+
if (this.options.cache === true) this.options.cache = new Map();
|
|
315
|
+
|
|
289
316
|
if (this.options.cache instanceof Map) {
|
|
290
317
|
// each of the types have their own Keyv with prefix
|
|
291
318
|
for (const type of this.constructor.TYPES) {
|
|
@@ -700,7 +727,7 @@ class Resolver extends dns.promises.Resolver {
|
|
|
700
727
|
//
|
|
701
728
|
async #request(
|
|
702
729
|
pkt,
|
|
703
|
-
|
|
730
|
+
server,
|
|
704
731
|
abortController,
|
|
705
732
|
requestTimeout = this.options.timeout
|
|
706
733
|
) {
|
|
@@ -709,7 +736,8 @@ class Resolver extends dns.promises.Resolver {
|
|
|
709
736
|
|
|
710
737
|
let localAddress;
|
|
711
738
|
let localPort;
|
|
712
|
-
|
|
739
|
+
let url = `${this.options.protocol}://${server}/dns-query`;
|
|
740
|
+
if (isIPv4(new URL(url).hostname)) {
|
|
713
741
|
localAddress = this.options.ipv4;
|
|
714
742
|
if (this.options.ipv4LocalPort) localPort = this.options.ipv4LocalPort;
|
|
715
743
|
} else {
|
|
@@ -726,7 +754,6 @@ class Resolver extends dns.promises.Resolver {
|
|
|
726
754
|
if (localPort) options.localPort = localPort;
|
|
727
755
|
|
|
728
756
|
// <https://github.com/hildjj/dohdec/blob/43564118c40f2127af871bdb4d40f615409d4b9c/pkg/dohdec/lib/doh.js#L117-L120>
|
|
729
|
-
let url = `${this.options.protocol}://${ip}/dns-query`;
|
|
730
757
|
if (this.options.undici.method === 'GET') {
|
|
731
758
|
if (!dohdec) await pWaitFor(() => Boolean(dohdec));
|
|
732
759
|
url += `?dns=${dohdec.DNSoverHTTPS.base64urlEncode(pkt)}`;
|
|
@@ -763,7 +790,8 @@ class Resolver extends dns.promises.Resolver {
|
|
|
763
790
|
let buffer;
|
|
764
791
|
const errors = [];
|
|
765
792
|
// NOTE: we would have used `p-map-series` but it did not support abort/break
|
|
766
|
-
|
|
793
|
+
const servers = [...this.options.servers];
|
|
794
|
+
for (const server of servers) {
|
|
767
795
|
const ipErrors = [];
|
|
768
796
|
for (let i = 0; i < this.options.tries; i++) {
|
|
769
797
|
try {
|
|
@@ -771,7 +799,7 @@ class Resolver extends dns.promises.Resolver {
|
|
|
771
799
|
// eslint-disable-next-line no-await-in-loop
|
|
772
800
|
const response = await this.#request(
|
|
773
801
|
pkt,
|
|
774
|
-
|
|
802
|
+
server,
|
|
775
803
|
abortController,
|
|
776
804
|
this.options.timeout * 2 ** i
|
|
777
805
|
);
|
|
@@ -826,7 +854,7 @@ class Resolver extends dns.promises.Resolver {
|
|
|
826
854
|
// break out if we had a response
|
|
827
855
|
if (buffer) break;
|
|
828
856
|
if (ipErrors.length > 0) {
|
|
829
|
-
// if the `
|
|
857
|
+
// if the `server` had all errors, then remove it and add to end
|
|
830
858
|
// (this ensures we don't keep retrying servers that keep timing out)
|
|
831
859
|
// (which improves upon default c-ares behavior)
|
|
832
860
|
if (this.options.servers.size > 1 && this.options.smartRotate) {
|
|
@@ -834,9 +862,9 @@ class Resolver extends dns.promises.Resolver {
|
|
|
834
862
|
new Error('Rotating DNS servers due to issues'),
|
|
835
863
|
...ipErrors
|
|
836
864
|
]);
|
|
837
|
-
this.options.logger.error(err, {
|
|
838
|
-
this.options.servers.delete(
|
|
839
|
-
this.options.servers.add(
|
|
865
|
+
this.options.logger.error(err, { server });
|
|
866
|
+
this.options.servers.delete(server);
|
|
867
|
+
this.options.servers.add(server);
|
|
840
868
|
}
|
|
841
869
|
|
|
842
870
|
errors.push(...ipErrors);
|
|
@@ -1026,17 +1054,18 @@ class Resolver extends dns.promises.Resolver {
|
|
|
1026
1054
|
}
|
|
1027
1055
|
|
|
1028
1056
|
setServers(servers) {
|
|
1029
|
-
if (
|
|
1030
|
-
!Array.isArray(servers) ||
|
|
1031
|
-
servers.length === 0 ||
|
|
1032
|
-
servers.some((s) => typeof s !== 'string' || s.trim() === '' || !isIP(s))
|
|
1033
|
-
) {
|
|
1057
|
+
if (!Array.isArray(servers) || servers.length === 0) {
|
|
1034
1058
|
const err = new TypeError(
|
|
1035
1059
|
'The "name" argument must be an instance of Array.'
|
|
1036
1060
|
);
|
|
1037
1061
|
err.code = 'ERR_INVALID_ARG_TYPE';
|
|
1038
1062
|
}
|
|
1039
1063
|
|
|
1064
|
+
//
|
|
1065
|
+
// TODO: every address must be ipv4 or ipv6 (use `new URL` to parse and check)
|
|
1066
|
+
// servers [ string ] - array of RFC 5952 formatted addresses
|
|
1067
|
+
//
|
|
1068
|
+
|
|
1040
1069
|
// <https://github.com/nodejs/node/blob/9bbde3d7baef584f14569ef79f116e9d288c7aaa/lib/internal/dns/utils.js#L87-L95>
|
|
1041
1070
|
this.options.servers = new Set(servers);
|
|
1042
1071
|
}
|
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 via Keyv.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"author": "Forward Email (https://forwardemail.net)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/forwardemail/tangerine/issues"
|