tangerine 1.0.2 → 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 +41 -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,6 +283,31 @@ 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 (?)
|
|
@@ -702,7 +727,7 @@ class Resolver extends dns.promises.Resolver {
|
|
|
702
727
|
//
|
|
703
728
|
async #request(
|
|
704
729
|
pkt,
|
|
705
|
-
|
|
730
|
+
server,
|
|
706
731
|
abortController,
|
|
707
732
|
requestTimeout = this.options.timeout
|
|
708
733
|
) {
|
|
@@ -711,7 +736,8 @@ class Resolver extends dns.promises.Resolver {
|
|
|
711
736
|
|
|
712
737
|
let localAddress;
|
|
713
738
|
let localPort;
|
|
714
|
-
|
|
739
|
+
let url = `${this.options.protocol}://${server}/dns-query`;
|
|
740
|
+
if (isIPv4(new URL(url).hostname)) {
|
|
715
741
|
localAddress = this.options.ipv4;
|
|
716
742
|
if (this.options.ipv4LocalPort) localPort = this.options.ipv4LocalPort;
|
|
717
743
|
} else {
|
|
@@ -728,7 +754,6 @@ class Resolver extends dns.promises.Resolver {
|
|
|
728
754
|
if (localPort) options.localPort = localPort;
|
|
729
755
|
|
|
730
756
|
// <https://github.com/hildjj/dohdec/blob/43564118c40f2127af871bdb4d40f615409d4b9c/pkg/dohdec/lib/doh.js#L117-L120>
|
|
731
|
-
let url = `${this.options.protocol}://${ip}/dns-query`;
|
|
732
757
|
if (this.options.undici.method === 'GET') {
|
|
733
758
|
if (!dohdec) await pWaitFor(() => Boolean(dohdec));
|
|
734
759
|
url += `?dns=${dohdec.DNSoverHTTPS.base64urlEncode(pkt)}`;
|
|
@@ -765,7 +790,8 @@ class Resolver extends dns.promises.Resolver {
|
|
|
765
790
|
let buffer;
|
|
766
791
|
const errors = [];
|
|
767
792
|
// NOTE: we would have used `p-map-series` but it did not support abort/break
|
|
768
|
-
|
|
793
|
+
const servers = [...this.options.servers];
|
|
794
|
+
for (const server of servers) {
|
|
769
795
|
const ipErrors = [];
|
|
770
796
|
for (let i = 0; i < this.options.tries; i++) {
|
|
771
797
|
try {
|
|
@@ -773,7 +799,7 @@ class Resolver extends dns.promises.Resolver {
|
|
|
773
799
|
// eslint-disable-next-line no-await-in-loop
|
|
774
800
|
const response = await this.#request(
|
|
775
801
|
pkt,
|
|
776
|
-
|
|
802
|
+
server,
|
|
777
803
|
abortController,
|
|
778
804
|
this.options.timeout * 2 ** i
|
|
779
805
|
);
|
|
@@ -828,7 +854,7 @@ class Resolver extends dns.promises.Resolver {
|
|
|
828
854
|
// break out if we had a response
|
|
829
855
|
if (buffer) break;
|
|
830
856
|
if (ipErrors.length > 0) {
|
|
831
|
-
// if the `
|
|
857
|
+
// if the `server` had all errors, then remove it and add to end
|
|
832
858
|
// (this ensures we don't keep retrying servers that keep timing out)
|
|
833
859
|
// (which improves upon default c-ares behavior)
|
|
834
860
|
if (this.options.servers.size > 1 && this.options.smartRotate) {
|
|
@@ -836,9 +862,9 @@ class Resolver extends dns.promises.Resolver {
|
|
|
836
862
|
new Error('Rotating DNS servers due to issues'),
|
|
837
863
|
...ipErrors
|
|
838
864
|
]);
|
|
839
|
-
this.options.logger.error(err, {
|
|
840
|
-
this.options.servers.delete(
|
|
841
|
-
this.options.servers.add(
|
|
865
|
+
this.options.logger.error(err, { server });
|
|
866
|
+
this.options.servers.delete(server);
|
|
867
|
+
this.options.servers.add(server);
|
|
842
868
|
}
|
|
843
869
|
|
|
844
870
|
errors.push(...ipErrors);
|
|
@@ -1028,17 +1054,18 @@ class Resolver extends dns.promises.Resolver {
|
|
|
1028
1054
|
}
|
|
1029
1055
|
|
|
1030
1056
|
setServers(servers) {
|
|
1031
|
-
if (
|
|
1032
|
-
!Array.isArray(servers) ||
|
|
1033
|
-
servers.length === 0 ||
|
|
1034
|
-
servers.some((s) => typeof s !== 'string' || s.trim() === '' || !isIP(s))
|
|
1035
|
-
) {
|
|
1057
|
+
if (!Array.isArray(servers) || servers.length === 0) {
|
|
1036
1058
|
const err = new TypeError(
|
|
1037
1059
|
'The "name" argument must be an instance of Array.'
|
|
1038
1060
|
);
|
|
1039
1061
|
err.code = 'ERR_INVALID_ARG_TYPE';
|
|
1040
1062
|
}
|
|
1041
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
|
+
|
|
1042
1069
|
// <https://github.com/nodejs/node/blob/9bbde3d7baef584f14569ef79f116e9d288c7aaa/lib/internal/dns/utils.js#L87-L95>
|
|
1043
1070
|
this.options.servers = new Set(servers);
|
|
1044
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"
|