@nictool/dns-resource-record 1.3.1 → 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 +28 -0
- package/README.md +20 -19
- package/lib/tinydns.js +45 -24
- package/package.json +9 -9
- package/rr/a.js +5 -5
- package/rr/aaaa.js +9 -9
- package/rr/apl.js +49 -2
- package/rr/caa.js +26 -23
- package/rr/cert.js +63 -4
- package/rr/cname.js +5 -5
- package/rr/dhcid.js +17 -2
- package/rr/dname.js +5 -5
- package/rr/dnskey.js +14 -8
- package/rr/ds.js +6 -6
- package/rr/hinfo.js +13 -10
- package/rr/hip.js +43 -2
- package/rr/https.js +37 -3
- package/rr/ipseckey.js +16 -16
- package/rr/key.js +34 -3
- package/rr/kx.js +18 -2
- package/rr/loc.js +14 -14
- package/rr/mx.js +5 -5
- package/rr/naptr.js +24 -20
- package/rr/ns.js +5 -5
- package/rr/nsec.js +19 -3
- package/rr/nsec3.js +74 -17
- package/rr/nsec3param.js +62 -12
- package/rr/nxt.js +31 -3
- package/rr/openpgpkey.js +28 -4
- package/rr/ptr.js +5 -5
- package/rr/rp.js +19 -2
- package/rr/rrsig.js +84 -10
- package/rr/sig.js +98 -12
- package/rr/smimea.js +20 -3
- package/rr/soa.js +6 -6
- package/rr/spf.js +3 -3
- package/rr/srv.js +11 -11
- package/rr/sshfp.js +8 -8
- package/rr/svcb.js +41 -3
- package/rr/tlsa.js +8 -8
- package/rr/tsig.js +139 -13
- package/rr/txt.js +10 -11
- package/rr/uri.js +8 -8
- package/rr/wks.js +26 -2
- package/rr.js +52 -7
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@ Notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
#### Unreleased
|
|
6
6
|
|
|
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
|
|
23
|
+
|
|
24
|
+
### [1.4.0] - 2026-03-21
|
|
25
|
+
|
|
26
|
+
- feat(toBind): added to nsec3param, rrsig
|
|
27
|
+
- feat(toTinydns): added to key, nsec3param, nxt, openpgpkey
|
|
28
|
+
- feat(fromTinydns): added to cert, https, key, nsec, nsec3param, nxt, openpgpkey, smimea, svcb
|
|
29
|
+
- fix(naptr): fixed flags calc in fromTinydns
|
|
30
|
+
- fix(tinydns): more robust unpackDomainName
|
|
31
|
+
- test: switch test runner to node --test
|
|
32
|
+
|
|
7
33
|
### [1.3.1] - 2026-03-13
|
|
8
34
|
|
|
9
35
|
- ci: update workflow triggers
|
|
@@ -338,3 +364,5 @@ Notable changes to this project are documented in this file.
|
|
|
338
364
|
[1.2.4]: https://github.com/NicTool/dns-resource-record/releases/tag/1.2.4
|
|
339
365
|
[1.3.0]: https://github.com/NicTool/dns-resource-record/releases/tag/v1.3.0
|
|
340
366
|
[1.3.1]: https://github.com/NicTool/dns-resource-record/releases/tag/v1.3.1
|
|
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
|
@@ -27,9 +27,13 @@ export function escapeOctal(re, str) {
|
|
|
27
27
|
return escaped
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export function unescapeOctal(str) {
|
|
31
|
+
return this.octalToChar(str)
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
export function octalToChar(str) {
|
|
31
35
|
// relace instances of \NNN with ASCII
|
|
32
|
-
return str.replace(octalRe, (o) => String.fromCharCode(parseInt(o.
|
|
36
|
+
return str.replace(octalRe, (o) => String.fromCharCode(parseInt(o.slice(1), 8)))
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export function octalToHex(str) {
|
|
@@ -37,29 +41,29 @@ export function octalToHex(str) {
|
|
|
37
41
|
return str.replace(octalRe, (o) => {
|
|
38
42
|
// parseInt(n, 8) -> from octal to decimal
|
|
39
43
|
// .toString(16) -> decimal to hex
|
|
40
|
-
return parseInt(o.
|
|
44
|
+
return parseInt(o.slice(1), 8).toString(16).padStart(2, 0)
|
|
41
45
|
})
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
export function octalToUInt8(str) {
|
|
45
49
|
const b = Buffer.alloc(1)
|
|
46
|
-
b.writeUInt8(parseInt(str.
|
|
50
|
+
b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
|
|
47
51
|
return b.readUInt8()
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
export function octalToUInt16(str) {
|
|
51
55
|
const b = Buffer.alloc(2)
|
|
52
|
-
b.writeUInt8(parseInt(str.
|
|
53
|
-
b.writeUInt8(parseInt(str.
|
|
56
|
+
b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
|
|
57
|
+
b.writeUInt8(parseInt(str.slice(5, 8), 8), 1)
|
|
54
58
|
return b.readUInt16BE()
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
export function octalToUInt32(str) {
|
|
58
62
|
const b = Buffer.alloc(4)
|
|
59
|
-
b.writeUInt8(parseInt(str.
|
|
60
|
-
b.writeUInt8(parseInt(str.
|
|
61
|
-
b.writeUInt8(parseInt(str.
|
|
62
|
-
b.writeUInt8(parseInt(str.
|
|
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)
|
|
63
67
|
return b.readUInt32BE()
|
|
64
68
|
}
|
|
65
69
|
|
|
@@ -95,7 +99,7 @@ export function packDomainName(fqdn) {
|
|
|
95
99
|
// RFC 1035, 3.3 Standard RRs
|
|
96
100
|
// The standard wire format for DNS names. (1 octet length + octets)
|
|
97
101
|
let packed = ''
|
|
98
|
-
fqdn.split('.').
|
|
102
|
+
fqdn.split('.').forEach((label) => {
|
|
99
103
|
if (label === undefined || !label.length) return
|
|
100
104
|
|
|
101
105
|
const len = Buffer.alloc(1)
|
|
@@ -108,22 +112,39 @@ export function packDomainName(fqdn) {
|
|
|
108
112
|
return packed
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
export function unpackDomainName(
|
|
112
|
-
fqdn = Buffer.from(octalToChar(fqdn.toString()))
|
|
113
|
-
|
|
114
|
-
const labels = []
|
|
115
|
+
export function unpackDomainName(escaped) {
|
|
115
116
|
let pos = 0
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
let binaryLen = 0
|
|
118
|
+
const labels = []
|
|
119
|
+
|
|
120
|
+
// consume the next logical "byte" (char or octal escape)
|
|
121
|
+
const getNextByte = () => {
|
|
122
|
+
if (pos >= escaped.length) return null
|
|
123
|
+
|
|
124
|
+
let value
|
|
125
|
+
if (escaped[pos] === '\\') {
|
|
126
|
+
value = parseInt(escaped.slice(pos + 1, pos + 4), 8)
|
|
127
|
+
pos += 4
|
|
128
|
+
} else {
|
|
129
|
+
value = escaped.charCodeAt(pos++)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
binaryLen++
|
|
133
|
+
return value
|
|
122
134
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
135
|
+
|
|
136
|
+
let lengthByte
|
|
137
|
+
while ((lengthByte = getNextByte()) && lengthByte !== 0) {
|
|
138
|
+
let label = ''
|
|
139
|
+
for (let i = 0; i < lengthByte; i++) {
|
|
140
|
+
const char = getNextByte()
|
|
141
|
+
if (char === null) break
|
|
142
|
+
label += String.fromCharCode(char)
|
|
143
|
+
}
|
|
144
|
+
labels.push(label)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return [`${labels.join('.')}.`, pos, binaryLen]
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
export function packHex(str) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nictool/dns-resource-record",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "DNS Resource Records",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -12,15 +12,16 @@
|
|
|
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
|
|
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 .",
|
|
19
19
|
"prettier:fix": "npx prettier --ignore-path .gitignore --write .",
|
|
20
|
-
"test": "
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"versions:fix": "npx
|
|
20
|
+
"test": "node --test test/*.js",
|
|
21
|
+
"test:coverage": "npx c8 --reporter=text --reporter=text-summary npm test",
|
|
22
|
+
"versions": "npx npm-dep-mgr check",
|
|
23
|
+
"versions:fix": "npx npm-dep-mgr update",
|
|
24
|
+
"watch": "node --test --watch test/*.js"
|
|
24
25
|
},
|
|
25
26
|
"repository": {
|
|
26
27
|
"type": "git",
|
|
@@ -47,9 +48,8 @@
|
|
|
47
48
|
"homepage": "https://github.com/NicTool/dns-resource-record#readme",
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@eslint/js": "^10.0.1",
|
|
50
|
-
"eslint": "^10.0
|
|
51
|
-
"globals": "^17.4.0"
|
|
52
|
-
"mocha": "^11.7.5"
|
|
51
|
+
"eslint": "^10.1.0",
|
|
52
|
+
"globals": "^17.4.0"
|
|
53
53
|
},
|
|
54
54
|
"prettier": {
|
|
55
55
|
"printWidth": 110,
|
package/rr/a.js
CHANGED
|
@@ -39,9 +39,9 @@ export default class A extends RR {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/****** IMPORTERS *******/
|
|
42
|
-
fromTinydns(
|
|
42
|
+
fromTinydns({ tinyline }) {
|
|
43
43
|
// +fqdn:ip:ttl:timestamp:lo
|
|
44
|
-
const [owner, ip, ttl, ts, loc] =
|
|
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
|
|
52
|
+
location: loc?.trim() ?? '',
|
|
53
53
|
})
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
fromBind(
|
|
56
|
+
fromBind({ bindline }) {
|
|
57
57
|
// test.example.com 3600 IN A 192.0.2.127
|
|
58
|
-
const [owner, ttl, c, type, address] =
|
|
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
|
|
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(
|
|
49
|
-
const str =
|
|
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.
|
|
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.
|
|
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
|
|
76
|
+
location: loc?.trim() ?? '',
|
|
77
77
|
})
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
fromBind(
|
|
80
|
+
fromBind({ bindline }) {
|
|
81
81
|
// test.example.com 3600 IN AAAA ...
|
|
82
|
-
const [owner, ttl, c, type, ip] =
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
94
|
+
fromTinydns({ tinyline }) {
|
|
95
95
|
// CAA via generic, :fqdn:n:rdata:ttl:timestamp:lo
|
|
96
|
-
const [fqdn, n, rdata, ttl, ts, loc] =
|
|
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.
|
|
100
|
-
const taglen = TINYDNS.octalToUInt8(rdata.
|
|
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.
|
|
103
|
-
const tag = unescaped.
|
|
104
|
-
const fingerprint = unescaped.
|
|
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
|
|
114
|
+
location: loc?.trim() ?? '',
|
|
115
115
|
})
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
fromBind(
|
|
118
|
+
fromBind({ bindline }) {
|
|
119
119
|
// test.example.com 3600 IN CAA flags, tags, value
|
|
120
|
-
const regex =
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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,9 +99,44 @@ export default class CERT extends RR {
|
|
|
75
99
|
|
|
76
100
|
/****** IMPORTERS *******/
|
|
77
101
|
|
|
78
|
-
|
|
102
|
+
fromTinydns({ tinyline }) {
|
|
103
|
+
const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
104
|
+
if (n != 37) this.throwHelp('CERT fromTinydns, invalid n')
|
|
105
|
+
|
|
106
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
107
|
+
const typeNum = bytes.readUInt16BE(0)
|
|
108
|
+
let certType = typeNum
|
|
109
|
+
|
|
110
|
+
const types = {
|
|
111
|
+
1: 'PKIX',
|
|
112
|
+
2: 'SPKI',
|
|
113
|
+
3: 'PGP',
|
|
114
|
+
4: 'IPKIX',
|
|
115
|
+
5: 'ISPKI',
|
|
116
|
+
6: 'IPGP',
|
|
117
|
+
7: 'ACPKIX',
|
|
118
|
+
8: 'IACPKIX',
|
|
119
|
+
253: 'URI',
|
|
120
|
+
254: 'OID',
|
|
121
|
+
}
|
|
122
|
+
if (types[typeNum]) certType = types[typeNum]
|
|
123
|
+
|
|
124
|
+
return new CERT({
|
|
125
|
+
owner: this.fullyQualify(owner),
|
|
126
|
+
ttl: parseInt(ttl, 10),
|
|
127
|
+
type: 'CERT',
|
|
128
|
+
'cert type': certType,
|
|
129
|
+
'key tag': bytes.readUInt16BE(2),
|
|
130
|
+
algorithm: bytes.readUInt8(4),
|
|
131
|
+
certificate: bytes.slice(5).toString(),
|
|
132
|
+
timestamp: ts,
|
|
133
|
+
location: loc?.trim() ?? '',
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fromBind({ bindline }) {
|
|
79
138
|
// test.example.com 3600 IN CERT certtype, keytag, algo, cert
|
|
80
|
-
const [owner, ttl, c, type, certtype, keytag, algo, certificate] =
|
|
139
|
+
const [owner, ttl, c, type, certtype, keytag, algo, certificate] = bindline.split(/\s+/)
|
|
81
140
|
return new CERT({
|
|
82
141
|
owner,
|
|
83
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(
|
|
41
|
+
fromTinydns({ tinyline }) {
|
|
42
42
|
// Cfqdn:p:ttl:timestamp:lo
|
|
43
|
-
const [fqdn, p, ttl, ts, loc] =
|
|
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
|
|
51
|
+
location: loc?.trim() ?? '',
|
|
52
52
|
})
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
fromBind(
|
|
55
|
+
fromBind({ bindline }) {
|
|
56
56
|
// test.example.com 3600 IN CNAME ...
|
|
57
|
-
const [owner, ttl, c, type, cname] =
|
|
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
|
-
|
|
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 =
|
|
60
|
+
const parts = bindline.split(/\s+/)
|
|
46
61
|
const [owner, ttl, c, type] = parts
|
|
47
62
|
return new DHCID({
|
|
48
63
|
owner,
|