@nictool/dns-resource-record 1.2.0 → 1.2.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ Notable changes to this project are documented in this file.
4
4
 
5
5
  #### Unreleased
6
6
 
7
+ ### [1.2.1] - 2024-03-10
8
+
9
+ - fix(nsec3param): fixed setHash fname typo
10
+ - feat(SVCB,HTTPS): add record support (#29)
11
+ - feat(OPENGPGPKEY): improved bindline parser, added test
12
+ - feat(throwHelp): far more useful error messages (#41)
13
+ - feat(A,MX,AAAA,CAA,IPSECKEY): added getCanonical
14
+
7
15
  ### [1.2.0] - 2024-03-07
8
16
 
9
17
  - feat(index): export typeMap (lookup table for type to id)
@@ -286,3 +294,4 @@ Notable changes to this project are documented in this file.
286
294
  [1.1.5]: https://github.com/NicTool/dns-resource-record/releases/tag/1.1.5
287
295
  [1.1.6]: https://github.com/NicTool/dns-resource-record/releases/tag/1.1.6
288
296
  [1.1.8]: https://github.com/NicTool/dns-resource-record/releases/tag/1.1.8
297
+ [1.2.1]: https://github.com/NicTool/dns-resource-record/releases/tag/1.2.1
package/README.md CHANGED
@@ -10,7 +10,7 @@ DNS resource record parser, validator, importer, and exporter.
10
10
  This module is used to:
11
11
 
12
12
  - validate well formedness and RFC compliance of DNS resource records
13
- - import RRs from and from the following formats:
13
+ - import RRs to and from the following formats:
14
14
 
15
15
  | **RR format** | **import** | **export** |
16
16
  | :------------------------------------------------------: | :----------------: | :----------------: |
@@ -196,6 +196,7 @@ PRs are welcome, especially PRs with tests.
196
196
  | **DNSKEY** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
197
197
  | **DS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
198
198
  | **HINFO** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
199
+ | **HTTPS** | | | | |
199
200
  | **IPSECKEY** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
200
201
  | **KEY** | | | | |
201
202
  | **LOC** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
@@ -215,6 +216,7 @@ PRs are welcome, especially PRs with tests.
215
216
  | **SPF** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
216
217
  | **SRV** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
217
218
  | **SSHFP** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
219
+ | **SVCB** | | | | |
218
220
  | **TLSA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
219
221
  | **TSIG** | | | | |
220
222
  | **TXT** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
package/index.js CHANGED
@@ -10,6 +10,7 @@ import DNAME from './rr/dname.js'
10
10
  import DNSKEY from './rr/dnskey.js'
11
11
  import DS from './rr/ds.js'
12
12
  import HINFO from './rr/hinfo.js'
13
+ import HTTPS from './rr/https.js'
13
14
  import IPSECKEY from './rr/ipseckey.js'
14
15
  import KEY from './rr/key.js'
15
16
  import LOC from './rr/loc.js'
@@ -29,6 +30,7 @@ import SOA from './rr/soa.js'
29
30
  import SPF from './rr/spf.js'
30
31
  import SRV from './rr/srv.js'
31
32
  import SSHFP from './rr/sshfp.js'
33
+ import SVCB from './rr/svcb.js'
32
34
  import TLSA from './rr/tlsa.js'
33
35
  import TSIG from './rr/tsig.js'
34
36
  import TXT from './rr/txt.js'
@@ -47,6 +49,7 @@ export {
47
49
  DNSKEY,
48
50
  DS,
49
51
  HINFO,
52
+ HTTPS,
50
53
  IPSECKEY,
51
54
  KEY,
52
55
  LOC,
@@ -66,6 +69,7 @@ export {
66
69
  SOA,
67
70
  SPF,
68
71
  SRV,
72
+ SVCB,
69
73
  TLSA,
70
74
  TSIG,
71
75
  TXT,
@@ -84,6 +88,7 @@ for (const c of [
84
88
  DNSKEY,
85
89
  DS,
86
90
  HINFO,
91
+ HTTPS,
87
92
  IPSECKEY,
88
93
  KEY,
89
94
  LOC,
@@ -103,6 +108,7 @@ for (const c of [
103
108
  SOA,
104
109
  SPF,
105
110
  SRV,
111
+ SVCB,
106
112
  TLSA,
107
113
  TSIG,
108
114
  TXT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nictool/dns-resource-record",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "DNS Resource Records",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/rr/a.js CHANGED
@@ -1,4 +1,4 @@
1
- import net from 'net'
1
+ import net from 'node:net'
2
2
 
3
3
  import RR from '../rr.js'
4
4
 
@@ -9,8 +9,8 @@ export default class A extends RR {
9
9
 
10
10
  /****** Resource record specific setters *******/
11
11
  setAddress(val) {
12
- if (!val) throw new Error('A: address is required')
13
- if (!net.isIPv4(val)) throw new Error('A address must be IPv4')
12
+ if (!val) this.throwHelp('A: address is required')
13
+ if (!net.isIPv4(val)) this.throwHelp('A address must be IPv4')
14
14
  this.set('address', val)
15
15
  }
16
16
 
@@ -30,6 +30,16 @@ export default class A extends RR {
30
30
  return 1
31
31
  }
32
32
 
33
+ getCanonical() {
34
+ return {
35
+ owner: 'host.example.com.',
36
+ class: 'IN',
37
+ ttl: 3600,
38
+ type: 'A',
39
+ address: '192.0.2.127',
40
+ }
41
+ }
42
+
33
43
  /****** IMPORTERS *******/
34
44
  fromTinydns(opts) {
35
45
  // +fqdn:ip:ttl:timestamp:lo
package/rr/aaaa.js CHANGED
@@ -1,4 +1,4 @@
1
- import net from 'net'
1
+ import net from 'node:net'
2
2
 
3
3
  import RR from '../rr.js'
4
4
  import * as TINYDNS from '../lib/tinydns.js'
@@ -10,8 +10,8 @@ export default class AAAA extends RR {
10
10
 
11
11
  /****** Resource record specific setters *******/
12
12
  setAddress(val) {
13
- if (!val) throw new Error('AAAA: address is required')
14
- if (!net.isIPv6(val)) throw new Error(`AAAA: address must be IPv6 (${val})`)
13
+ if (!val) this.throwHelp('AAAA: address is required')
14
+ if (!net.isIPv6(val)) this.throwHelp(`AAAA: address must be IPv6 (${val})`)
15
15
 
16
16
  this.set('address', this.expand(val.toLowerCase())) // lower case: RFC 5952
17
17
  }
@@ -36,6 +36,16 @@ export default class AAAA extends RR {
36
36
  return 28
37
37
  }
38
38
 
39
+ getCanonical() {
40
+ return {
41
+ owner: 'host.example.com.',
42
+ address: '2001:0db8:0020:000a:0000:0000:0000:0004',
43
+ class: 'IN',
44
+ ttl: 3600,
45
+ type: 'A',
46
+ }
47
+ }
48
+
39
49
  /****** IMPORTERS *******/
40
50
  fromTinydns(opts) {
41
51
  const str = opts.tinyline
@@ -45,7 +55,7 @@ export default class AAAA extends RR {
45
55
  case ':':
46
56
  // GENERIC => :fqdn:28:rdata:ttl:timestamp:lo
47
57
  ;[fqdn, n, rdata, ttl, ts, loc] = str.substring(1).split(':')
48
- if (n != 28) throw new Error('AAAA fromTinydns, invalid n')
58
+ if (n != 28) this.throwHelp('AAAA fromTinydns, invalid n')
49
59
  ip = TINYDNS.octalToHex(rdata)
50
60
  .match(/([0-9a-fA-F]{4})/g)
51
61
  .join(':')
package/rr/caa.js CHANGED
@@ -11,7 +11,7 @@ export default class CAA extends RR {
11
11
  this.is8bitInt('CAA', 'flags', val)
12
12
 
13
13
  if (![0, 128].includes(val)) {
14
- throw new Error(`CAA flags ${val} not recognized, ${this.citeRFC()}`)
14
+ this.throwHelp(`CAA flags ${val} not recognized`)
15
15
  }
16
16
 
17
17
  this.set('flags', val)
@@ -19,12 +19,12 @@ export default class CAA extends RR {
19
19
 
20
20
  setTag(val) {
21
21
  if (typeof val !== 'string' || val.length < 1 || /[^a-z0-9]/.test(val))
22
- throw new Error(
23
- `CAA tag must be a sequence of ASCII letters and numbers in lowercase, ${this.citeRFC()}`,
22
+ this.throwHelp(
23
+ `CAA tag must be a sequence of ASCII letters and numbers in lowercase`,
24
24
  )
25
25
 
26
26
  if (!['issue', 'issuewild', 'iodef'].includes(val)) {
27
- throw new Error(`CAA tag ${val} not recognized: ${this.citeRFC()}`)
27
+ this.throwHelp(`CAA tag ${val} not recognized`)
28
28
  }
29
29
  this.set('tag', val)
30
30
  }
@@ -35,16 +35,14 @@ export default class CAA extends RR {
35
35
  if (this.isQuoted(val)) {
36
36
  val = val.replace(/^["']|["']$/g, '') // strip quotes
37
37
  } else {
38
- // if (/\s/.test(val)) throw new Error(`CAA value may not have spaces unless quoted: RFC 8659`)
38
+ // if (/\s/.test(val)) this.throwHelp(`CAA value may not have spaces unless quoted`)
39
39
  }
40
40
 
41
41
  // check if val starts with one of iodefSchemes
42
42
  if (this.get('tag') === 'iodef') {
43
43
  const iodefSchemes = ['mailto:', 'http:', 'https:']
44
44
  if (!iodefSchemes.filter((s) => val.startsWith(s)).length) {
45
- throw new Error(
46
- `CAA value must have valid iodefScheme prefix, ${this.citeRFC()}`,
47
- )
45
+ this.throwHelp(`CAA value must have valid iodefScheme prefix`)
48
46
  }
49
47
  }
50
48
 
@@ -71,11 +69,23 @@ export default class CAA extends RR {
71
69
  return 257
72
70
  }
73
71
 
72
+ getCanonical() {
73
+ return {
74
+ owner: 'example.com.',
75
+ ttl: 3600,
76
+ class: 'IN',
77
+ type: 'CAA',
78
+ flags: 0,
79
+ tag: 'issue',
80
+ value: 'http://letsencrypt.org',
81
+ }
82
+ }
83
+
74
84
  /****** IMPORTERS *******/
75
85
  fromTinydns(opts) {
76
86
  // CAA via generic, :fqdn:n:rdata:ttl:timestamp:lo
77
87
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
78
- if (n != 257) throw new Error('CAA fromTinydns, invalid n')
88
+ if (n != 257) this.throwHelp('CAA fromTinydns, invalid n')
79
89
 
80
90
  const flags = TINYDNS.octalToUInt8(rdata.substring(0, 4))
81
91
  const taglen = TINYDNS.octalToUInt8(rdata.substring(4, 8))
@@ -101,7 +111,7 @@ export default class CAA extends RR {
101
111
  const fields = opts.bindline.match(
102
112
  /^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+(\w+)\s+("[^"]+"|[^\s]+?)\s*$/i,
103
113
  )
104
- if (!fields) throw new Error(`unable to parse: ${opts.bindline}`)
114
+ if (!fields) this.throwHelp(`unable to parse: ${opts.bindline}`)
105
115
 
106
116
  const [owner, ttl, c, type, flags, tag, value] = fields.slice(1)
107
117
  return new CAA({
package/rr/cname.js CHANGED
@@ -1,4 +1,4 @@
1
- import net from 'net'
1
+ import net from 'node:net'
2
2
 
3
3
  import RR from '../rr.js'
4
4
 
@@ -12,10 +12,10 @@ export default class CNAME extends RR {
12
12
  // A <domain-name> which specifies the canonical or primary
13
13
  // name for the owner. The owner name is an alias.
14
14
 
15
- if (!val) throw new Error('CNAME: cname is required')
15
+ if (!val) this.throwHelp('CNAME: cname is required')
16
16
 
17
17
  if (net.isIPv4(val) || net.isIPv6(val))
18
- throw new Error(`CNAME: cname must be a FQDN: RFC 2181`)
18
+ this.throwHelp(`CNAME: cname must be a FQDN: RFC 2181`)
19
19
 
20
20
  if (!this.isFullyQualified('CNAME', 'cname', val)) return
21
21
  if (!this.isValidHostname('CNAME', 'cname', val)) return
package/rr/dname.js CHANGED
@@ -1,4 +1,4 @@
1
- import net from 'net'
1
+ import net from 'node:net'
2
2
 
3
3
  import RR from '../rr.js'
4
4
  import * as TINYDNS from '../lib/tinydns.js'
@@ -10,10 +10,10 @@ export default class DNAME extends RR {
10
10
 
11
11
  /****** Resource record specific setters *******/
12
12
  setTarget(val) {
13
- if (!val) throw new Error('DNAME: target is required')
13
+ if (!val) this.throwHelp('DNAME: target is required')
14
14
 
15
15
  if (net.isIPv4(val) || net.isIPv6(val))
16
- throw new Error(`DNAME: target must be a domain name, ${this.citeRFC()}`)
16
+ this.throwHelp(`DNAME: target must be a domain name`)
17
17
 
18
18
  this.isFullyQualified('DNAME', 'target', val)
19
19
  this.isValidHostname('DNAME', 'target', val)
@@ -42,7 +42,7 @@ export default class DNAME extends RR {
42
42
  fromTinydns(opts) {
43
43
  // DNAME via generic, :fqdn:n:rdata:ttl:timestamp:lo
44
44
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
45
- if (n != 39) throw new Error('DNAME fromTinydns, invalid n')
45
+ if (n != 39) this.throwHelp('DNAME fromTinydns, invalid n')
46
46
 
47
47
  return new DNAME({
48
48
  type: 'DNAME',
package/rr/dnskey.js CHANGED
@@ -13,8 +13,7 @@ export default class DNSKEY extends RR {
13
13
  this.is16bitInt('DNSKEY', 'flags', val)
14
14
 
15
15
  // the possible values are: 0, 256, and 257; RFC 4034
16
- if (![0, 256, 257].includes(val))
17
- throw new Error(`DNSKEY: flags invalid, ${this.citeRFC()}`)
16
+ if (![0, 256, 257].includes(val)) this.throwHelp(`DNSKEY: flags invalid`)
18
17
 
19
18
  this.set('flags', val)
20
19
  }
@@ -24,8 +23,7 @@ export default class DNSKEY extends RR {
24
23
  this.is8bitInt('DNSKEY', 'protocol', val)
25
24
 
26
25
  // The Protocol Field MUST be represented as an unsigned decimal integer with a value of 3.
27
- if (![3].includes(val))
28
- throw new Error(`DNSKEY: protocol invalid, ${this.citeRFC()}`)
26
+ if (![3].includes(val)) this.throwHelp(`DNSKEY: protocol invalid`)
29
27
 
30
28
  this.set('protocol', val)
31
29
  }
@@ -37,16 +35,13 @@ export default class DNSKEY extends RR {
37
35
  // https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
38
36
  // 1=RSA/MD5, 2=DH, 3=DSA/SHA-1, 4=EC, 5=RSA/SHA-1
39
37
  if (![...Array(16).keys(), 253, 254].includes(val))
40
- console.error(
41
- `DNSKEY: algorithm (${val}) not recognized, ${this.citeRFC()}`,
42
- )
38
+ console.error(`DNSKEY: algorithm (${val}) not recognized`)
43
39
 
44
40
  this.set('algorithm', val)
45
41
  }
46
42
 
47
43
  setPublickey(val) {
48
- if (!val)
49
- throw new Error(`DNSKEY: publickey is required, ${this.citeRFC()}`)
44
+ if (!val) this.throwHelp(`DNSKEY: publickey is required`)
50
45
 
51
46
  this.set('publickey', val)
52
47
  }
@@ -74,7 +69,7 @@ export default class DNSKEY extends RR {
74
69
  const match = opts.bindline.match(
75
70
  /^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+\s*(.*?)\s*$/,
76
71
  )
77
- if (!match) throw new Error(`unable to parse DNSKEY: ${opts.bindline}`)
72
+ if (!match) this.throwHelp(`unable to parse DNSKEY: ${opts.bindline}`)
78
73
  const [owner, ttl, c, type, flags, protocol, algorithm, publickey] =
79
74
  match.slice(1)
80
75
 
@@ -92,7 +87,7 @@ export default class DNSKEY extends RR {
92
87
 
93
88
  fromTinydns(opts) {
94
89
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
95
- if (n != 48) throw new Error('DNSKEY fromTinydns, invalid n')
90
+ if (n != 48) this.throwHelp('DNSKEY fromTinydns, invalid n')
96
91
 
97
92
  const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
98
93
 
package/rr/ds.js CHANGED
@@ -10,9 +10,8 @@ export default class DS extends RR {
10
10
  /****** Resource record specific setters *******/
11
11
  setKeyTag(val) {
12
12
  // a 2 octet Key Tag field...in network byte order
13
- if (!val) throw new Error(`DS: key tag is required`)
14
- if (val.length > 2)
15
- throw new Error(`DS: key tag is too long, ${this.citeRFC()}`)
13
+ if (!val) this.throwHelp(`DS: key tag is required`)
14
+ if (val.length > 2) this.throwHelp(`DS: key tag is too long`)
16
15
 
17
16
  this.set('key tag', val)
18
17
  }
@@ -20,20 +19,19 @@ export default class DS extends RR {
20
19
  setAlgorithm(val) {
21
20
  // 1=RSA/MD5, 2=DH, 3=DSA/SHA-1, 4=EC, 5=RSA/SHA-1
22
21
  if (![1, 2, 3, 4, 5, 253, 254].includes(val))
23
- throw new Error(`DS: algorithm invalid, ${this.citeRFC()}`)
22
+ this.throwHelp(`DS: algorithm invalid`)
24
23
 
25
24
  this.set('algorithm', val)
26
25
  }
27
26
 
28
27
  setDigestType(val) {
29
- if (![1, 2].includes(val))
30
- throw new Error(`DS: digest type invalid, ${this.citeRFC()}`)
28
+ if (![1, 2].includes(val)) this.throwHelp(`DS: digest type invalid`)
31
29
 
32
30
  this.set('digest type', val)
33
31
  }
34
32
 
35
33
  setDigest(val) {
36
- if (!val) throw new Error(`DS: digest is required, ${this.citeRFC()}`)
34
+ if (!val) this.throwHelp(`DS: digest is required`)
37
35
 
38
36
  this.set('digest', val)
39
37
  }
@@ -74,7 +72,7 @@ export default class DS extends RR {
74
72
 
75
73
  fromTinydns(opts) {
76
74
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
77
- if (n != 43) throw new Error('DS fromTinydns, invalid n')
75
+ if (n != 43) this.throwHelp('DS fromTinydns, invalid n')
78
76
 
79
77
  const binRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
80
78
 
package/rr/hinfo.js CHANGED
@@ -9,12 +9,12 @@ export default class HINFO extends RR {
9
9
 
10
10
  /****** Resource record specific setters *******/
11
11
  setCpu(val) {
12
- if (val.length > 255) throw new Error('HINFO cpu cannot exceed 255 chars')
12
+ if (val.length > 255) this.throwHelp('HINFO cpu cannot exceed 255 chars')
13
13
  this.set('cpu', val.replace(/^["']|["']$/g, ''))
14
14
  }
15
15
 
16
16
  setOs(val) {
17
- if (val.length > 255) throw new Error('HINFO os cannot exceed 255 chars')
17
+ if (val.length > 255) this.throwHelp('HINFO os cannot exceed 255 chars')
18
18
  this.set('os', val.replace(/^["']|["']$/g, ''))
19
19
  }
20
20
 
@@ -44,7 +44,7 @@ export default class HINFO extends RR {
44
44
  const match = opts.bindline.match(
45
45
  /([^\s]+)\s+([0-9]+)\s+(IN)\s+(HINFO)\s+("[^"]+"|[^\s]+)\s+("[^"]+"|[^\s]+)/i,
46
46
  )
47
- if (!match) throw new Error(`unable to parse HINFO: ${opts.bindline}`)
47
+ if (!match) this.throwHelp(`unable to parse HINFO: ${opts.bindline}`)
48
48
  const [owner, ttl, c, type, cpu, os] = match.slice(1)
49
49
 
50
50
  return new HINFO({
package/rr/https.js ADDED
@@ -0,0 +1,62 @@
1
+ import RR from '../rr.js'
2
+
3
+ export default class HTTPS extends RR {
4
+ constructor(opts) {
5
+ super(opts)
6
+ }
7
+
8
+ /****** Resource record specific setters *******/
9
+ setPriority(val) {
10
+ this.is16bitInt('HTTPS', 'priority', val)
11
+
12
+ this.set('priority', val)
13
+ }
14
+
15
+ setTargetName(val) {
16
+ // this.isFullyQualified('HTTPS', 'target name', val)
17
+ // this.isValidHostname('HTTPS', 'target name', val)
18
+
19
+ // RFC 4034: letters in the DNS names are lower cased
20
+ this.set('target name', val.toLowerCase())
21
+ }
22
+
23
+ setParams(val) {
24
+ // if (!val) this.throwHelp(`HTTPS: params is required`)
25
+
26
+ this.set('params', val)
27
+ }
28
+
29
+ getDescription() {
30
+ return 'HTTP Semantics'
31
+ }
32
+
33
+ getRdataFields(arg) {
34
+ return ['priority', 'target name', 'params']
35
+ }
36
+
37
+ getRFCs() {
38
+ return [9460]
39
+ }
40
+
41
+ getTypeId() {
42
+ return 65
43
+ }
44
+
45
+ /****** IMPORTERS *******/
46
+
47
+ fromBind(opts) {
48
+ // test.example.com 3600 IN HTTPS Priority TargetName Params
49
+ const [owner, ttl, c, type, pri, fqdn] = opts.bindline.split(/\s+/)
50
+ return new HTTPS({
51
+ owner,
52
+ ttl: parseInt(ttl, 10),
53
+ class: c,
54
+ type,
55
+ priority: parseInt(pri, 10),
56
+ 'target name': fqdn,
57
+ params: opts.bindline.split(/\s+/).slice(6).join(' ').trim(),
58
+ })
59
+ }
60
+
61
+ /****** EXPORTERS *******/
62
+ }
package/rr/ipseckey.js CHANGED
@@ -1,4 +1,4 @@
1
- import net from 'net'
1
+ import net from 'node:net'
2
2
 
3
3
  import RR from '../rr.js'
4
4
 
@@ -20,23 +20,20 @@ export default class IPSECKEY extends RR {
20
20
  setGatewayType(val) {
21
21
  // 0 (none), 1 (4-byte IPv4), 2 (16-byte IPv6), 3 (wire encoded domain name)
22
22
  if (![0, 1, 2, 3].includes(val))
23
- throw new Error(`IPSECKEY: Gateway Type is invalid, ${this.citeRFC()}`)
23
+ this.throwHelp(`IPSECKEY: Gateway Type is invalid`)
24
24
 
25
25
  this.set('gateway type', val)
26
26
  }
27
27
 
28
28
  setAlgorithm(val) {
29
29
  // unsigned int, 1 octet, values: 1=DSA, 2=RSA
30
- if (![1, 2].includes(val))
31
- throw new Error(`IPSECKEY: Algorithm invalid, ${this.citeRFC()}`)
30
+ if (![1, 2].includes(val)) this.throwHelp(`IPSECKEY: Algorithm invalid`)
32
31
 
33
32
  this.set('algorithm', val)
34
33
  }
35
34
 
36
35
  setGateway(val) {
37
- const gwErr = new Error(
38
- `IPSECKEY: gateway invalid (${val}), ${this.citeRFC()}`,
39
- )
36
+ const gwErr = new Error(`IPSECKEY: gateway invalid (${val})`)
40
37
  switch (this.get('gateway type')) {
41
38
  case 0:
42
39
  if (val !== '.') throw gwErr
@@ -53,7 +50,7 @@ export default class IPSECKEY extends RR {
53
50
  }
54
51
 
55
52
  setPublickey(val) {
56
- // if (val) throw new Error(`IPSECKEY: publickey is optional, ${this.citeRFC()}`)
53
+ // if (val) this.throwHelp(`IPSECKEY: publickey is optional`)
57
54
 
58
55
  this.set('publickey', val)
59
56
  }
@@ -74,6 +71,20 @@ export default class IPSECKEY extends RR {
74
71
  return 45
75
72
  }
76
73
 
74
+ getCanonical() {
75
+ return {
76
+ owner: '38.2.0.192.in-addr.arpa.',
77
+ ttl: 7200,
78
+ class: 'IN',
79
+ type: 'IPSECKEY',
80
+ precedence: 10,
81
+ 'gateway type': 1,
82
+ algorithm: 2,
83
+ gateway: '192.0.2.38',
84
+ publickey: 'AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==',
85
+ }
86
+ }
87
+
77
88
  /****** IMPORTERS *******/
78
89
  fromBind(opts) {
79
90
  // FQDN TTL CLASS IPSECKEY Precedence GatewayType Algorithm Gateway PublicKey
@@ -94,7 +105,7 @@ export default class IPSECKEY extends RR {
94
105
 
95
106
  fromTinydns(opts) {
96
107
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
97
- if (n != 45) throw new Error('IPSECKEY fromTinydns, invalid n')
108
+ if (n != 45) this.throwHelp('IPSECKEY fromTinydns, invalid n')
98
109
 
99
110
  const precedence = TINYDNS.octalToUInt8(rdata.substring(0, 4))
100
111
  const gwType = TINYDNS.octalToUInt8(rdata.substring(4, 8))
package/rr/key.js CHANGED
@@ -24,13 +24,13 @@ export default class KEY extends RR {
24
24
  // 1 octet
25
25
  // 1=RSA/MD5, 2=DH, 3=DSA/SHA-1, 4=EC, 5=RSA/SHA-1
26
26
  if (![1, 2, 3, 4, 5, 253, 254].includes(val))
27
- throw new Error(`KEY: algorithm invalid, ${this.citeRFC()}`)
27
+ this.throwHelp(`KEY: algorithm invalid`)
28
28
 
29
29
  this.set('algorithm', val)
30
30
  }
31
31
 
32
32
  setPublickey(val) {
33
- if (!val) throw new Error(`KEY: publickey is required, ${this.citeRFC()}`)
33
+ if (!val) this.throwHelp(`KEY: publickey is required`)
34
34
 
35
35
  this.set('publickey', val)
36
36
  }
package/rr/loc.js CHANGED
@@ -20,7 +20,7 @@ export default class LOC extends RR {
20
20
 
21
21
  /****** Resource record specific setters *******/
22
22
  setAddress(val) {
23
- if (!val) throw new Error('LOC: address is required')
23
+ if (!val) this.throwHelp('LOC: address is required')
24
24
 
25
25
  /*
26
26
  ... LOC ( d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]]
@@ -59,7 +59,7 @@ export default class LOC extends RR {
59
59
  // put them all together
60
60
  const locRe = new RegExp(`^${dms}(N|S)\\s+${dms}(E|W)\\s+${alt}`, 'i')
61
61
  const r = string.match(locRe)
62
- if (!r) throw new Error('LOC address: invalid format, see RFC 1876')
62
+ if (!r) this.throwHelp('LOC address: invalid format, see RFC 1876')
63
63
 
64
64
  const loc = {
65
65
  latitude: {
@@ -89,7 +89,7 @@ export default class LOC extends RR {
89
89
  fromTinydns(opts) {
90
90
  // LOC via generic, :fqdn:n:rdata:ttl:timestamp:lo
91
91
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
92
- if (n != 29) throw new Error('LOC fromTinydns, invalid n')
92
+ if (n != 29) this.throwHelp('LOC fromTinydns, invalid n')
93
93
 
94
94
  // divide by 100 is to convert cm to meters
95
95
  const l = {
@@ -173,7 +173,7 @@ export default class LOC extends RR {
173
173
  hem = rawmsec >= REF.LATLON ? 'E' : 'W'
174
174
  break
175
175
  default:
176
- throw new Error('unknown or missing hemisphere')
176
+ this.throwHelp('unknown or missing hemisphere')
177
177
  }
178
178
 
179
179
  return `${deg} ${min} ${sec}${msec ? '.' + msec : ''} ${hem}`
package/rr/mx.js CHANGED
@@ -1,4 +1,4 @@
1
- import net from 'net'
1
+ import net from 'node:net'
2
2
 
3
3
  import RR from '../rr.js'
4
4
 
@@ -10,15 +10,16 @@ export default class MX extends RR {
10
10
  /****** Resource record specific setters *******/
11
11
  setPreference(val) {
12
12
  if (val === undefined) val = this?.default?.preference
13
+ if (val === undefined) this.throwHelp('MX: preference is required')
13
14
  this.is16bitInt('MX', 'preference', val)
14
15
  this.set('preference', val)
15
16
  }
16
17
 
17
18
  setExchange(val) {
18
- if (!val) throw new Error('MX: exchange is required')
19
+ if (!val) this.throwHelp('MX: exchange is required')
19
20
 
20
21
  if (net.isIPv4(val) || net.isIPv6(val))
21
- throw new Error(`MX: exchange must be a FQDN, ${this.citeRFC()}`)
22
+ this.throwHelp(`MX: exchange must be a FQDN`)
22
23
 
23
24
  this.isFullyQualified('MX', 'exchange', val)
24
25
  this.isValidHostname('MX', 'exchange', val)
@@ -43,6 +44,17 @@ export default class MX extends RR {
43
44
  return 15
44
45
  }
45
46
 
47
+ getCanonical() {
48
+ return {
49
+ owner: 'example.com.',
50
+ ttl: 43200,
51
+ class: 'IN',
52
+ type: 'MX',
53
+ preference: 0,
54
+ exchange: 'mail.example.com.',
55
+ }
56
+ }
57
+
46
58
  /****** IMPORTERS *******/
47
59
  fromTinydns(opts) {
48
60
  // @fqdn:ip:x:dist:ttl:timestamp:lo
package/rr/naptr.js CHANGED
@@ -41,7 +41,7 @@ export default class NAPTR extends RR {
41
41
 
42
42
  setFlags(val) {
43
43
  if (!['', 'S', 'A', 'U', 'P'].includes(val.toUpperCase()))
44
- throw new Error(`NAPTR flags are invalid, ${this.citeRFC()}`)
44
+ this.throwHelp(`NAPTR flags are invalid`)
45
45
 
46
46
  this.set('flags', val.toUpperCase())
47
47
  }
@@ -62,7 +62,7 @@ export default class NAPTR extends RR {
62
62
  fromTinydns(opts) {
63
63
  // NAPTR via generic, :fqdn:n:rdata:ttl:timestamp:lo
64
64
  const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
65
- if (n != 35) throw new Error('NAPTR fromTinydns, invalid n')
65
+ if (n != 35) this.throwHelp('NAPTR fromTinydns, invalid n')
66
66
 
67
67
  const binRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
68
68