Comparing version 1.4.6 to 1.4.7
113
index.js
@@ -23,3 +23,2 @@ const dns = require('node:dns'); | ||
const structuredClone = require('@ungap/structured-clone').default; | ||
const { Hosts } = require('hosts-parser'); | ||
const { getService } = require('port-numbers'); | ||
@@ -31,9 +30,2 @@ | ||
const hosts = new Hosts( | ||
hostile | ||
.get() | ||
.map((arr) => arr.join(' ')) | ||
.join('\n') | ||
); | ||
// dynamically import dohdec | ||
@@ -46,4 +38,28 @@ let dohdec; | ||
// dynamically import private-ip | ||
let isPrivateIP; | ||
// eslint-disable-next-line unicorn/prefer-top-level-await | ||
import('private-ip').then((obj) => { | ||
isPrivateIP = obj.default; | ||
}); | ||
const HOSTFILE = hostile | ||
.get(true) | ||
.map((s) => (Array.isArray(s) ? s.join(' ') : s)) | ||
.join('\n'); | ||
const HOSTS = []; | ||
const hosts = hostile.get(); | ||
for (const line of hosts) { | ||
const [ip, str] = line; | ||
const hosts = str.split(' '); | ||
HOSTS.push({ ip, hosts }); | ||
} | ||
// <https://github.com/szmarczak/cacheable-lookup/pull/76> | ||
class Tangerine extends dns.promises.Resolver { | ||
static HOSTFILE = HOSTFILE; | ||
static HOSTS = HOSTS; | ||
static isValidPort(port) { | ||
@@ -622,2 +638,12 @@ return Number.isSafeInteger(port) && port >= 0 && port <= 65535; | ||
if (name === '.') { | ||
const err = this.constructor.createError(name, '', dns.NOTFOUND); | ||
// remap and perform syscall | ||
err.syscall = 'getaddrinfo'; | ||
err.message = err.message.replace('query', 'getaddrinfo'); | ||
err.errno = -3008; // <-- ? | ||
// err.errno = -3007; | ||
throw err; | ||
} | ||
// purge cache support | ||
@@ -669,21 +695,18 @@ let purgeCache; | ||
// sorted in reverse to match behavior of lookup | ||
for (const rule of hosts._origin.reverse()) { | ||
if ( | ||
rule.hostname.toLowerCase() !== name.toLowerCase() && | ||
rule.ip !== name | ||
) | ||
continue; | ||
const lower = name.toLowerCase(); | ||
for (const rule of this.constructor.HOSTS) { | ||
if (rule.hosts.every((h) => h.toLowerCase() !== lower)) continue; | ||
const type = isIP(rule.ip); | ||
if (!resolve4 && type === 4) resolve4 = [rule.ip]; | ||
else if (!resolve6 && type === 6) resolve6 = [rule.ip]; | ||
if (resolve4 && resolve6) break; | ||
if (!resolve4 && type === 4) { | ||
if (!Array.isArray(resolve4)) resolve4 = [rule.ip]; | ||
else if (!resolve4.includes(rule.ip)) resolve4.push([rule.ip]); | ||
} else if (!resolve6 && type === 6) { | ||
if (!Array.isArray(resolve6)) resolve6 = [rule.ip]; | ||
else if (!resolve6.includes(rule.ip)) resolve6.push(rule.ip); | ||
} | ||
} | ||
// if no matches found for resolve4 and resolve6 and it was localhost | ||
// (this is a safeguard in case host file is missing these) | ||
if ( | ||
name.toLowerCase() === 'localhost' || | ||
name.toLowerCase() === 'localhost.' | ||
) { | ||
// safeguard (matches c-ares) | ||
if (lower === 'localhost' || lower === 'localhost.') { | ||
if (!resolve4) resolve4 = ['127.0.0.1']; | ||
@@ -741,21 +764,2 @@ if (!resolve6) resolve6 = ['::1']; | ||
/* | ||
// | ||
// NOTE: we probably should handle this differently (?) | ||
// (not sure what native nodejs dns module does for different errors - haven't checked yet) | ||
// | ||
if (errors.every((e) => e.code !== 'ENODATA')) { | ||
const err = this.constructor.combineErrors(errors); | ||
err.hostname = name; | ||
// remap and perform syscall | ||
err.syscall = 'getaddrinfo'; | ||
err.message = err.message.replace('query', 'getaddrinfo'); | ||
if (!err.code) | ||
err.code = errors.find((e) => e.code)?.code || dns.BADRESP; | ||
if (!err.errno) | ||
err.errno = errors.find((e) => e.errno)?.errno || undefined; | ||
throw err; | ||
} | ||
*/ | ||
// default node behavior seems to return IPv4 by default always regardless | ||
@@ -936,5 +940,22 @@ if (answers.length > 0) | ||
// edge case where localhost IP returns empty | ||
if (ip === '127.0.0.1' || ip === '::1') return []; | ||
// edge case where localhost IP returns matches | ||
if (!isPrivateIP) await pWaitFor(() => Boolean(isPrivateIP)); | ||
const answers = new Set(); | ||
let match = false; | ||
for (const rule of this.constructor.HOSTS) { | ||
if (rule.ip === ip) { | ||
match = true; | ||
for (const host of rule.hosts.slice(1)) { | ||
answers.add(host); | ||
} | ||
} | ||
} | ||
if (answers.size > 0 || match) return [...answers]; | ||
// NOTE: we can prob remove this (?) | ||
// if (ip === '::1' || ip === '127.0.0.1') return []; | ||
// reverse the IP address | ||
@@ -1440,3 +1461,3 @@ if (!dohdec) await pWaitFor(() => Boolean(dohdec)); | ||
// <https://github.com/c-ares/c-ares/blob/38b30bc922c21faa156939bde15ea35332c30e08/src/lib/ares_getaddrinfo.c#L829> | ||
if (name.startsWith('.') || name.includes('..')) | ||
if (name !== '.' && (name.startsWith('.') || name.includes('..'))) | ||
throw this.constructor.createError(name, rrtype, dns.BADNAME); | ||
@@ -1443,0 +1464,0 @@ |
{ | ||
"name": "tangerine", | ||
"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).", | ||
"version": "1.4.6", | ||
"version": "1.4.7", | ||
"author": "Forward Email (https://forwardemail.net)", | ||
@@ -19,3 +19,2 @@ "bugs": { | ||
"hostile": "^1.3.3", | ||
"hosts-parser": "^0.3.2", | ||
"ipaddr.js": "^2.0.1", | ||
@@ -27,2 +26,3 @@ "merge-options": "3.0.4", | ||
"port-numbers": "^6.0.1", | ||
"private-ip": "^3.0.0", | ||
"punycode": "^2.3.0", | ||
@@ -46,2 +46,3 @@ "semver": "^7.3.8" | ||
"ioredis-mock": "^8.2.6", | ||
"is-ci": "^3.0.1", | ||
"lint-staged": "^13.1.2", | ||
@@ -48,0 +49,0 @@ "lodash": "^4.17.21", |
@@ -61,2 +61,3 @@ <h1 align="center"> | ||
* [Cache](#cache) | ||
* [Compatibility](#compatibility) | ||
* [Debugging](#debugging) | ||
@@ -434,2 +435,11 @@ * [Benchmarks](#benchmarks) | ||
## Compatibility | ||
The only known compatibility issue is for locally running DNS servers that have wildcard DNS matching. | ||
If you are using `dnsmasq` with a wildcard match on "localhost" to "127.0.0.1", then the results may vary. For example, if your `dnsmasq` configuration has `address=/localhost/127.0.0.1`, then any match of `localhost` will resolve to `127.0.0.1`. This means that `dns.promises.lookup('foo.localhost')` will return `127.0.0.1` – however with :tangerine: Tangerine it will not return a value. | ||
The reason is because :tangerine: Tangerine only looks at either `/etc/hosts` (macOS/Linux) and `C:/Windows/System32/drivers/etc/hosts` (Windows). It does not lookup BIND, dnsmasq, or other configurations running locally. We would welcome a PR to resolve this (see `isCI` usage in test folder) – however it is a non-issue, as the workaround is to simply append a new line to the hostfile of `127.0.0.1 foo.localhost`. | ||
## Debugging | ||
@@ -436,0 +446,0 @@ |
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
120571
1669
0
693
28
+ Addedprivate-ip@^3.0.0
+ Added@chainsafe/is-ip@2.1.0(transitive)
+ Addedip-regex@5.0.0(transitive)
+ Addednetmask@2.0.2(transitive)
+ Addedprivate-ip@3.0.2(transitive)
- Removedhosts-parser@^0.3.2
- Removedhosts-parser@0.3.2(transitive)
- Removedlodash@3.10.1(transitive)