@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/rr/nsec3.js
CHANGED
|
@@ -78,10 +78,10 @@ export default class NSEC3 extends RR {
|
|
|
78
78
|
|
|
79
79
|
/****** IMPORTERS *******/
|
|
80
80
|
|
|
81
|
-
fromBind(
|
|
81
|
+
fromBind({ bindline }) {
|
|
82
82
|
// test.example.com. 3600 IN NSEC3 1 1 12 aabbccdd (2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG)
|
|
83
|
-
const [owner, ttl, c, type, ha, flags, iterations, salt] =
|
|
84
|
-
const rdata =
|
|
83
|
+
const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
|
|
84
|
+
const rdata = bindline.split(/\(|\)/)[1]
|
|
85
85
|
|
|
86
86
|
return new NSEC3({
|
|
87
87
|
owner,
|
|
@@ -93,41 +93,53 @@ export default class NSEC3 extends RR {
|
|
|
93
93
|
iterations: parseInt(iterations, 10),
|
|
94
94
|
salt,
|
|
95
95
|
'next hashed owner name': rdata.split(/\s+/)[0],
|
|
96
|
-
'type bit maps': rdata.split(/\s+/).slice(1).join('
|
|
96
|
+
'type bit maps': rdata.split(/\s+/).slice(1).join(' '),
|
|
97
97
|
})
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
fromTinydns(
|
|
101
|
-
const [fqdn, n, rdata, ttl, ts, loc] =
|
|
100
|
+
fromTinydns({ tinyline }) {
|
|
101
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
102
102
|
if (n != 50) this.throwHelp('NSEC3 fromTinydns, invalid n')
|
|
103
103
|
|
|
104
104
|
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
105
105
|
|
|
106
|
+
const hashAlgorithm = bytes.readUInt8(0)
|
|
107
|
+
const flags = bytes.readUInt8(1)
|
|
108
|
+
const iterations = bytes.readUInt16BE(2)
|
|
109
|
+
|
|
110
|
+
// The remaining bytes in the buffer contain:
|
|
111
|
+
// Salt Length (1 octet)
|
|
112
|
+
// Salt (variable length based on Salt Length)
|
|
113
|
+
// Next Hashed Owner Name (variable length)
|
|
114
|
+
// Type Bit Maps (variable length)
|
|
115
|
+
const { salt, nextHashedOwnerName, typeBitMaps } = parseNSEC3Buffer(bytes)
|
|
116
|
+
|
|
106
117
|
return new NSEC3({
|
|
107
118
|
owner: this.fullyQualify(fqdn),
|
|
108
119
|
ttl: parseInt(ttl, 10),
|
|
109
120
|
type: 'NSEC3',
|
|
110
|
-
'hash algorithm':
|
|
111
|
-
flags:
|
|
112
|
-
iterations:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
'hash algorithm': hashAlgorithm,
|
|
122
|
+
flags: flags,
|
|
123
|
+
iterations: iterations,
|
|
124
|
+
salt: salt,
|
|
125
|
+
'next hashed owner name': nextHashedOwnerName,
|
|
126
|
+
'type bit maps': typeBitMaps,
|
|
116
127
|
timestamp: ts,
|
|
117
|
-
location: loc
|
|
128
|
+
location: loc?.trim() ?? '',
|
|
118
129
|
})
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
/****** EXPORTERS *******/
|
|
122
133
|
|
|
123
134
|
toBind(zone_opts) {
|
|
124
|
-
return `${this.getFQDN('owner', zone_opts)}
|
|
135
|
+
return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} NSEC3${this.getRdataFields()
|
|
125
136
|
.slice(0, 4)
|
|
126
|
-
.map((f) => '
|
|
127
|
-
.join('')}
|
|
137
|
+
.map((f) => ' ' + this.get(f))
|
|
138
|
+
.join('')} (${this.getRdataFields()
|
|
128
139
|
.slice(4)
|
|
129
140
|
.map((f) => this.get(f))
|
|
130
|
-
.join('
|
|
141
|
+
.join(' ')})
|
|
142
|
+
`
|
|
131
143
|
}
|
|
132
144
|
|
|
133
145
|
toTinydns() {
|
|
@@ -143,3 +155,48 @@ export default class NSEC3 extends RR {
|
|
|
143
155
|
)
|
|
144
156
|
}
|
|
145
157
|
}
|
|
158
|
+
|
|
159
|
+
function parseNSEC3Buffer(bytes) {
|
|
160
|
+
// bytes is a Buffer containing the full RDATA binary (hash alg, flags, iterations, then ASCII salt + next-hashed + type bit maps)
|
|
161
|
+
// Start after the first 4 bytes (hash alg, flags, iterations)
|
|
162
|
+
const rest = bytes.slice(4).toString('utf8')
|
|
163
|
+
|
|
164
|
+
// determine expected next hashed owner name length from hash algorithm
|
|
165
|
+
const hashAlgorithm = bytes.readUInt8(0)
|
|
166
|
+
// common mapping: algorithm 1 => SHA-1 => 20 bytes => base32 length 32
|
|
167
|
+
const expectedLen = hashAlgorithm === 1 ? 32 : hashAlgorithm === 2 ? 52 : 32
|
|
168
|
+
|
|
169
|
+
// salt length is ambiguous in this representation; try to find a split where
|
|
170
|
+
// the following segment matches expected base32 length
|
|
171
|
+
let salt = ''
|
|
172
|
+
let nextHashedOwnerName = ''
|
|
173
|
+
let typeBitMaps = ''
|
|
174
|
+
|
|
175
|
+
const maxSl = Math.min(64, rest.length)
|
|
176
|
+
for (let sl = maxSl; sl >= 1; sl--) {
|
|
177
|
+
const candNext = rest.slice(sl, sl + expectedLen)
|
|
178
|
+
if (candNext.length !== expectedLen) continue
|
|
179
|
+
if (!/^[0-9a-z]+$/.test(candNext)) continue
|
|
180
|
+
// candidate looks like a base32 name; accept and treat remainder as type bit maps
|
|
181
|
+
const saltCandidate = rest.slice(0, sl)
|
|
182
|
+
if (!/^[0-9A-Fa-f]+$/.test(saltCandidate)) continue
|
|
183
|
+
salt = saltCandidate
|
|
184
|
+
nextHashedOwnerName = candNext
|
|
185
|
+
typeBitMaps = rest.slice(sl + expectedLen)
|
|
186
|
+
break
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// fallback: if we couldn't find a split, treat everything up to first non-hex as salt
|
|
190
|
+
if (!nextHashedOwnerName) {
|
|
191
|
+
const saltMatch = rest.match(/^([0-9A-Fa-f]*)/)
|
|
192
|
+
salt = saltMatch ? saltMatch[1] : ''
|
|
193
|
+
nextHashedOwnerName = rest.slice(salt.length)
|
|
194
|
+
typeBitMaps = ''
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
salt,
|
|
199
|
+
nextHashedOwnerName,
|
|
200
|
+
typeBitMaps,
|
|
201
|
+
}
|
|
202
|
+
}
|
package/rr/nsec3param.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
2
3
|
|
|
3
4
|
export default class NSEC3PARAM extends RR {
|
|
4
5
|
constructor(opts) {
|
|
@@ -10,7 +11,7 @@ export default class NSEC3PARAM extends RR {
|
|
|
10
11
|
setHashAlgorithm(val) {
|
|
11
12
|
// Hash Algorithm is a single octet.
|
|
12
13
|
// The Hash Algorithm field is represented as an unsigned decimal integer.
|
|
13
|
-
if (
|
|
14
|
+
if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'hash algorithm' is required`)
|
|
14
15
|
|
|
15
16
|
this.is8bitInt('NSEC3PARAM', 'hash algorithm', val)
|
|
16
17
|
|
|
@@ -19,7 +20,7 @@ export default class NSEC3PARAM extends RR {
|
|
|
19
20
|
|
|
20
21
|
setFlags(val) {
|
|
21
22
|
// The Flags field is represented as an unsigned decimal integer.
|
|
22
|
-
if (
|
|
23
|
+
if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'flags' is required`)
|
|
23
24
|
|
|
24
25
|
this.is8bitInt('NSEC3PARAM', 'flags', val)
|
|
25
26
|
|
|
@@ -28,7 +29,7 @@ export default class NSEC3PARAM extends RR {
|
|
|
28
29
|
|
|
29
30
|
setIterations(val) {
|
|
30
31
|
// The Iterations field is represented as an unsigned decimal integer. 0-65535
|
|
31
|
-
if (
|
|
32
|
+
if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'iterations' is required`)
|
|
32
33
|
|
|
33
34
|
this.is16bitInt('NSEC3PARAM', 'iterations', val)
|
|
34
35
|
|
|
@@ -40,6 +41,15 @@ export default class NSEC3PARAM extends RR {
|
|
|
40
41
|
// hexadecimal digits. Whitespace is not allowed within the
|
|
41
42
|
// sequence. The Salt field is represented as "-" (without the
|
|
42
43
|
// quotes) when the Salt Length field has a value of 0
|
|
44
|
+
if (val === '-') {
|
|
45
|
+
this.set('salt', val)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (val !== undefined && val !== null && !/^[0-9A-Fa-f]*$/.test(val)) {
|
|
50
|
+
this.throwHelp(`NSEC3PARAM: 'salt' must be hex or '-'`)
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
this.set('salt', val)
|
|
44
54
|
}
|
|
45
55
|
|
|
@@ -61,22 +71,62 @@ export default class NSEC3PARAM extends RR {
|
|
|
61
71
|
|
|
62
72
|
/****** IMPORTERS *******/
|
|
63
73
|
|
|
64
|
-
fromBind(
|
|
65
|
-
// test.example.com 3600 IN NSEC3PARAM
|
|
66
|
-
|
|
74
|
+
fromBind({ bindline }) {
|
|
75
|
+
// test.example.com 3600 IN NSEC3PARAM <hash> <flags> <iterations> <salt>
|
|
76
|
+
// Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
|
|
77
|
+
const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
|
|
67
78
|
return new NSEC3PARAM({
|
|
68
79
|
owner,
|
|
69
80
|
ttl: parseInt(ttl, 10),
|
|
70
81
|
class: c,
|
|
71
82
|
type: type,
|
|
72
|
-
'hash algorithm':
|
|
73
|
-
flags:
|
|
74
|
-
iterations:
|
|
75
|
-
salt:
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
'hash algorithm': parseInt(ha, 10),
|
|
84
|
+
flags: parseInt(flags, 10),
|
|
85
|
+
iterations: parseInt(iterations, 10),
|
|
86
|
+
salt: salt,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fromTinydns({ tinyline }) {
|
|
91
|
+
// RDATA format: Hash Algorithm (3 octal chars) + Flags (3 octal chars) + Iterations (6 octal chars) + Salt (escaped hex string)
|
|
92
|
+
const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
93
|
+
if (rd.length < 4) {
|
|
94
|
+
this.throwHelp(`NSEC3PARAM: RDATA too short: ${rd}`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// rd may contain actual binary characters (from JS string '\\001' -> char 0x01),
|
|
98
|
+
// so convert via octalToChar and read bytes from a Buffer for robust parsing.
|
|
99
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rd), 'binary')
|
|
100
|
+
|
|
101
|
+
return new NSEC3PARAM({
|
|
102
|
+
owner: this.fullyQualify(owner),
|
|
103
|
+
ttl: parseInt(ttl, 10),
|
|
104
|
+
type: 'NSEC3PARAM',
|
|
105
|
+
'hash algorithm': bytes.readUInt8(0),
|
|
106
|
+
flags: bytes.readUInt8(1),
|
|
107
|
+
iterations: bytes.readUInt16BE(2),
|
|
108
|
+
salt: bytes.slice(4).toString('utf8'),
|
|
109
|
+
timestamp: ts,
|
|
110
|
+
location: loc?.trim() ?? '',
|
|
78
111
|
})
|
|
79
112
|
}
|
|
80
113
|
|
|
81
114
|
/****** EXPORTERS *******/
|
|
115
|
+
|
|
116
|
+
toBind(zone_opts) {
|
|
117
|
+
// Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
|
|
118
|
+
return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} NSEC3PARAM ${this.get('hash algorithm')} ${this.get('flags')} ${this.get('iterations')} ${this.get('salt')}
|
|
119
|
+
`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
toTinydns() {
|
|
123
|
+
const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
|
|
124
|
+
|
|
125
|
+
return this.getTinydnsGeneric(
|
|
126
|
+
TINYDNS.UInt8toOctal(this.get('hash algorithm')) +
|
|
127
|
+
TINYDNS.UInt8toOctal(this.get('flags')) +
|
|
128
|
+
TINYDNS.UInt16toOctal(this.get('iterations')) +
|
|
129
|
+
TINYDNS.escapeOctal(dataRe, this.get('salt')),
|
|
130
|
+
)
|
|
131
|
+
}
|
|
82
132
|
}
|
package/rr/nxt.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
2
|
|
|
3
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
|
+
|
|
3
5
|
export default class NXT extends RR {
|
|
4
6
|
constructor(opts) {
|
|
5
7
|
super(opts)
|
|
@@ -41,20 +43,46 @@ export default class NXT extends RR {
|
|
|
41
43
|
|
|
42
44
|
/****** IMPORTERS *******/
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
fromTinydns({ tinyline }) {
|
|
47
|
+
const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
48
|
+
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('NXT fromTinydns, invalid n')
|
|
49
|
+
|
|
50
|
+
const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
51
|
+
const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
|
|
52
|
+
|
|
53
|
+
return new NXT({
|
|
54
|
+
owner: this.fullyQualify(owner),
|
|
55
|
+
ttl: parseInt(ttl, 10),
|
|
56
|
+
type: 'NXT',
|
|
57
|
+
'next domain': nextDomain,
|
|
58
|
+
'type bit map': binaryRdata.slice(binaryLen).toString(),
|
|
59
|
+
timestamp: ts,
|
|
60
|
+
location: loc?.trim() ?? '',
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fromBind({ bindline }) {
|
|
45
65
|
// test.example.com 3600 IN NXT NextDomain TypeBitMap
|
|
46
|
-
const [owner, ttl, c, type, next] =
|
|
66
|
+
const [owner, ttl, c, type, next] = bindline.split(/\s+/)
|
|
47
67
|
return new NXT({
|
|
48
68
|
owner,
|
|
49
69
|
ttl: parseInt(ttl, 10),
|
|
50
70
|
class: c,
|
|
51
71
|
type: type,
|
|
52
72
|
'next domain': next,
|
|
53
|
-
'type bit map':
|
|
73
|
+
'type bit map': bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
|
|
54
74
|
})
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
/****** EXPORTERS *******/
|
|
78
|
+
|
|
79
|
+
toTinydns() {
|
|
80
|
+
const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
|
|
81
|
+
|
|
82
|
+
return this.getTinydnsGeneric(
|
|
83
|
+
TINYDNS.packDomainName(this.get('next domain')) + TINYDNS.escapeOctal(dataRe, this.get('type bit map')),
|
|
84
|
+
)
|
|
85
|
+
}
|
|
58
86
|
}
|
|
59
87
|
|
|
60
88
|
const removeParens = (a) => !['(', ')'].includes(a)
|
package/rr/openpgpkey.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
2
3
|
|
|
3
4
|
export default class OPENPGPKEY extends RR {
|
|
4
5
|
constructor(opts) {
|
|
@@ -27,11 +28,14 @@ export default class OPENPGPKEY extends RR {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/****** IMPORTERS *******/
|
|
30
|
-
fromBind(
|
|
31
|
+
fromBind({ bindline: bindline }) {
|
|
31
32
|
// test.example.com 3600 IN OPENPGPKEY <base64 public key>
|
|
32
|
-
const regex =
|
|
33
|
-
|
|
34
|
-
const
|
|
33
|
+
const regex =
|
|
34
|
+
/^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<class>IN)\s+(?<type>OPENPGPKEY)\s+(?<publickey>\S[\s\S]*)$/i
|
|
35
|
+
const match = bindline.trim().match(regex)
|
|
36
|
+
if (!match) this.throwHelp(`unable to parse OPENPGPKEY: ${bindline}`)
|
|
37
|
+
|
|
38
|
+
const { owner, ttl, class: c, type, publickey } = match.groups
|
|
35
39
|
|
|
36
40
|
return new OPENPGPKEY({
|
|
37
41
|
owner,
|
|
@@ -42,5 +46,25 @@ export default class OPENPGPKEY extends RR {
|
|
|
42
46
|
})
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
fromTinydns({ tinyline }) {
|
|
50
|
+
const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
51
|
+
return new OPENPGPKEY({
|
|
52
|
+
owner: this.fullyQualify(owner),
|
|
53
|
+
ttl: parseInt(ttl, 10),
|
|
54
|
+
type: 'OPENPGPKEY',
|
|
55
|
+
'public key': Buffer.from(TINYDNS.unescapeOctal(rd), 'base64').toString('utf-8'),
|
|
56
|
+
timestamp: ts,
|
|
57
|
+
location: loc?.trim() ?? '',
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
45
61
|
/****** EXPORTERS *******/
|
|
62
|
+
toTinydns() {
|
|
63
|
+
const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
|
|
64
|
+
const escapedPublicKey = TINYDNS.escapeOctal(
|
|
65
|
+
dataRe,
|
|
66
|
+
Buffer.from(this.get('public key'), 'utf-8').toString('base64'),
|
|
67
|
+
)
|
|
68
|
+
return this.getTinydnsGeneric(escapedPublicKey)
|
|
69
|
+
}
|
|
46
70
|
}
|
package/rr/ptr.js
CHANGED
|
@@ -31,9 +31,9 @@ export default class PTR extends RR {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/****** IMPORTERS *******/
|
|
34
|
-
fromTinydns(
|
|
34
|
+
fromTinydns({ tinyline }) {
|
|
35
35
|
// ^fqdn:p:ttl:timestamp:lo
|
|
36
|
-
const [fqdn, p, ttl, ts, loc] =
|
|
36
|
+
const [fqdn, p, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
37
37
|
|
|
38
38
|
return new PTR({
|
|
39
39
|
owner: this.fullyQualify(fqdn),
|
|
@@ -41,13 +41,13 @@ export default class PTR extends RR {
|
|
|
41
41
|
type: 'PTR',
|
|
42
42
|
dname: this.fullyQualify(p),
|
|
43
43
|
timestamp: ts,
|
|
44
|
-
location: loc
|
|
44
|
+
location: loc?.trim() ?? '',
|
|
45
45
|
})
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
fromBind(
|
|
48
|
+
fromBind({ bindline }) {
|
|
49
49
|
// test.example.com 3600 IN PTR dname
|
|
50
|
-
const [owner, ttl, c, type, dname] =
|
|
50
|
+
const [owner, ttl, c, type, dname] = bindline.split(/\s+/)
|
|
51
51
|
return new PTR({
|
|
52
52
|
owner,
|
|
53
53
|
ttl: parseInt(ttl, 10),
|
package/rr/rp.js
CHANGED
|
@@ -52,9 +52,9 @@ export default class RP extends RR {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/****** IMPORTERS *******/
|
|
55
|
-
fromBind(
|
|
55
|
+
fromBind({ bindline }) {
|
|
56
56
|
// test.example.com 3600 IN RP mbox txt
|
|
57
|
-
const [owner, ttl, c, type, mbox, txt] =
|
|
57
|
+
const [owner, ttl, c, type, mbox, txt] = bindline.split(/\s+/)
|
|
58
58
|
return new RP({
|
|
59
59
|
owner,
|
|
60
60
|
ttl: parseInt(ttl, 10),
|
|
@@ -65,6 +65,23 @@ export default class RP extends RR {
|
|
|
65
65
|
})
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
fromTinydns({ tinyline }) {
|
|
69
|
+
const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
70
|
+
|
|
71
|
+
const [mbox, consumed] = TINYDNS.unpackDomainName(rdata)
|
|
72
|
+
const txt = TINYDNS.unpackDomainName(rdata.slice(consumed))[0]
|
|
73
|
+
|
|
74
|
+
return new RP({
|
|
75
|
+
owner: this.fullyQualify(owner),
|
|
76
|
+
ttl: parseInt(ttl, 10),
|
|
77
|
+
type: 'RP',
|
|
78
|
+
mbox,
|
|
79
|
+
txt,
|
|
80
|
+
timestamp: ts,
|
|
81
|
+
location: loc?.trim() ?? '',
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
68
85
|
/****** EXPORTERS *******/
|
|
69
86
|
toBind(zone_opts) {
|
|
70
87
|
return `${this.getPrefix(zone_opts)}\t${this.getFQDN('mbox', zone_opts)}\t${this.getFQDN('txt', zone_opts)}\n`
|
package/rr/rrsig.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
2
3
|
|
|
3
4
|
export default class RRSIG extends RR {
|
|
4
5
|
constructor(opts) {
|
|
@@ -101,16 +102,89 @@ export default class RRSIG extends RR {
|
|
|
101
102
|
|
|
102
103
|
/****** IMPORTERS *******/
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
fromBind({ bindline }) {
|
|
106
|
+
// example.com. 3600 IN RRSIG typecovered algorithm labels origttl sigexp siginc keytag signersname ( signature )
|
|
107
|
+
const parts = bindline.trim().split(/\s+/)
|
|
108
|
+
return new RRSIG({
|
|
109
|
+
owner: parts[0],
|
|
110
|
+
ttl: parseInt(parts[1], 10),
|
|
111
|
+
class: parts[2],
|
|
112
|
+
type: 'RRSIG',
|
|
113
|
+
'type covered': parseInt(parts[4], 10),
|
|
114
|
+
algorithm: parseInt(parts[5], 10),
|
|
115
|
+
labels: parseInt(parts[6], 10),
|
|
116
|
+
'original ttl': parseInt(parts[7], 10),
|
|
117
|
+
'signature expiration': parseInt(parts[8], 10),
|
|
118
|
+
'signature inception': parseInt(parts[9], 10),
|
|
119
|
+
'key tag': parseInt(parts[10], 10),
|
|
120
|
+
'signers name': parts[11],
|
|
121
|
+
signature: parts
|
|
122
|
+
.slice(12)
|
|
123
|
+
.filter((a) => a !== '(' && a !== ')')
|
|
124
|
+
.join(' ')
|
|
125
|
+
.trim(),
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fromTinydns({ tinyline }) {
|
|
130
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
131
|
+
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('RRSIG fromTinydns, invalid n')
|
|
132
|
+
|
|
133
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
134
|
+
const typeCovered = bytes.readUInt16BE(0)
|
|
135
|
+
const algorithm = bytes.readUInt8(2)
|
|
136
|
+
const labels = bytes.readUInt8(3)
|
|
137
|
+
const originalTtl = bytes.readUInt32BE(4)
|
|
138
|
+
const signatureExpiration = bytes.readUInt32BE(8)
|
|
139
|
+
const signatureInception = bytes.readUInt32BE(12)
|
|
140
|
+
const keyTag = bytes.readUInt16BE(16)
|
|
141
|
+
|
|
142
|
+
let pos = 18
|
|
143
|
+
const labelArr = []
|
|
144
|
+
while (pos < bytes.length) {
|
|
145
|
+
const len = bytes.readUInt8(pos++)
|
|
146
|
+
if (len === 0) break
|
|
147
|
+
labelArr.push(bytes.slice(pos, pos + len).toString())
|
|
148
|
+
pos += len
|
|
149
|
+
}
|
|
150
|
+
const signersName = `${labelArr.join('.')}.`
|
|
151
|
+
const signature = bytes.slice(pos).toString()
|
|
152
|
+
|
|
153
|
+
return new RRSIG({
|
|
154
|
+
owner: this.fullyQualify(fqdn),
|
|
155
|
+
ttl: parseInt(ttl, 10),
|
|
156
|
+
type: 'RRSIG',
|
|
157
|
+
'type covered': typeCovered,
|
|
158
|
+
algorithm,
|
|
159
|
+
labels,
|
|
160
|
+
'original ttl': originalTtl,
|
|
161
|
+
'signature expiration': signatureExpiration,
|
|
162
|
+
'signature inception': signatureInception,
|
|
163
|
+
'key tag': keyTag,
|
|
164
|
+
'signers name': signersName,
|
|
165
|
+
signature,
|
|
166
|
+
timestamp: ts,
|
|
167
|
+
location: loc?.trim() ?? '',
|
|
168
|
+
})
|
|
169
|
+
}
|
|
114
170
|
|
|
115
171
|
/****** EXPORTERS *******/
|
|
172
|
+
toBind(zone_opts) {
|
|
173
|
+
return `${this.getPrefix(zone_opts)}\t${this.get('type covered')}\t${this.get('algorithm')}\t${this.get('labels')}\t${this.get('original ttl')}\t${this.get('signature expiration')}\t${this.get('signature inception')}\t${this.get('key tag')}\t${this.getFQDN('signers name', zone_opts)}\t${this.get('signature')}\n`
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
toTinydns() {
|
|
177
|
+
const dataRe = new RegExp(/[\r\n\t:]/, 'g')
|
|
178
|
+
return this.getTinydnsGeneric(
|
|
179
|
+
TINYDNS.UInt16toOctal(this.get('type covered')) +
|
|
180
|
+
TINYDNS.UInt8toOctal(this.get('algorithm')) +
|
|
181
|
+
TINYDNS.UInt8toOctal(this.get('labels')) +
|
|
182
|
+
TINYDNS.UInt32toOctal(this.get('original ttl')) +
|
|
183
|
+
TINYDNS.UInt32toOctal(this.get('signature expiration')) +
|
|
184
|
+
TINYDNS.UInt32toOctal(this.get('signature inception')) +
|
|
185
|
+
TINYDNS.UInt16toOctal(this.get('key tag')) +
|
|
186
|
+
TINYDNS.packDomainName(this.get('signers name')) +
|
|
187
|
+
TINYDNS.escapeOctal(dataRe, this.get('signature')),
|
|
188
|
+
)
|
|
189
|
+
}
|
|
116
190
|
}
|
package/rr/sig.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
2
|
|
|
3
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
|
+
|
|
3
5
|
export default class SIG extends RR {
|
|
4
6
|
constructor(opts) {
|
|
5
7
|
super(opts)
|
|
@@ -15,8 +17,7 @@ export default class SIG extends RR {
|
|
|
15
17
|
|
|
16
18
|
setAlgorithm(val) {
|
|
17
19
|
// a 1 octet Algorithm field
|
|
18
|
-
|
|
19
|
-
this.is8bitInt('SIG', 'labels', val)
|
|
20
|
+
this.is8bitInt('SIG', 'algorithm', val)
|
|
20
21
|
|
|
21
22
|
this.set('algorithm', val)
|
|
22
23
|
}
|
|
@@ -90,16 +91,101 @@ export default class SIG extends RR {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
/****** IMPORTERS *******/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
|
|
95
|
+
fromBind({ bindline }) {
|
|
96
|
+
// example.com. 3600 IN SIG TypeCovered Algorithm Labels OrigTTL SigExpiration SigInception KeyTag SignersName ( Signature )
|
|
97
|
+
const parts = bindline.trim().split(/\s+/)
|
|
98
|
+
|
|
99
|
+
return new SIG({
|
|
100
|
+
owner: parts[0],
|
|
101
|
+
ttl: parseInt(parts[1], 10),
|
|
102
|
+
class: parts[2],
|
|
103
|
+
type: 'SIG',
|
|
104
|
+
'type covered': parseInt(parts[4], 10),
|
|
105
|
+
algorithm: parseInt(parts[5], 10),
|
|
106
|
+
labels: parseInt(parts[6], 10),
|
|
107
|
+
'original ttl': parseInt(parts[7], 10),
|
|
108
|
+
'signature expiration': parseInt(parts[8], 10),
|
|
109
|
+
'signature inception': parseInt(parts[9], 10),
|
|
110
|
+
'key tag': parseInt(parts[10], 10),
|
|
111
|
+
'signers name': parts[11],
|
|
112
|
+
signature: parts
|
|
113
|
+
.slice(12)
|
|
114
|
+
.filter((a) => a !== '(' && a !== ')')
|
|
115
|
+
.join(' ')
|
|
116
|
+
.trim(),
|
|
117
|
+
})
|
|
118
|
+
}
|
|
103
119
|
|
|
104
120
|
/****** EXPORTERS *******/
|
|
121
|
+
toTinydns() {
|
|
122
|
+
const dataRe = new RegExp(/[\r\n\t:]/, 'g')
|
|
123
|
+
|
|
124
|
+
return this.getTinydnsGeneric(
|
|
125
|
+
TINYDNS.UInt16toOctal(this.get('type covered')) +
|
|
126
|
+
TINYDNS.UInt8toOctal(this.get('algorithm')) +
|
|
127
|
+
TINYDNS.UInt8toOctal(this.get('labels')) +
|
|
128
|
+
TINYDNS.UInt32toOctal(this.get('original ttl')) +
|
|
129
|
+
TINYDNS.UInt32toOctal(this.get('signature expiration')) +
|
|
130
|
+
TINYDNS.UInt32toOctal(this.get('signature inception')) +
|
|
131
|
+
TINYDNS.UInt16toOctal(this.get('key tag')) +
|
|
132
|
+
TINYDNS.packDomainName(this.get('signers name')) +
|
|
133
|
+
TINYDNS.escapeOctal(dataRe, this.get('signature')),
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fromTinydns({ tinyline }) {
|
|
138
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.substring(1).split(':')
|
|
139
|
+
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('SIG fromTinydns, invalid n')
|
|
140
|
+
|
|
141
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
142
|
+
|
|
143
|
+
const typeCovered = bytes.readUInt16BE(0)
|
|
144
|
+
const algorithm = bytes.readUInt8(2)
|
|
145
|
+
const labels = bytes.readUInt8(3)
|
|
146
|
+
const originalTtl = bytes.readUInt32BE(4)
|
|
147
|
+
const signatureExpiration = bytes.readUInt32BE(8)
|
|
148
|
+
const signatureInception = bytes.readUInt32BE(12)
|
|
149
|
+
const keyTag = bytes.readUInt16BE(16)
|
|
150
|
+
|
|
151
|
+
// parse signers name from binary buffer starting at offset 18
|
|
152
|
+
let pos = 18
|
|
153
|
+
const labelsArr = []
|
|
154
|
+
while (pos < bytes.length) {
|
|
155
|
+
const len = bytes.readUInt8(pos++)
|
|
156
|
+
if (len === 0) break
|
|
157
|
+
labelsArr.push(bytes.slice(pos, pos + len).toString())
|
|
158
|
+
pos += len
|
|
159
|
+
}
|
|
160
|
+
const signersName = `${labelsArr.join('.')}.`
|
|
161
|
+
|
|
162
|
+
const signature = bytes.slice(pos).toString()
|
|
163
|
+
|
|
164
|
+
return new SIG({
|
|
165
|
+
owner: this.fullyQualify(fqdn),
|
|
166
|
+
ttl: parseInt(ttl, 10),
|
|
167
|
+
type: 'SIG',
|
|
168
|
+
'type covered': typeCovered,
|
|
169
|
+
algorithm,
|
|
170
|
+
labels,
|
|
171
|
+
'original ttl': originalTtl,
|
|
172
|
+
'signature expiration': signatureExpiration,
|
|
173
|
+
'signature inception': signatureInception,
|
|
174
|
+
'key tag': keyTag,
|
|
175
|
+
'signers name': signersName,
|
|
176
|
+
signature,
|
|
177
|
+
timestamp: ts,
|
|
178
|
+
location: loc?.trim() ?? '',
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
toBind(zone_opts) {
|
|
183
|
+
return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} SIG${this.getRdataFields()
|
|
184
|
+
.slice(0, 4)
|
|
185
|
+
.map((f) => ' ' + this.get(f))
|
|
186
|
+
.join('')} ${this.getRdataFields()
|
|
187
|
+
.slice(4, 8)
|
|
188
|
+
.map((f) => this.get(f))
|
|
189
|
+
.join(' ')} ( ${this.get('signature')} )`
|
|
190
|
+
}
|
|
105
191
|
}
|