tangerine 1.4.9 → 1.5.0
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 +43 -0
- package/index.js +79 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
* [`tangerine.reverse(ip[, abortController, purgeCache])`](#tangerinereverseip-abortcontroller-purgecache)
|
|
57
57
|
* [`tangerine.setDefaultResultOrder(order)`](#tangerinesetdefaultresultorderorder)
|
|
58
58
|
* [`tangerine.setServers(servers)`](#tangerinesetserversservers)
|
|
59
|
+
* [`tangerine.spoofPacket(hostname, rrtype, answers)`](#tangerinespoofpackethostname-rrtype-answers)
|
|
59
60
|
* [Options](#options)
|
|
60
61
|
* [Cache](#cache)
|
|
61
62
|
* [Compatibility](#compatibility)
|
|
@@ -331,6 +332,48 @@ This mirrors output from <https://github.com/rthalley/dnspython>.
|
|
|
331
332
|
|
|
332
333
|
### `tangerine.setServers(servers)`
|
|
333
334
|
|
|
335
|
+
### `tangerine.spoofPacket(hostname, rrtype, answers)`
|
|
336
|
+
|
|
337
|
+
This method is useful for writing tests to spoof DNS packets in-memory.
|
|
338
|
+
|
|
339
|
+
The `rrtype` must be either `"TXT"` or `"MX"`, and `answers` must be an Array of DNS resource record answers.
|
|
340
|
+
|
|
341
|
+
For example, if you want to spoof TXT and MX records:
|
|
342
|
+
|
|
343
|
+
```js
|
|
344
|
+
const Redis = require('ioredis-mock');
|
|
345
|
+
const Tangerine = require('tangerine');
|
|
346
|
+
const ip = require('ip');
|
|
347
|
+
|
|
348
|
+
const cache = new Redis();
|
|
349
|
+
const tangerine = new Tangerine({ cache });
|
|
350
|
+
|
|
351
|
+
const obj = {};
|
|
352
|
+
|
|
353
|
+
obj['txt:forwardmail.net'] = tangerine.spoofPacket('forwardmail.net', 'TXT', [
|
|
354
|
+
`v=spf1 ip4:${ip.address()} -all`
|
|
355
|
+
]);
|
|
356
|
+
|
|
357
|
+
obj['mx:forwardemail.net'] = tangerine.spoofPacket('forwardemail.net', 'MX', [
|
|
358
|
+
{ exchange: 'mx1.forwardemail.net', preference: 0 },
|
|
359
|
+
{ exchange: 'mx2.forwardemail.net', preference: 0 }
|
|
360
|
+
]);
|
|
361
|
+
|
|
362
|
+
await cache.mset(obj);
|
|
363
|
+
|
|
364
|
+
//
|
|
365
|
+
// NOTE: spoofed values are used below (this means no DNS query performed)
|
|
366
|
+
//
|
|
367
|
+
|
|
368
|
+
const txt = await tangerine.resolveTxt('forwardemail.net');
|
|
369
|
+
console.log('txt', txt);
|
|
370
|
+
|
|
371
|
+
const mx = await tangerine.resolveMx('forwardemail.net');
|
|
372
|
+
console.log('mx', mx);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Pull requests are welcome to add support for other `rrtype` values for this method.**
|
|
376
|
+
|
|
334
377
|
|
|
335
378
|
## Options
|
|
336
379
|
|
package/index.js
CHANGED
|
@@ -1428,6 +1428,74 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1428
1428
|
this.options.servers = new Set(servers);
|
|
1429
1429
|
}
|
|
1430
1430
|
|
|
1431
|
+
spoofPacket(name, rrtype, answers = []) {
|
|
1432
|
+
if (typeof name !== 'string') {
|
|
1433
|
+
const err = new TypeError('The "name" argument must be of type string.');
|
|
1434
|
+
err.code = 'ERR_INVALID_ARG_TYPE';
|
|
1435
|
+
throw err;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
if (typeof rrtype !== 'string') {
|
|
1439
|
+
const err = new TypeError(
|
|
1440
|
+
'The "rrtype" argument must be of type string.'
|
|
1441
|
+
);
|
|
1442
|
+
err.code = 'ERR_INVALID_ARG_TYPE';
|
|
1443
|
+
throw err;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
if (!this.constructor.TYPES.has(rrtype)) {
|
|
1447
|
+
const err = new TypeError("The argument 'rrtype' is invalid.");
|
|
1448
|
+
err.code = 'ERR_INVALID_ARG_VALUE';
|
|
1449
|
+
throw err;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
if (!Array.isArray(answers)) {
|
|
1453
|
+
const err = new TypeError("The argument 'answers' is invalid.");
|
|
1454
|
+
err.code = 'ERR_INVALID_ARG_VALUE';
|
|
1455
|
+
throw err;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
return {
|
|
1459
|
+
id: 0,
|
|
1460
|
+
type: 'response',
|
|
1461
|
+
flags: 384,
|
|
1462
|
+
flag_qr: true,
|
|
1463
|
+
opcode: 'QUERY',
|
|
1464
|
+
flag_aa: false,
|
|
1465
|
+
flag_tc: false,
|
|
1466
|
+
flag_rd: true,
|
|
1467
|
+
flag_ra: true,
|
|
1468
|
+
flag_z: false,
|
|
1469
|
+
flag_ad: false,
|
|
1470
|
+
flag_cd: false,
|
|
1471
|
+
rcode: 'NOERROR',
|
|
1472
|
+
questions: [{ name, type: rrtype, class: 'IN' }],
|
|
1473
|
+
answers: answers.map((answer) => ({
|
|
1474
|
+
name,
|
|
1475
|
+
type: rrtype,
|
|
1476
|
+
ttl: 300,
|
|
1477
|
+
class: 'IN',
|
|
1478
|
+
flush: false,
|
|
1479
|
+
data: rrtype === 'TXT' ? [answer] : answer
|
|
1480
|
+
})),
|
|
1481
|
+
authorities: [],
|
|
1482
|
+
additionals: [
|
|
1483
|
+
{
|
|
1484
|
+
name: '.',
|
|
1485
|
+
type: 'OPT',
|
|
1486
|
+
udpPayloadSize: 1232,
|
|
1487
|
+
extendedRcode: 0,
|
|
1488
|
+
ednsVersion: 0,
|
|
1489
|
+
flags: 0,
|
|
1490
|
+
flag_do: false,
|
|
1491
|
+
options: [Array]
|
|
1492
|
+
}
|
|
1493
|
+
],
|
|
1494
|
+
ttl: 300,
|
|
1495
|
+
expires: Date.now() + 10000
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1431
1499
|
// eslint-disable-next-line complexity
|
|
1432
1500
|
async resolve(name, rrtype = 'A', options = {}, abortController) {
|
|
1433
1501
|
if (typeof name !== 'string') {
|
|
@@ -1481,6 +1549,17 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1481
1549
|
// (this saves us from duplicating the same `...sort().filter(Number.isFinite)` logic)
|
|
1482
1550
|
//
|
|
1483
1551
|
data = await this.options.cache.get(key);
|
|
1552
|
+
//
|
|
1553
|
+
// if it's not an object then assume that
|
|
1554
|
+
// the cache implementation does not have JSON.parse built-in
|
|
1555
|
+
// and so we should try to parse it on our own (user-friendly)
|
|
1556
|
+
//
|
|
1557
|
+
if (typeof data === 'string') {
|
|
1558
|
+
try {
|
|
1559
|
+
data = JSON.parse(data);
|
|
1560
|
+
} catch {}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1484
1563
|
// safeguard in case cache pollution
|
|
1485
1564
|
if (data && typeof data === 'object') {
|
|
1486
1565
|
debug('cache retrieved', key);
|
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": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"author": "Forward Email (https://forwardemail.net)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/forwardemail/tangerine/issues"
|