@nictool/dns-resource-record 1.4.0 → 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/CHANGELOG.md CHANGED
@@ -4,9 +4,24 @@ Notable changes to this project are documented in this file.
4
4
 
5
5
  #### Unreleased
6
6
 
7
- ### [1.4.0] - 2026-03-21
7
+ ### [1.5.0] - 2026-03-22
8
+
9
+ - feat(RRSIG, SIG, TSIG): added toBind, toTinydns, fromTinydns
10
+ - feat(TSIG): added missing setters
11
+ - feat(fromTinydns): added to APL, DHCID, HIP, KX, RRSIG, RP, WKS
12
+ - feat(CERT): validate mnemonic values
13
+ - fix: hardened polynomial regexes #56
14
+ - fix(NAPTR): replace polynomial regexp
15
+ - test: coverage is at 98% 🎉
16
+ - test(NSEC3PARAM): added test cases, better binary parser, better salt
17
+ - test(AAAA, APL, CERT, KEY, NAPTR, NSEC3, NSEC3PARAM, RRSIG, SIG, SSHFP, TSIG, URL): added test coverage
18
+ - doc(README): updated RR table completion status
19
+ - style(es6): use ?. and ?? operators (nullish coalescing) #57
20
+ - style(es6): use argument expansion / destructured parameters
21
+ - style(es6): substring -> slice #55
22
+ - style(es6): use more trim() #55
8
23
 
9
- #### Changed
24
+ ### [1.4.0] - 2026-03-21
10
25
 
11
26
  - feat(toBind): added to nsec3param, rrsig
12
27
  - feat(toTinydns): added to key, nsec3param, nxt, openpgpkey
@@ -350,3 +365,4 @@ Notable changes to this project are documented in this file.
350
365
  [1.3.0]: https://github.com/NicTool/dns-resource-record/releases/tag/v1.3.0
351
366
  [1.3.1]: https://github.com/NicTool/dns-resource-record/releases/tag/v1.3.1
352
367
  [1.4.0]: https://github.com/NicTool/dns-resource-record/releases/tag/v1.4.0
368
+ [1.5.0]: https://github.com/NicTool/dns-resource-record/releases/tag/v1.5.0
package/README.md CHANGED
@@ -19,6 +19,7 @@ This module is used to:
19
19
  | **[Tinydns](https://cr.yp.to/djbdns/tinydns-data.html)** | :white_check_mark: | :white_check_mark: |
20
20
  | **MaraDNS** | | :white_check_mark: |
21
21
  | **JS** | :white_check_mark: | :white_check_mark: |
22
+ | **PowerDNS** | | |
22
23
 
23
24
  This package intends to import and export RFC compliant DNS resource records. Please [raise an issue](https://github.com/NicTool/dns-resource-record/issues) if a valid resource record fails to pass or an invalid resource record passes.
24
25
 
@@ -189,44 +190,44 @@ PRs are welcome, especially PRs with tests.
189
190
  | :------------: | :----------------: | :----------------: | :----------------: | :----------------: |
190
191
  | **A** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
191
192
  | **AAAA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
192
- | **APL** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
193
+ | **APL** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
193
194
  | **CAA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
194
- | **CERT** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
195
+ | **CERT** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
195
196
  | **CNAME** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
196
- | **DHCID** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
197
+ | **DHCID** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
197
198
  | **DNAME** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
198
199
  | **DNSKEY** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
199
200
  | **DS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
200
201
  | **HINFO** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
201
- | **HIP** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
202
- | **HTTPS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
202
+ | **HIP** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
203
+ | **HTTPS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
203
204
  | **IPSECKEY** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
204
- | **KEY** | | | | |
205
- | **KX** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
205
+ | **KEY** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
206
+ | **KX** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
206
207
  | **LOC** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
207
208
  | **MX** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
208
209
  | **NAPTR** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
209
210
  | **NS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
210
- | **NSEC** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
211
- | **NSEC3** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
212
- | **NSEC3PARAM** | | | | |
213
- | **NXT** | | | | |
214
- | **OPENPGPKEY** | | | | |
211
+ | **NSEC** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
212
+ | **NSEC3** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
213
+ | **NSEC3PARAM** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
214
+ | **NXT** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
215
+ | **OPENPGPKEY** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
215
216
  | **PTR** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
216
- | **RP** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
217
- | **RRSIG** | | | | |
218
- | **SIG** | | | | |
219
- | **SMIMEA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
217
+ | **RP** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
218
+ | **RRSIG** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
219
+ | **SIG** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
220
+ | **SMIMEA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
220
221
  | **SOA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
221
222
  | **SPF** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
222
223
  | **SRV** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
223
224
  | **SSHFP** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
224
- | **SVCB** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
225
+ | **SVCB** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
225
226
  | **TLSA** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
226
- | **TSIG** | | | | |
227
+ | **TSIG** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
227
228
  | **TXT** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
228
229
  | **URI** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
229
- | **WKS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
230
+ | **WKS** | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
230
231
 
231
232
  ## TIPS
232
233
 
package/lib/tinydns.js CHANGED
@@ -33,7 +33,7 @@ export function unescapeOctal(str) {
33
33
 
34
34
  export function octalToChar(str) {
35
35
  // relace instances of \NNN with ASCII
36
- return str.replace(octalRe, (o) => String.fromCharCode(parseInt(o.substring(1), 8)))
36
+ return str.replace(octalRe, (o) => String.fromCharCode(parseInt(o.slice(1), 8)))
37
37
  }
38
38
 
39
39
  export function octalToHex(str) {
@@ -41,29 +41,29 @@ export function octalToHex(str) {
41
41
  return str.replace(octalRe, (o) => {
42
42
  // parseInt(n, 8) -> from octal to decimal
43
43
  // .toString(16) -> decimal to hex
44
- return parseInt(o.substring(1), 8).toString(16).padStart(2, 0)
44
+ return parseInt(o.slice(1), 8).toString(16).padStart(2, 0)
45
45
  })
46
46
  }
47
47
 
48
48
  export function octalToUInt8(str) {
49
49
  const b = Buffer.alloc(1)
50
- b.writeUInt8(parseInt(str.substring(1, 4), 8), 0)
50
+ b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
51
51
  return b.readUInt8()
52
52
  }
53
53
 
54
54
  export function octalToUInt16(str) {
55
55
  const b = Buffer.alloc(2)
56
- b.writeUInt8(parseInt(str.substring(1, 4), 8), 0)
57
- b.writeUInt8(parseInt(str.substring(5, 8), 8), 1)
56
+ b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
57
+ b.writeUInt8(parseInt(str.slice(5, 8), 8), 1)
58
58
  return b.readUInt16BE()
59
59
  }
60
60
 
61
61
  export function octalToUInt32(str) {
62
62
  const b = Buffer.alloc(4)
63
- b.writeUInt8(parseInt(str.substring(1, 4), 8), 0)
64
- b.writeUInt8(parseInt(str.substring(5, 8), 8), 1)
65
- b.writeUInt8(parseInt(str.substring(9, 12), 8), 2)
66
- b.writeUInt8(parseInt(str.substring(13, 16), 8), 3)
63
+ b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
64
+ b.writeUInt8(parseInt(str.slice(5, 8), 8), 1)
65
+ b.writeUInt8(parseInt(str.slice(9, 12), 8), 2)
66
+ b.writeUInt8(parseInt(str.slice(13, 16), 8), 3)
67
67
  return b.readUInt32BE()
68
68
  }
69
69
 
@@ -99,7 +99,7 @@ export function packDomainName(fqdn) {
99
99
  // RFC 1035, 3.3 Standard RRs
100
100
  // The standard wire format for DNS names. (1 octet length + octets)
101
101
  let packed = ''
102
- fqdn.split('.').map((label) => {
102
+ fqdn.split('.').forEach((label) => {
103
103
  if (label === undefined || !label.length) return
104
104
 
105
105
  const len = Buffer.alloc(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nictool/dns-resource-record",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "DNS Resource Records",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -12,7 +12,7 @@
12
12
  "type": "module",
13
13
  "scripts": {
14
14
  "format:check": "npm run prettier; npm run lint",
15
- "format": "npm run prettier -- --write && npm run lint --fix",
15
+ "format": "npm run prettier -- --write && npm run lint:fix",
16
16
  "lint": "npx eslint **/*.js",
17
17
  "lint:fix": "npm run lint -- --fix",
18
18
  "prettier": "npx prettier --ignore-path .gitignore --check .",
package/rr/a.js CHANGED
@@ -39,9 +39,9 @@ export default class A extends RR {
39
39
  }
40
40
 
41
41
  /****** IMPORTERS *******/
42
- fromTinydns(opts) {
42
+ fromTinydns({ tinyline }) {
43
43
  // +fqdn:ip:ttl:timestamp:lo
44
- const [owner, ip, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
44
+ const [owner, ip, ttl, ts, loc] = tinyline.slice(1).split(':')
45
45
 
46
46
  return new A({
47
47
  owner: this.fullyQualify(owner),
@@ -49,13 +49,13 @@ export default class A extends RR {
49
49
  address: ip,
50
50
  ttl: parseInt(ttl, 10),
51
51
  timestamp: ts,
52
- location: loc !== '' && loc !== '\n' ? loc : '',
52
+ location: loc?.trim() ?? '',
53
53
  })
54
54
  }
55
55
 
56
- fromBind(opts) {
56
+ fromBind({ bindline }) {
57
57
  // test.example.com 3600 IN A 192.0.2.127
58
- const [owner, ttl, c, type, address] = opts.bindline.split(/\s+/)
58
+ const [owner, ttl, c, type, address] = bindline.split(/\s+/)
59
59
  return new A({
60
60
  owner,
61
61
  ttl: parseInt(ttl, 10),
package/rr/aaaa.js CHANGED
@@ -15,7 +15,7 @@ export default class AAAA extends RR {
15
15
  }
16
16
 
17
17
  getCompressed(val) {
18
- return this.compress(val || this.get('address'))
18
+ return this.compress(val ?? this.get('address'))
19
19
  }
20
20
 
21
21
  getDescription() {
@@ -45,14 +45,14 @@ export default class AAAA extends RR {
45
45
  }
46
46
 
47
47
  /****** IMPORTERS *******/
48
- fromTinydns(opts) {
49
- const str = opts.tinyline
48
+ fromTinydns({ tinyline }) {
49
+ const str = tinyline
50
50
  let fqdn, ip, n, rdata, ttl, ts, loc
51
51
 
52
52
  switch (str[0]) {
53
53
  case ':':
54
54
  // GENERIC => :fqdn:28:rdata:ttl:timestamp:lo
55
- ;[fqdn, n, rdata, ttl, ts, loc] = str.substring(1).split(':')
55
+ ;[fqdn, n, rdata, ttl, ts, loc] = str.slice(1).split(':')
56
56
  if (n != 28) this.throwHelp('AAAA fromTinydns, invalid n')
57
57
  ip = TINYDNS.octalToHex(rdata)
58
58
  .match(/([0-9a-fA-F]{4})/g)
@@ -62,7 +62,7 @@ export default class AAAA extends RR {
62
62
  case '6':
63
63
  // AAAA => 3fqdn:ip:x:ttl:timestamp:lo
64
64
  // AAAA,PTR => 6fqdn:ip:x:ttl:timestamp:lo
65
- ;[fqdn, rdata, ttl, ts, loc] = str.substring(1).split(':')
65
+ ;[fqdn, rdata, ttl, ts, loc] = str.slice(1).split(':')
66
66
  ip = rdata.match(/(.{4})/g).join(':')
67
67
  break
68
68
  }
@@ -73,13 +73,13 @@ export default class AAAA extends RR {
73
73
  type: 'AAAA',
74
74
  address: ip,
75
75
  timestamp: ts,
76
- location: loc !== '' && loc !== '\n' ? loc : '',
76
+ location: loc?.trim() ?? '',
77
77
  })
78
78
  }
79
79
 
80
- fromBind(opts) {
80
+ fromBind({ bindline }) {
81
81
  // test.example.com 3600 IN AAAA ...
82
- const [owner, ttl, c, type, ip] = opts.bindline.split(/\s+/)
82
+ const [owner, ttl, c, type, ip] = bindline.split(/\s+/)
83
83
  return new AAAA({
84
84
  owner,
85
85
  ttl: parseInt(ttl, 10),
@@ -124,7 +124,7 @@ export default class AAAA extends RR {
124
124
  if (delimiter === undefined) delimiter = ':'
125
125
 
126
126
  const colons = val.match(/:/g)
127
- if (colons && colons.length < 7) {
127
+ if (colons?.length < 7) {
128
128
  // console.log(`AAAA: restoring compressed colons`)
129
129
  val = val.replace(/::/, ':'.repeat(9 - colons.length))
130
130
  }
package/rr/apl.js CHANGED
@@ -42,9 +42,56 @@ export default class APL extends RR {
42
42
  }
43
43
 
44
44
  /****** IMPORTERS *******/
45
- fromBind(opts) {
45
+ fromTinydns({ tinyline }) {
46
+ // APL via generic, :fqdn:42:rdata:ttl:timestamp:lo
47
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
48
+ if (n != 42) this.throwHelp('APL fromTinydns, invalid n')
49
+
50
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
51
+ const items = []
52
+ let pos = 0
53
+
54
+ while (pos < bytes.length) {
55
+ const afi = bytes.readUInt16BE(pos)
56
+ pos += 2
57
+ const prefix = bytes.readUInt8(pos)
58
+ pos++
59
+ const adfLenByte = bytes.readUInt8(pos)
60
+ pos++
61
+ const neg = (adfLenByte & 0x80) !== 0
62
+ const addrLen = adfLenByte & 0x7f
63
+ const addrBytes = bytes.slice(pos, pos + addrLen)
64
+ pos += addrLen
65
+
66
+ let addr
67
+ if (afi === 1) {
68
+ const padded = Buffer.alloc(4)
69
+ addrBytes.copy(padded)
70
+ addr = [...padded].join('.')
71
+ } else {
72
+ const padded = Buffer.alloc(16)
73
+ addrBytes.copy(padded)
74
+ const groups = []
75
+ for (let i = 0; i < 16; i += 2) groups.push(padded.readUInt16BE(i).toString(16).padStart(4, '0'))
76
+ addr = this.compressIPv6(groups.join(':'))
77
+ }
78
+
79
+ items.push(`${neg ? '!' : ''}${afi}:${addr}/${prefix}`)
80
+ }
81
+
82
+ return new APL({
83
+ owner: this.fullyQualify(fqdn),
84
+ ttl: parseInt(ttl, 10),
85
+ type: 'APL',
86
+ 'apl rdata': items.join(' '),
87
+ timestamp: ts,
88
+ location: loc?.trim() ?? '',
89
+ })
90
+ }
91
+
92
+ fromBind({ bindline }) {
46
93
  // test.example.com 3600 IN APL {[!]afi:address/prefix}*
47
- const parts = opts.bindline.split(/\s+/)
94
+ const parts = bindline.split(/\s+/)
48
95
  const [owner, ttl, c, type] = parts
49
96
  return new APL({
50
97
  owner,
package/rr/caa.js CHANGED
@@ -91,17 +91,17 @@ export default class CAA extends RR {
91
91
  }
92
92
 
93
93
  /****** IMPORTERS *******/
94
- fromTinydns(opts) {
94
+ fromTinydns({ tinyline }) {
95
95
  // CAA via generic, :fqdn:n:rdata:ttl:timestamp:lo
96
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
96
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
97
97
  if (n != 257) this.throwHelp('CAA fromTinydns, invalid n')
98
98
 
99
- const flags = TINYDNS.octalToUInt8(rdata.substring(0, 4))
100
- const taglen = TINYDNS.octalToUInt8(rdata.substring(4, 8))
99
+ const flags = TINYDNS.octalToUInt8(rdata.slice(0, 4))
100
+ const taglen = TINYDNS.octalToUInt8(rdata.slice(4, 8))
101
101
 
102
- const unescaped = TINYDNS.octalToChar(rdata.substring(8))
103
- const tag = unescaped.substring(0, taglen)
104
- const fingerprint = unescaped.substring(taglen)
102
+ const unescaped = TINYDNS.octalToChar(rdata.slice(8))
103
+ const tag = unescaped.slice(0, taglen)
104
+ const fingerprint = unescaped.slice(taglen)
105
105
 
106
106
  return new CAA({
107
107
  owner: this.fullyQualify(fqdn),
@@ -111,17 +111,23 @@ export default class CAA extends RR {
111
111
  tag,
112
112
  value: fingerprint,
113
113
  timestamp: ts,
114
- location: loc !== '' && loc !== '\n' ? loc : '',
114
+ location: loc?.trim() ?? '',
115
115
  })
116
116
  }
117
117
 
118
- fromBind(opts) {
118
+ fromBind({ bindline }) {
119
119
  // test.example.com 3600 IN CAA flags, tags, value
120
- const regex = /^([\S]+)\s+([0-9]{1,10})\s+(IN)\s+(CAA)\s+([0-9]+)\s+(\w+)\s+("[^"]+"|[\S]+?)$/i
121
- const fields = opts.bindline.trim().match(regex)
122
- if (!fields) this.throwHelp(`unable to parse: ${opts.bindline}`)
120
+ const regex =
121
+ /^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<class>IN)\s+(?<type>CAA)\s+(?<flags>\d+)\s+(?<tag>\w+)\s+(?:"(?<quotedValue>[^"]+)"|(?<unquotedValue>\S+))$/i
122
+
123
+ const match = bindline.trim().match(regex)
124
+
125
+ if (!match) {
126
+ this.throwHelp(`unable to parse CAA: ${bindline}`)
127
+ }
128
+
129
+ const { owner, ttl, class: c, type, flags, tag, quotedValue, unquotedValue } = match.groups
123
130
 
124
- const [owner, ttl, c, type, flags, tag, value] = fields.slice(1)
125
131
  return new CAA({
126
132
  owner,
127
133
  ttl: parseInt(ttl, 10),
@@ -129,21 +135,18 @@ export default class CAA extends RR {
129
135
  type,
130
136
  flags: parseInt(flags, 10),
131
137
  tag,
132
- value,
138
+ value: quotedValue ?? unquotedValue,
133
139
  })
134
140
  }
135
141
 
136
142
  /****** EXPORTERS *******/
137
143
 
138
144
  toTinydns() {
139
- let rdata = ''
140
- rdata += TINYDNS.UInt8toOctal(this.get('flags'))
141
-
142
- rdata += TINYDNS.UInt8toOctal(this.get('tag').length)
143
- rdata += TINYDNS.escapeOctal(/[\r\n\t:\\/]/, this.get('tag'))
144
-
145
- rdata += TINYDNS.escapeOctal(/[\r\n\t:\\/]/, this.getQuoted('value'))
146
-
147
- return this.getTinydnsGeneric(rdata)
145
+ return this.getTinydnsGeneric(
146
+ TINYDNS.UInt8toOctal(this.get('flags')) +
147
+ TINYDNS.UInt8toOctal(this.get('tag').length) +
148
+ TINYDNS.escapeOctal(/[\r\n\t:\\/]/, this.get('tag')) +
149
+ TINYDNS.escapeOctal(/[\r\n\t:\\/]/, this.getQuoted('value')),
150
+ )
148
151
  }
149
152
  }
package/rr/cert.js CHANGED
@@ -11,8 +11,29 @@ export default class CERT extends RR {
11
11
  setCertType(val) {
12
12
  // The type field is the certificate type
13
13
  // the type field as an unsigned decimal integer or as a mnemonic symbol
14
- // this.is16bitInt('CERT', 'type', val)
15
-
14
+ if (val === undefined || val === null || val === '') {
15
+ this.throwHelp('cert type is required')
16
+ }
17
+ // Accept both mnemonic and numeric, but validate mnemonic
18
+ if (typeof val === 'string') {
19
+ const types = {
20
+ PKIX: 1,
21
+ SPKI: 2,
22
+ PGP: 3,
23
+ IPKIX: 4,
24
+ ISPKI: 5,
25
+ IPGP: 6,
26
+ ACPKIX: 7,
27
+ IACPKIX: 8,
28
+ URI: 253,
29
+ OID: 254,
30
+ }
31
+ if (!Object.hasOwn(types, val)) {
32
+ this.throwHelp(`CERT: unknown cert type mnemonic: ${val}`)
33
+ }
34
+ } else {
35
+ this.is16bitInt('CERT', 'cert type', val)
36
+ }
16
37
  this.set('cert type', val)
17
38
  }
18
39
 
@@ -54,6 +75,9 @@ export default class CERT extends RR {
54
75
  setCertificate(val) {
55
76
  // certificate/CRL portion is represented in base 64 [16] and may be
56
77
  // divided into any number of white-space-separated substrings
78
+ if (val === undefined || val === null || val === '') {
79
+ this.throwHelp('certificate is required and cannot be empty')
80
+ }
57
81
  this.set('certificate', val)
58
82
  }
59
83
 
@@ -75,8 +99,8 @@ export default class CERT extends RR {
75
99
 
76
100
  /****** IMPORTERS *******/
77
101
 
78
- fromTinydns(opts) {
79
- const [owner, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
102
+ fromTinydns({ tinyline }) {
103
+ const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
80
104
  if (n != 37) this.throwHelp('CERT fromTinydns, invalid n')
81
105
 
82
106
  const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
@@ -106,13 +130,13 @@ export default class CERT extends RR {
106
130
  algorithm: bytes.readUInt8(4),
107
131
  certificate: bytes.slice(5).toString(),
108
132
  timestamp: ts,
109
- location: loc !== '' && loc !== '\n' ? loc : '',
133
+ location: loc?.trim() ?? '',
110
134
  })
111
135
  }
112
136
 
113
- fromBind(opts) {
137
+ fromBind({ bindline }) {
114
138
  // test.example.com 3600 IN CERT certtype, keytag, algo, cert
115
- const [owner, ttl, c, type, certtype, keytag, algo, certificate] = opts.bindline.split(/\s+/)
139
+ const [owner, ttl, c, type, certtype, keytag, algo, certificate] = bindline.split(/\s+/)
116
140
  return new CERT({
117
141
  owner,
118
142
  ttl: parseInt(ttl, 10),
package/rr/cname.js CHANGED
@@ -38,9 +38,9 @@ export default class CNAME extends RR {
38
38
  }
39
39
 
40
40
  /****** IMPORTERS *******/
41
- fromTinydns(opts) {
41
+ fromTinydns({ tinyline }) {
42
42
  // Cfqdn:p:ttl:timestamp:lo
43
- const [fqdn, p, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
43
+ const [fqdn, p, ttl, ts, loc] = tinyline.slice(1).split(':')
44
44
 
45
45
  return new CNAME({
46
46
  owner: this.fullyQualify(fqdn),
@@ -48,13 +48,13 @@ export default class CNAME extends RR {
48
48
  type: 'CNAME',
49
49
  cname: this.fullyQualify(p),
50
50
  timestamp: ts,
51
- location: loc !== '' && loc !== '\n' ? loc : '',
51
+ location: loc?.trim() ?? '',
52
52
  })
53
53
  }
54
54
 
55
- fromBind(opts) {
55
+ fromBind({ bindline }) {
56
56
  // test.example.com 3600 IN CNAME ...
57
- const [owner, ttl, c, type, cname] = opts.bindline.split(/\s+/)
57
+ const [owner, ttl, c, type, cname] = bindline.split(/\s+/)
58
58
  return new CNAME({
59
59
  owner,
60
60
  ttl: parseInt(ttl, 10),
package/rr/dhcid.js CHANGED
@@ -40,9 +40,24 @@ export default class DHCID extends RR {
40
40
  }
41
41
 
42
42
  /****** IMPORTERS *******/
43
- fromBind(opts) {
43
+ fromTinydns({ tinyline }) {
44
+ // DHCID via generic, :fqdn:49:rdata:ttl:timestamp:lo
45
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
46
+ if (n != 49) this.throwHelp('DHCID fromTinydns, invalid n')
47
+
48
+ return new DHCID({
49
+ owner: this.fullyQualify(fqdn),
50
+ ttl: parseInt(ttl, 10),
51
+ type: 'DHCID',
52
+ data: TINYDNS.octalToBase64(rdata),
53
+ timestamp: ts,
54
+ location: loc?.trim() ?? '',
55
+ })
56
+ }
57
+
58
+ fromBind({ bindline }) {
44
59
  // host.example.com 3600 IN DHCID <base64data>
45
- const parts = opts.bindline.split(/\s+/)
60
+ const parts = bindline.split(/\s+/)
46
61
  const [owner, ttl, c, type] = parts
47
62
  return new DHCID({
48
63
  owner,
package/rr/dname.js CHANGED
@@ -36,9 +36,9 @@ export default class DNAME extends RR {
36
36
  }
37
37
 
38
38
  /****** IMPORTERS *******/
39
- fromTinydns(opts) {
39
+ fromTinydns({ tinyline }) {
40
40
  // DNAME via generic, :fqdn:n:rdata:ttl:timestamp:lo
41
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
41
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
42
42
  if (n != 39) this.throwHelp('DNAME fromTinydns, invalid n')
43
43
 
44
44
  return new DNAME({
@@ -47,13 +47,13 @@ export default class DNAME extends RR {
47
47
  target: TINYDNS.unpackDomainName(rdata)[0],
48
48
  ttl: parseInt(ttl, 10),
49
49
  timestamp: ts,
50
- location: loc !== '' && loc !== '\n' ? loc : '',
50
+ location: loc?.trim() ?? '',
51
51
  })
52
52
  }
53
53
 
54
- fromBind(opts) {
54
+ fromBind({ bindline }) {
55
55
  // test.example.com 3600 IN DNAME ...
56
- const [owner, ttl, c, type, target] = opts.bindline.split(/\s+/)
56
+ const [owner, ttl, c, type, target] = bindline.split(/\s+/)
57
57
  return new DNAME({
58
58
  owner,
59
59
  ttl: parseInt(ttl, 10),
package/rr/dnskey.js CHANGED
@@ -93,12 +93,18 @@ export default class DNSKEY extends RR {
93
93
 
94
94
  /****** IMPORTERS *******/
95
95
 
96
- fromBind(opts) {
96
+ fromBind({ bindline }) {
97
97
  // test.example.com 3600 IN DNSKEY Flags Protocol Algorithm PublicKey
98
- const regex = /^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+\s*(.*?)\s*$/
99
- const match = opts.bindline.match(regex)
100
- if (!match) this.throwHelp(`unable to parse DNSKEY: ${opts.bindline}`)
101
- const [owner, ttl, c, type, flags, protocol, algorithm, publickey] = match.slice(1)
98
+ const regex =
99
+ /^(?<owner>\S+)\s+(?<ttl>\d+)\s+(?<cls>\w+)\s+(?<type>DNSKEY)\s+(?<flags>\d+)\s+(?<protocol>\d+)\s+(?<algorithm>\d+)\s+(?<publickey>\S.*)$/i
100
+
101
+ const match = bindline.trim().match(regex)
102
+
103
+ if (!match) {
104
+ this.throwHelp(`unable to parse DNSKEY: ${bindline}`)
105
+ }
106
+
107
+ const { owner, ttl, c, type, flags, protocol, algorithm, publickey } = match.groups
102
108
 
103
109
  return new DNSKEY({
104
110
  owner,
@@ -112,8 +118,8 @@ export default class DNSKEY extends RR {
112
118
  })
113
119
  }
114
120
 
115
- fromTinydns(opts) {
116
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
121
+ fromTinydns({ tinyline }) {
122
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.substring(1).split(':')
117
123
  if (n != 48) this.throwHelp('DNSKEY fromTinydns, invalid n')
118
124
 
119
125
  const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
@@ -127,7 +133,7 @@ export default class DNSKEY extends RR {
127
133
  algorithm: bytes.readUInt8(3),
128
134
  publickey: bytes.slice(4).toString(),
129
135
  timestamp: ts,
130
- location: loc !== '' && loc !== '\n' ? loc : '',
136
+ location: loc?.trim() ?? '',
131
137
  })
132
138
  }
133
139
 
package/rr/ds.js CHANGED
@@ -64,9 +64,9 @@ export default class DS extends RR {
64
64
 
65
65
  /****** IMPORTERS *******/
66
66
 
67
- fromBind(opts) {
67
+ fromBind({ bindline }) {
68
68
  // test.example.com 3600 IN DS Key Tag Algorithm, Digest Type, Digest
69
- const [owner, ttl, c, type, keytag, algorithm, digesttype] = opts.bindline.split(/\s+/)
69
+ const [owner, ttl, c, type, keytag, algorithm, digesttype] = bindline.split(/\s+/)
70
70
  return new DS({
71
71
  owner,
72
72
  ttl: parseInt(ttl, 10),
@@ -75,12 +75,12 @@ export default class DS extends RR {
75
75
  'key tag': parseInt(keytag, 10),
76
76
  algorithm: parseInt(algorithm, 10),
77
77
  'digest type': parseInt(digesttype, 10),
78
- digest: opts.bindline.split(/\s+/).slice(7).join(' ').trim(),
78
+ digest: bindline.split(/\s+/).slice(7).join(' ').trim(),
79
79
  })
80
80
  }
81
81
 
82
- fromTinydns(opts) {
83
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
82
+ fromTinydns({ tinyline }) {
83
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
84
84
  if (n != 43) this.throwHelp('DS fromTinydns, invalid n')
85
85
 
86
86
  const binRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
@@ -94,7 +94,7 @@ export default class DS extends RR {
94
94
  'digest type': binRdata.readUInt8(3),
95
95
  digest: binRdata.slice(4).toString(),
96
96
  timestamp: ts,
97
- location: loc !== '' && loc !== '\n' ? loc : '',
97
+ location: loc?.trim() ?? '',
98
98
  })
99
99
  }
100
100