tangerine 1.4.3 → 1.4.5

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.
Files changed (3) hide show
  1. package/README.md +55 -0
  2. package/index.js +174 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -51,6 +51,8 @@
51
51
  * [`tangerine.resolveSoa(hostname[, options, abortController]))`](#tangerineresolvesoahostname-options-abortcontroller)
52
52
  * [`tangerine.resolveSrv(hostname[, options, abortController]))`](#tangerineresolvesrvhostname-options-abortcontroller)
53
53
  * [`tangerine.resolveTxt(hostname[, options, abortController]))`](#tangerineresolvetxthostname-options-abortcontroller)
54
+ * [`tangerine.resolveCert(hostname, [, options, abortController]))`](#tangerineresolvecerthostname--options-abortcontroller)
55
+ * [`tangerine.resolveTlsa(hostname, [, options, abortController]))`](#tangerineresolvetlsahostname--options-abortcontroller)
54
56
  * [`tangerine.reverse(ip[, abortController, purgeCache])`](#tangerinereverseip-abortcontroller-purgecache)
55
57
  * [`tangerine.setDefaultResultOrder(order)`](#tangerinesetdefaultresultorderorder)
56
58
  * [`tangerine.setServers(servers)`](#tangerinesetserversservers)
@@ -230,6 +232,7 @@ tangerine.resolve('forwardemail.net').then(console.log);
230
232
  * If set to `true`, then the result will be re-queried and re-cached – see [Cache](#cache) documentation for more insight.
231
233
  * Instances of `new Tangerine()` are instances of `dns.promises.Resolver` via `class Tangerine extends dns.promises.Resolver { ... }` (namely for compatibility with projects such as [cacheable-lookup](https://github.com/szmarczak/cacheable-lookup)).
232
234
  * See the complete list of [Options](#options) below.
235
+ * Any `rrtype` from the list at <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4> is supported (unlike the native Node.js DNS module which only supports a limited set).
233
236
 
234
237
  ### `tangerine.cancel()`
235
238
 
@@ -269,6 +272,58 @@ Tangerine supports a new `ecsSubnet` property in the `options` Object argument.
269
272
 
270
273
  ### `tangerine.resolveTxt(hostname[, options, abortController]))`
271
274
 
275
+ ### `tangerine.resolveCert(hostname, [, options, abortController]))`
276
+
277
+ This function returns a Promise that resolves with an Array with parsed values from results:
278
+
279
+ ```js
280
+ [
281
+ {
282
+ algorithm: 0,
283
+ certificate: 'MIIEoTCCA4mgAwIBAgICAacwDQYJKoZIhvcNAQELBQAwgY0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNRDEOMAwGA1UEBwwFQm95ZHMxEzARBgNVBAoMCkRyYWplciBMTEMxIjAgBgNVBAMMGWludGVybWVkaWF0ZS5oZWFsdGhpdC5nb3YxKDAmBgkqhkiG9w0BCQEWGWludGVybWVkaWF0ZS5oZWFsdGhpdC5nb3YwHhcNMTgwOTI1MTgyNDIzWhcNMjgwOTIyMTgyNDIzWjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTUQxDjAMBgNVBAcMBUJveWRzMRMwEQYDVQQKDApEcmFqZXIgTExDMRkwFwYDVQQDDBBldHQuaGVhbHRoaXQuZ292MR8wHQYJKoZIhvcNAQkBFhBldHQuaGVhbHRoaXQuZ292MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaA2MIuaqpvP2Id85KIhUVA6zlj+CgZh/3prgJ1q4leP3T5F1tSSgrQ/WYTFglEwN7FJx4yJ324NaKncaMPDBIg3IUgC3Q5nrPUbIJAUgM5+67pXnGgt6s9bQelEsTdbyA/JlLC7Hsv184mqo0yrueC9NJEea4/yTV51G9S4jLjnKhr0XUTw0Fb/PFNL9ZwaEdFgQfUaE1maleazKGDyLLuEGvpXsRNs1Ju/kdHkOUVLf741Cq8qLlqOKN2v5jQkUdFUKHbYIF5KXt4ToV9mvxTaz6Mps1UbS+a73Xr+VqmBqmEQnXA5DZ7ucikzv9DLokDwtmPzhdqye2msgDpw0QIDAQABo4IBGjCCARYwCQYDVR0TBAIwADAbBgNVHREEFDASghBldHQuaGVhbHRoaXQuZ292MB0GA1UdDgQWBBQ6E22jc99mm+WraUj93IvQcw6JHDAfBgNVHSMEGDAWgBRfW20fzencvG+Attm1rcvQV+3rOTALBgNVHQ8EBAMCBaAwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDovL2NhLmRpcmVjdGNhLm9yZy9jcmwvaW50ZXJtZWRpYXRlLmhlYWx0aGl0Lmdvdi5jcmwwVAYIKwYBBQUHAQEESDBGMEQGCCsGAQUFBzAChjhodHRwOi8vY2EuZGlyZWN0Y2Eub3JnL2FpYS9pbnRlcm1lZGlhdGUuaGVhbHRoaXQuZ292LmRlcjANBgkqhkiG9w0BAQsFAAOCAQEAhCASLubdxWp+XzXO4a8zMgWOMpjft+ilIy2ROVKOKslbB7lKx0NR7chrTPxCmK+YTL2ttLaTpOniw/vTGrZgeFPyXzJCNtpnx8fFipPE18OAlKMc2nyy7RfUscf28UAEmFo2cEJfpsZjyynkBsTnQ5rQVNgM7TbXXfboxwWwhg4HnWIcmlTs2YM1a9v+idK6LSfX9y/Nvhf9pl0DQflc9ym4z/XCq87erCce+11kxH1+36N6rRqeiHVBYnoYIGMH690r4cgE8cW5B4eK7kaD3iCbmpChO0gZSa5Lex49WLXeFfM+ukd9y3AB00KMZcsUV5bCgwShH053ZQa+FMON8w==',
284
+ certificate_type: 'PKIX',
285
+ key_tag: 0,
286
+ name: 'ett.healthit.gov',
287
+ ttl: 19045,
288
+ },
289
+ ]
290
+ ```
291
+
292
+ This mirrors output from <https://github.com/rthalley/dnspython>.
293
+
294
+ ### `tangerine.resolveTlsa(hostname, [, options, abortController]))`
295
+
296
+ This method was added for DANE and TLSA support. See this [excellent article](https://www.mailhardener.com/kb/dane), [index.js](https://github.com/forwardemail/tangerine/blob/main/index.js), and <https://github.com/nodejs/node/issues/39569> for more insight.
297
+
298
+ This function returns a Promise that resolves with an Array with parsed values from results:
299
+
300
+ ```js
301
+ [
302
+ {
303
+ cert: Buffer @Uint8Array [
304
+ e1ae9c3d e848ece1 ba72e0d9 91ae4d0d 9ec547c6 bad1ddda b9d6beb0 a7e0e0d8
305
+ ],
306
+ mtype: 1,
307
+ name: 'proloprod.mail._dane.internet.nl',
308
+ selector: 1,
309
+ ttl: 622,
310
+ usage: 2,
311
+ },
312
+ {
313
+ cert: Buffer @Uint8Array [
314
+ d6fea64d 4e68caea b7cbb2e0 f905d7f3 ca3308b1 2fd88c5b 469f08ad 7e05c7c7
315
+ ],
316
+ mtype: 1,
317
+ name: 'proloprod.mail._dane.internet.nl',
318
+ selector: 1,
319
+ ttl: 622,
320
+ usage: 3,
321
+ },
322
+ ]
323
+ ```
324
+
325
+ This mirrors output from <https://github.com/rthalley/dnspython>.
326
+
272
327
  ### `tangerine.reverse(ip[, abortController, purgeCache])`
273
328
 
274
329
  ### `tangerine.setDefaultResultOrder(order)`
package/index.js CHANGED
@@ -38,6 +38,19 @@ class Tangerine extends dns.promises.Resolver {
38
38
  return Number.isSafeInteger(port) && port >= 0 && port <= 65535;
39
39
  }
40
40
 
41
+ static CTYPE_BY_VALUE = {
42
+ 1: 'PKIX',
43
+ 2: 'SPKI',
44
+ 3: 'PGP',
45
+ 4: 'IPKIX',
46
+ 5: 'ISPKI',
47
+ 6: 'IPGP',
48
+ 7: 'ACPKIX',
49
+ 8: 'IACPKIX',
50
+ 253: 'URI',
51
+ 254: 'OID'
52
+ };
53
+
41
54
  static getAddrConfigTypes() {
42
55
  const networkInterfaces = os.networkInterfaces();
43
56
  let hasIPv4 = false;
@@ -138,7 +151,7 @@ class Tangerine extends dns.promises.Resolver {
138
151
  dns.TIMEOUT
139
152
  ]);
140
153
 
141
- static TYPES = new Set([
154
+ static DNS_TYPES = new Set([
142
155
  'A',
143
156
  'AAAA',
144
157
  'CAA',
@@ -152,6 +165,99 @@ class Tangerine extends dns.promises.Resolver {
152
165
  'TXT'
153
166
  ]);
154
167
 
168
+ // <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4>
169
+ static TYPES = new Set([
170
+ 'A',
171
+ 'A6',
172
+ 'AAAA',
173
+ 'AFSDB',
174
+ 'AMTRELAY',
175
+ 'APL',
176
+ 'ATMA',
177
+ 'AVC',
178
+ 'AXFR',
179
+ 'CAA',
180
+ 'CDNSKEY',
181
+ 'CDS',
182
+ 'CERT',
183
+ 'CNAME',
184
+ 'CSYNC',
185
+ 'DHCID',
186
+ 'DLV',
187
+ 'DNAME',
188
+ 'DNSKEY',
189
+ 'DOA',
190
+ 'DS',
191
+ 'EID',
192
+ 'EUI48',
193
+ 'EUI64',
194
+ 'GID',
195
+ 'GPOS',
196
+ 'HINFO',
197
+ 'HIP',
198
+ 'HTTPS',
199
+ 'IPSECKEY',
200
+ 'ISDN',
201
+ 'IXFR',
202
+ 'KEY',
203
+ 'KX',
204
+ 'L32',
205
+ 'L64',
206
+ 'LOC',
207
+ 'LP',
208
+ 'MAILA',
209
+ 'MAILB',
210
+ 'MB',
211
+ 'MD',
212
+ 'MF',
213
+ 'MG',
214
+ 'MINFO',
215
+ 'MR',
216
+ 'MX',
217
+ 'NAPTR',
218
+ 'NID',
219
+ 'NIMLOC',
220
+ 'NINFO',
221
+ 'NS',
222
+ 'NSAP',
223
+ 'NSAP-PTR',
224
+ 'NSEC',
225
+ 'NSEC3',
226
+ 'NSEC3PARAM',
227
+ 'NULL',
228
+ 'NXT',
229
+ 'OPENPGPKEY',
230
+ 'OPT',
231
+ 'PTR',
232
+ 'PX',
233
+ 'RKEY',
234
+ 'RP',
235
+ 'RRSIG',
236
+ 'RT',
237
+ 'Reserved',
238
+ 'SIG',
239
+ 'SINK',
240
+ 'SMIMEA',
241
+ 'SOA',
242
+ 'SPF',
243
+ 'SRV',
244
+ 'SSHFP',
245
+ 'SVCB',
246
+ 'TA',
247
+ 'TALINK',
248
+ 'TKEY',
249
+ 'TLSA',
250
+ 'TSIG',
251
+ 'TXT',
252
+ 'UID',
253
+ 'UINFO',
254
+ 'UNSPEC',
255
+ 'URI',
256
+ 'WKS',
257
+ 'X25',
258
+ 'ZONEMD'
259
+ ]);
260
+
155
261
  static ANY_TYPES = [
156
262
  'A',
157
263
  'AAAA',
@@ -805,6 +911,15 @@ class Tangerine extends dns.promises.Resolver {
805
911
  return this.resolve(name, 'TXT', options, abortController);
806
912
  }
807
913
 
914
+ resolveCert(name, options, abortController) {
915
+ return this.resolve(name, 'CERT', options, abortController);
916
+ }
917
+
918
+ // NOTE: parse this properly according to spec (see below default case)
919
+ resolveTlsa(name, options, abortController) {
920
+ return this.resolve(name, 'TLSA', options, abortController);
921
+ }
922
+
808
923
  // 1:1 mapping with node's official dns.promises API
809
924
  // (this means it's a drop-in replacement for `dns`)
810
925
  // <https://github.com/nodejs/node/blob/9bbde3d7baef584f14569ef79f116e9d288c7aaa/lib/internal/dns/utils.js#L87-L95>
@@ -1386,7 +1501,7 @@ class Tangerine extends dns.promises.Resolver {
1386
1501
  // this supports both redis-based key/value/ttl and simple key/value implementations
1387
1502
  result.expires = Date.now() + Math.round(result.ttl * 1000);
1388
1503
  const args = [key, result, ...this.options.setCacheArgs(key, result)];
1389
- debug('setting cache', [key, result, ...args]);
1504
+ debug('setting cache', { args });
1390
1505
  await this.options.cache.set(...args);
1391
1506
  }
1392
1507
 
@@ -1572,8 +1687,64 @@ class Tangerine extends dns.promises.Resolver {
1572
1687
  });
1573
1688
  }
1574
1689
 
1690
+ case 'CERT': {
1691
+ // CERT records `tangerine.resolveCert`
1692
+ // <https://github.com/jpnarkinsky/tangerine/commit/5f70954875aa93ef4acf076172d7540298b0a16b>
1693
+ // <https://www.rfc-editor.org/rfc/rfc4398.html>
1694
+ return result.answers.map((answer) => {
1695
+ if (!Buffer.isBuffer(answer.data))
1696
+ throw new Error('Buffer was not available');
1697
+
1698
+ try {
1699
+ // <https://github.com/rthalley/dnspython/blob/98b12e9e43847dac615bb690355d2fabaff969d2/dns/rdtypes/ANY/CERT.py#L69>
1700
+ const obj = {
1701
+ name: answer.name,
1702
+ ttl: answer.ttl,
1703
+ certificate_type: answer.data.subarray(0, 2).readUInt16BE(),
1704
+ key_tag: answer.data.subarray(2, 4).readUInt16BE(),
1705
+ algorithm: answer.data.subarray(4, 5).readUInt8(),
1706
+ certificate: answer.data.subarray(5).toString('base64')
1707
+ };
1708
+ obj.certificate_type = this.constructor.CTYPE_BY_VALUE[
1709
+ obj.certificate_type
1710
+ ]
1711
+ ? this.constructor.CTYPE_BY_VALUE[obj.certificate_type]
1712
+ : obj.certificate_type.toString();
1713
+ return obj;
1714
+ } catch (err) {
1715
+ console.error(err);
1716
+ throw err;
1717
+ }
1718
+ });
1719
+ }
1720
+
1721
+ case 'TLSA': {
1722
+ // if it returns answers with `type: TLSA` then recursively lookup
1723
+ // 3 1 1 D6FEA64D4E68CAEAB7CBB2E0F905D7F3CA3308B12FD88C5B469F08AD 7E05C7C7
1724
+ return result.answers.map((answer) => {
1725
+ if (!Buffer.isBuffer(answer.data))
1726
+ throw new Error('Buffer was not available');
1727
+
1728
+ // <https://www.mailhardener.com/kb/dane>
1729
+ return {
1730
+ name: answer.name,
1731
+ ttl: answer.ttl,
1732
+ // <https://github.com/rthalley/dnspython/blob/98b12e9e43847dac615bb690355d2fabaff969d2/dns/rdtypes/tlsabase.py#L35>
1733
+ usage: answer.data.subarray(0, 1).readUInt8(),
1734
+ selector: answer.data.subarray(1, 2).readUInt8(),
1735
+ mtype: answer.data.subarray(2, 3).readUInt8(),
1736
+ cert: answer.data.subarray(3)
1737
+ };
1738
+ });
1739
+ }
1740
+
1575
1741
  default: {
1576
- throw new Error(`Unknown type of ${rrtype}`);
1742
+ this.options.logger.error(
1743
+ new Error(
1744
+ `Submit a PR at <https://github.com/forwardemail/tangerine> with proper parsing for ${rrtype} records. You can reference <https://github.com/rthalley/dnspython/tree/master/dns/rdtypes/ANY> for inspiration.`
1745
+ )
1746
+ );
1747
+ return result.answers;
1577
1748
  }
1578
1749
  }
1579
1750
  }
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.3",
4
+ "version": "1.4.5",
5
5
  "author": "Forward Email (https://forwardemail.net)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/forwardemail/tangerine/issues"