@nictool/dns-resource-record 1.4.0 → 1.6.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 +30 -1
- package/README.md +49 -42
- package/lib/readme.js +84 -0
- package/lib/tinydns.js +20 -10
- package/package.json +3 -2
- package/rr/a.js +13 -5
- package/rr/aaaa.js +19 -11
- package/rr/apl.js +49 -2
- package/rr/caa.js +30 -23
- package/rr/cert.js +44 -7
- package/rr/cname.js +22 -5
- package/rr/dhcid.js +17 -2
- package/rr/dname.js +15 -5
- package/rr/dnskey.js +31 -8
- package/rr/ds.js +23 -6
- package/rr/hinfo.js +28 -10
- package/rr/hip.js +43 -2
- package/rr/https.js +38 -9
- package/rr/ipseckey.js +20 -16
- package/rr/key.js +26 -17
- package/rr/kx.js +18 -2
- package/rr/loc.js +24 -14
- package/rr/mx.js +16 -6
- package/rr/naptr.js +39 -20
- package/rr/ns.js +23 -5
- package/rr/nsec.js +21 -6
- package/rr/nsec3.js +71 -7
- package/rr/nsec3param.js +52 -22
- package/rr/nxt.js +22 -7
- package/rr/openpgpkey.js +28 -8
- package/rr/ptr.js +23 -5
- package/rr/rp.js +23 -2
- package/rr/rrsig.js +106 -10
- package/rr/sig.js +111 -13
- package/rr/smimea.js +23 -6
- package/rr/soa.js +36 -6
- package/rr/spf.js +17 -3
- package/rr/srv.js +28 -11
- package/rr/sshfp.js +24 -8
- package/rr/svcb.js +37 -10
- package/rr/tlsa.js +26 -9
- package/rr/tsig.js +155 -13
- package/rr/txt.js +46 -12
- package/rr/uri.js +20 -8
- package/rr/wks.js +42 -2
- package/rr.js +109 -7
package/rr/nsec3param.js
CHANGED
|
@@ -11,7 +11,7 @@ export default class NSEC3PARAM extends RR {
|
|
|
11
11
|
setHashAlgorithm(val) {
|
|
12
12
|
// Hash Algorithm is a single octet.
|
|
13
13
|
// The Hash Algorithm field is represented as an unsigned decimal integer.
|
|
14
|
-
if (
|
|
14
|
+
if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'hash algorithm' is required`)
|
|
15
15
|
|
|
16
16
|
this.is8bitInt('NSEC3PARAM', 'hash algorithm', val)
|
|
17
17
|
|
|
@@ -20,7 +20,7 @@ export default class NSEC3PARAM extends RR {
|
|
|
20
20
|
|
|
21
21
|
setFlags(val) {
|
|
22
22
|
// The Flags field is represented as an unsigned decimal integer.
|
|
23
|
-
if (
|
|
23
|
+
if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'flags' is required`)
|
|
24
24
|
|
|
25
25
|
this.is8bitInt('NSEC3PARAM', 'flags', val)
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ export default class NSEC3PARAM extends RR {
|
|
|
29
29
|
|
|
30
30
|
setIterations(val) {
|
|
31
31
|
// The Iterations field is represented as an unsigned decimal integer. 0-65535
|
|
32
|
-
if (
|
|
32
|
+
if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'iterations' is required`)
|
|
33
33
|
|
|
34
34
|
this.is16bitInt('NSEC3PARAM', 'iterations', val)
|
|
35
35
|
|
|
@@ -41,6 +41,15 @@ export default class NSEC3PARAM extends RR {
|
|
|
41
41
|
// hexadecimal digits. Whitespace is not allowed within the
|
|
42
42
|
// sequence. The Salt field is represented as "-" (without the
|
|
43
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
|
+
|
|
44
53
|
this.set('salt', val)
|
|
45
54
|
}
|
|
46
55
|
|
|
@@ -48,6 +57,10 @@ export default class NSEC3PARAM extends RR {
|
|
|
48
57
|
return 'Next Secure Parameters'
|
|
49
58
|
}
|
|
50
59
|
|
|
60
|
+
getTags() {
|
|
61
|
+
return ['dnssec']
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
getRdataFields(arg) {
|
|
52
65
|
return ['hash algorithm', 'flags', 'iterations', 'salt']
|
|
53
66
|
}
|
|
@@ -60,12 +73,25 @@ export default class NSEC3PARAM extends RR {
|
|
|
60
73
|
return 51
|
|
61
74
|
}
|
|
62
75
|
|
|
76
|
+
getCanonical() {
|
|
77
|
+
return {
|
|
78
|
+
owner: 'example.com.',
|
|
79
|
+
ttl: 3600,
|
|
80
|
+
class: 'IN',
|
|
81
|
+
type: 'NSEC3PARAM',
|
|
82
|
+
'hash algorithm': 1,
|
|
83
|
+
flags: 1,
|
|
84
|
+
iterations: 12,
|
|
85
|
+
salt: 'aabbccdd',
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
/****** IMPORTERS *******/
|
|
64
90
|
|
|
65
|
-
fromBind(
|
|
91
|
+
fromBind({ bindline }) {
|
|
66
92
|
// test.example.com 3600 IN NSEC3PARAM <hash> <flags> <iterations> <salt>
|
|
67
93
|
// Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
|
|
68
|
-
const [owner, ttl, c, type, ha, flags, iterations, salt] =
|
|
94
|
+
const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
|
|
69
95
|
return new NSEC3PARAM({
|
|
70
96
|
owner,
|
|
71
97
|
ttl: parseInt(ttl, 10),
|
|
@@ -78,22 +104,27 @@ export default class NSEC3PARAM extends RR {
|
|
|
78
104
|
})
|
|
79
105
|
}
|
|
80
106
|
|
|
81
|
-
fromTinydns(
|
|
82
|
-
const rd = opts.rd
|
|
83
|
-
|
|
107
|
+
fromTinydns({ tinyline }) {
|
|
84
108
|
// RDATA format: Hash Algorithm (3 octal chars) + Flags (3 octal chars) + Iterations (6 octal chars) + Salt (escaped hex string)
|
|
85
|
-
|
|
109
|
+
const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
110
|
+
if (rd.length < 4) {
|
|
86
111
|
this.throwHelp(`NSEC3PARAM: RDATA too short: ${rd}`)
|
|
87
112
|
}
|
|
88
113
|
|
|
114
|
+
// rd may contain actual binary characters (from JS string '\\001' -> char 0x01),
|
|
115
|
+
// so convert via octalToChar and read bytes from a Buffer for robust parsing.
|
|
116
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rd), 'binary')
|
|
117
|
+
|
|
89
118
|
return new NSEC3PARAM({
|
|
90
|
-
owner: this.fullyQualify(
|
|
91
|
-
ttl: parseInt(
|
|
119
|
+
owner: this.fullyQualify(owner),
|
|
120
|
+
ttl: parseInt(ttl, 10),
|
|
92
121
|
type: 'NSEC3PARAM',
|
|
93
|
-
'hash algorithm':
|
|
94
|
-
flags:
|
|
95
|
-
iterations:
|
|
96
|
-
salt:
|
|
122
|
+
'hash algorithm': bytes.readUInt8(0),
|
|
123
|
+
flags: bytes.readUInt8(1),
|
|
124
|
+
iterations: bytes.readUInt16BE(2),
|
|
125
|
+
salt: bytes.slice(4).toString('utf8'),
|
|
126
|
+
timestamp: ts,
|
|
127
|
+
location: loc?.trim() ?? '',
|
|
97
128
|
})
|
|
98
129
|
}
|
|
99
130
|
|
|
@@ -108,12 +139,11 @@ export default class NSEC3PARAM extends RR {
|
|
|
108
139
|
toTinydns() {
|
|
109
140
|
const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
|
|
110
141
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return this.getTinydnsGeneric(rdata)
|
|
142
|
+
return this.getTinydnsGeneric(
|
|
143
|
+
TINYDNS.UInt8toOctal(this.get('hash algorithm')) +
|
|
144
|
+
TINYDNS.UInt8toOctal(this.get('flags')) +
|
|
145
|
+
TINYDNS.UInt16toOctal(this.get('iterations')) +
|
|
146
|
+
TINYDNS.escapeOctal(dataRe, this.get('salt')),
|
|
147
|
+
)
|
|
118
148
|
}
|
|
119
149
|
}
|
package/rr/nxt.js
CHANGED
|
@@ -29,6 +29,10 @@ export default class NXT extends RR {
|
|
|
29
29
|
return 'Next Secure'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
getTags() {
|
|
33
|
+
return ['deprecated']
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
getRdataFields(arg) {
|
|
33
37
|
return ['next domain', 'type bit map']
|
|
34
38
|
}
|
|
@@ -41,11 +45,22 @@ export default class NXT extends RR {
|
|
|
41
45
|
return 30
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
getCanonical() {
|
|
49
|
+
return {
|
|
50
|
+
owner: 'big.example.com.',
|
|
51
|
+
ttl: 3600,
|
|
52
|
+
class: 'IN',
|
|
53
|
+
type: 'NXT',
|
|
54
|
+
'next domain': 'host.example.com.',
|
|
55
|
+
'type bit map': 'A MX NXT',
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
44
59
|
/****** IMPORTERS *******/
|
|
45
60
|
|
|
46
|
-
fromTinydns(
|
|
47
|
-
const [owner, n, rdata, ttl, ts, loc] =
|
|
48
|
-
if (n
|
|
61
|
+
fromTinydns({ tinyline }) {
|
|
62
|
+
const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
63
|
+
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('NXT fromTinydns, invalid n')
|
|
49
64
|
|
|
50
65
|
const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
51
66
|
const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
|
|
@@ -57,20 +72,20 @@ export default class NXT extends RR {
|
|
|
57
72
|
'next domain': nextDomain,
|
|
58
73
|
'type bit map': binaryRdata.slice(binaryLen).toString(),
|
|
59
74
|
timestamp: ts,
|
|
60
|
-
location: loc
|
|
75
|
+
location: loc?.trim() ?? '',
|
|
61
76
|
})
|
|
62
77
|
}
|
|
63
78
|
|
|
64
|
-
fromBind(
|
|
79
|
+
fromBind({ bindline }) {
|
|
65
80
|
// test.example.com 3600 IN NXT NextDomain TypeBitMap
|
|
66
|
-
const [owner, ttl, c, type, next] =
|
|
81
|
+
const [owner, ttl, c, type, next] = bindline.split(/\s+/)
|
|
67
82
|
return new NXT({
|
|
68
83
|
owner,
|
|
69
84
|
ttl: parseInt(ttl, 10),
|
|
70
85
|
class: c,
|
|
71
86
|
type: type,
|
|
72
87
|
'next domain': next,
|
|
73
|
-
'type bit map':
|
|
88
|
+
'type bit map': bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
|
|
74
89
|
})
|
|
75
90
|
}
|
|
76
91
|
|
package/rr/openpgpkey.js
CHANGED
|
@@ -15,6 +15,10 @@ export default class OPENPGPKEY extends RR {
|
|
|
15
15
|
return 'OpenPGP Public Key'
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
getTags() {
|
|
19
|
+
return ['security']
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
getRdataFields() {
|
|
19
23
|
return ['public key']
|
|
20
24
|
}
|
|
@@ -27,12 +31,25 @@ export default class OPENPGPKEY extends RR {
|
|
|
27
31
|
return 61
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
getCanonical() {
|
|
35
|
+
return {
|
|
36
|
+
owner: 'matt.example.com.',
|
|
37
|
+
ttl: 3600,
|
|
38
|
+
class: 'IN',
|
|
39
|
+
type: 'OPENPGPKEY',
|
|
40
|
+
'public key': 'mQINBFY...',
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
/****** IMPORTERS *******/
|
|
31
|
-
fromBind(
|
|
45
|
+
fromBind({ bindline: bindline }) {
|
|
32
46
|
// test.example.com 3600 IN OPENPGPKEY <base64 public key>
|
|
33
|
-
const regex =
|
|
34
|
-
|
|
35
|
-
const
|
|
47
|
+
const regex =
|
|
48
|
+
/^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<class>IN)\s+(?<type>OPENPGPKEY)\s+(?<publickey>\S[\s\S]*)$/i
|
|
49
|
+
const match = bindline.trim().match(regex)
|
|
50
|
+
if (!match) this.throwHelp(`unable to parse OPENPGPKEY: ${bindline}`)
|
|
51
|
+
|
|
52
|
+
const { owner, ttl, class: c, type, publickey } = match.groups
|
|
36
53
|
|
|
37
54
|
return new OPENPGPKEY({
|
|
38
55
|
owner,
|
|
@@ -43,12 +60,15 @@ export default class OPENPGPKEY extends RR {
|
|
|
43
60
|
})
|
|
44
61
|
}
|
|
45
62
|
|
|
46
|
-
fromTinydns(
|
|
63
|
+
fromTinydns({ tinyline }) {
|
|
64
|
+
const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
47
65
|
return new OPENPGPKEY({
|
|
48
|
-
owner: this.fullyQualify(
|
|
49
|
-
ttl: parseInt(
|
|
66
|
+
owner: this.fullyQualify(owner),
|
|
67
|
+
ttl: parseInt(ttl, 10),
|
|
50
68
|
type: 'OPENPGPKEY',
|
|
51
|
-
'public key': Buffer.from(TINYDNS.unescapeOctal(
|
|
69
|
+
'public key': Buffer.from(TINYDNS.unescapeOctal(rd), 'base64').toString('utf-8'),
|
|
70
|
+
timestamp: ts,
|
|
71
|
+
location: loc?.trim() ?? '',
|
|
52
72
|
})
|
|
53
73
|
}
|
|
54
74
|
|
package/rr/ptr.js
CHANGED
|
@@ -18,6 +18,10 @@ export default class PTR extends RR {
|
|
|
18
18
|
return 'Pointer'
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
getTags() {
|
|
22
|
+
return ['common']
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
getRdataFields(arg) {
|
|
22
26
|
return ['dname']
|
|
23
27
|
}
|
|
@@ -30,10 +34,20 @@ export default class PTR extends RR {
|
|
|
30
34
|
return 12
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
getCanonical() {
|
|
38
|
+
return {
|
|
39
|
+
owner: '2.2.0.192.in-addr.arpa.',
|
|
40
|
+
ttl: 3600,
|
|
41
|
+
class: 'IN',
|
|
42
|
+
type: 'PTR',
|
|
43
|
+
dname: 'host.example.com.',
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
/****** IMPORTERS *******/
|
|
34
|
-
fromTinydns(
|
|
48
|
+
fromTinydns({ tinyline }) {
|
|
35
49
|
// ^fqdn:p:ttl:timestamp:lo
|
|
36
|
-
const [fqdn, p, ttl, ts, loc] =
|
|
50
|
+
const [fqdn, p, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
37
51
|
|
|
38
52
|
return new PTR({
|
|
39
53
|
owner: this.fullyQualify(fqdn),
|
|
@@ -41,13 +55,13 @@ export default class PTR extends RR {
|
|
|
41
55
|
type: 'PTR',
|
|
42
56
|
dname: this.fullyQualify(p),
|
|
43
57
|
timestamp: ts,
|
|
44
|
-
location: loc
|
|
58
|
+
location: loc?.trim() ?? '',
|
|
45
59
|
})
|
|
46
60
|
}
|
|
47
61
|
|
|
48
|
-
fromBind(
|
|
62
|
+
fromBind({ bindline }) {
|
|
49
63
|
// test.example.com 3600 IN PTR dname
|
|
50
|
-
const [owner, ttl, c, type, dname] =
|
|
64
|
+
const [owner, ttl, c, type, dname] = bindline.split(/\s+/)
|
|
51
65
|
return new PTR({
|
|
52
66
|
owner,
|
|
53
67
|
ttl: parseInt(ttl, 10),
|
|
@@ -58,6 +72,10 @@ export default class PTR extends RR {
|
|
|
58
72
|
}
|
|
59
73
|
|
|
60
74
|
/****** EXPORTERS *******/
|
|
75
|
+
getWireRdata() {
|
|
76
|
+
return this.wirePackDomain(this.get('dname'))
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
toTinydns() {
|
|
62
80
|
return `^${this.getTinyFQDN('owner')}:${this.getTinyFQDN('dname')}:${this.getTinydnsPostamble()}\n`
|
|
63
81
|
}
|
package/rr/rp.js
CHANGED
|
@@ -28,6 +28,10 @@ export default class RP extends RR {
|
|
|
28
28
|
return 'Responsible Person'
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
getTags() {
|
|
32
|
+
return ['obsolete']
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
getRdataFields(arg) {
|
|
32
36
|
return ['mbox', 'txt']
|
|
33
37
|
}
|
|
@@ -52,9 +56,9 @@ export default class RP extends RR {
|
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/****** IMPORTERS *******/
|
|
55
|
-
fromBind(
|
|
59
|
+
fromBind({ bindline }) {
|
|
56
60
|
// test.example.com 3600 IN RP mbox txt
|
|
57
|
-
const [owner, ttl, c, type, mbox, txt] =
|
|
61
|
+
const [owner, ttl, c, type, mbox, txt] = bindline.split(/\s+/)
|
|
58
62
|
return new RP({
|
|
59
63
|
owner,
|
|
60
64
|
ttl: parseInt(ttl, 10),
|
|
@@ -65,6 +69,23 @@ export default class RP extends RR {
|
|
|
65
69
|
})
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
fromTinydns({ tinyline }) {
|
|
73
|
+
const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
74
|
+
|
|
75
|
+
const [mbox, consumed] = TINYDNS.unpackDomainName(rdata)
|
|
76
|
+
const txt = TINYDNS.unpackDomainName(rdata.slice(consumed))[0]
|
|
77
|
+
|
|
78
|
+
return new RP({
|
|
79
|
+
owner: this.fullyQualify(owner),
|
|
80
|
+
ttl: parseInt(ttl, 10),
|
|
81
|
+
type: 'RP',
|
|
82
|
+
mbox,
|
|
83
|
+
txt,
|
|
84
|
+
timestamp: ts,
|
|
85
|
+
location: loc?.trim() ?? '',
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
68
89
|
/****** EXPORTERS *******/
|
|
69
90
|
toBind(zone_opts) {
|
|
70
91
|
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) {
|
|
@@ -77,6 +78,10 @@ export default class RRSIG extends RR {
|
|
|
77
78
|
return 'Resource Record Signature'
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
getTags() {
|
|
82
|
+
return ['dnssec']
|
|
83
|
+
}
|
|
84
|
+
|
|
80
85
|
getRdataFields(arg) {
|
|
81
86
|
return [
|
|
82
87
|
'type covered',
|
|
@@ -99,18 +104,109 @@ export default class RRSIG extends RR {
|
|
|
99
104
|
return 46
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
getCanonical() {
|
|
108
|
+
return {
|
|
109
|
+
owner: 'example.com.',
|
|
110
|
+
ttl: 3600,
|
|
111
|
+
class: 'IN',
|
|
112
|
+
type: 'RRSIG',
|
|
113
|
+
'type covered': 1,
|
|
114
|
+
algorithm: 5,
|
|
115
|
+
labels: 3,
|
|
116
|
+
'original ttl': 3600,
|
|
117
|
+
'signature expiration': 1045053120,
|
|
118
|
+
'signature inception': 1042461120,
|
|
119
|
+
'key tag': 12345,
|
|
120
|
+
'signers name': 'example.com.',
|
|
121
|
+
signature: 'ABCDEF...',
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
102
125
|
/****** IMPORTERS *******/
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
fromBind({ bindline }) {
|
|
128
|
+
// example.com. 3600 IN RRSIG typecovered algorithm labels origttl sigexp siginc keytag signersname ( signature )
|
|
129
|
+
const parts = bindline.trim().split(/\s+/)
|
|
130
|
+
return new RRSIG({
|
|
131
|
+
owner: parts[0],
|
|
132
|
+
ttl: parseInt(parts[1], 10),
|
|
133
|
+
class: parts[2],
|
|
134
|
+
type: 'RRSIG',
|
|
135
|
+
'type covered': parseInt(parts[4], 10),
|
|
136
|
+
algorithm: parseInt(parts[5], 10),
|
|
137
|
+
labels: parseInt(parts[6], 10),
|
|
138
|
+
'original ttl': parseInt(parts[7], 10),
|
|
139
|
+
'signature expiration': parseInt(parts[8], 10),
|
|
140
|
+
'signature inception': parseInt(parts[9], 10),
|
|
141
|
+
'key tag': parseInt(parts[10], 10),
|
|
142
|
+
'signers name': parts[11],
|
|
143
|
+
signature: parts
|
|
144
|
+
.slice(12)
|
|
145
|
+
.filter((a) => a !== '(' && a !== ')')
|
|
146
|
+
.join(' ')
|
|
147
|
+
.trim(),
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fromTinydns({ tinyline }) {
|
|
152
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
153
|
+
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('RRSIG fromTinydns, invalid n')
|
|
154
|
+
|
|
155
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
156
|
+
const typeCovered = bytes.readUInt16BE(0)
|
|
157
|
+
const algorithm = bytes.readUInt8(2)
|
|
158
|
+
const labels = bytes.readUInt8(3)
|
|
159
|
+
const originalTtl = bytes.readUInt32BE(4)
|
|
160
|
+
const signatureExpiration = bytes.readUInt32BE(8)
|
|
161
|
+
const signatureInception = bytes.readUInt32BE(12)
|
|
162
|
+
const keyTag = bytes.readUInt16BE(16)
|
|
163
|
+
|
|
164
|
+
let pos = 18
|
|
165
|
+
const labelArr = []
|
|
166
|
+
while (pos < bytes.length) {
|
|
167
|
+
const len = bytes.readUInt8(pos++)
|
|
168
|
+
if (len === 0) break
|
|
169
|
+
labelArr.push(bytes.slice(pos, pos + len).toString())
|
|
170
|
+
pos += len
|
|
171
|
+
}
|
|
172
|
+
const signersName = `${labelArr.join('.')}.`
|
|
173
|
+
const signature = bytes.slice(pos).toString()
|
|
174
|
+
|
|
175
|
+
return new RRSIG({
|
|
176
|
+
owner: this.fullyQualify(fqdn),
|
|
177
|
+
ttl: parseInt(ttl, 10),
|
|
178
|
+
type: 'RRSIG',
|
|
179
|
+
'type covered': typeCovered,
|
|
180
|
+
algorithm,
|
|
181
|
+
labels,
|
|
182
|
+
'original ttl': originalTtl,
|
|
183
|
+
'signature expiration': signatureExpiration,
|
|
184
|
+
'signature inception': signatureInception,
|
|
185
|
+
'key tag': keyTag,
|
|
186
|
+
'signers name': signersName,
|
|
187
|
+
signature,
|
|
188
|
+
timestamp: ts,
|
|
189
|
+
location: loc?.trim() ?? '',
|
|
190
|
+
})
|
|
191
|
+
}
|
|
114
192
|
|
|
115
193
|
/****** EXPORTERS *******/
|
|
194
|
+
toBind(zone_opts) {
|
|
195
|
+
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`
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
toTinydns() {
|
|
199
|
+
const dataRe = new RegExp(/[\r\n\t:]/, 'g')
|
|
200
|
+
return this.getTinydnsGeneric(
|
|
201
|
+
TINYDNS.UInt16toOctal(this.get('type covered')) +
|
|
202
|
+
TINYDNS.UInt8toOctal(this.get('algorithm')) +
|
|
203
|
+
TINYDNS.UInt8toOctal(this.get('labels')) +
|
|
204
|
+
TINYDNS.UInt32toOctal(this.get('original ttl')) +
|
|
205
|
+
TINYDNS.UInt32toOctal(this.get('signature expiration')) +
|
|
206
|
+
TINYDNS.UInt32toOctal(this.get('signature inception')) +
|
|
207
|
+
TINYDNS.UInt16toOctal(this.get('key tag')) +
|
|
208
|
+
TINYDNS.packDomainName(this.get('signers name')) +
|
|
209
|
+
TINYDNS.escapeOctal(dataRe, this.get('signature')),
|
|
210
|
+
)
|
|
211
|
+
}
|
|
116
212
|
}
|
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
|
}
|
|
@@ -67,6 +68,10 @@ export default class SIG extends RR {
|
|
|
67
68
|
return 'Signature'
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
getTags() {
|
|
72
|
+
return ['obsolete']
|
|
73
|
+
}
|
|
74
|
+
|
|
70
75
|
getRdataFields(arg) {
|
|
71
76
|
return [
|
|
72
77
|
'type covered',
|
|
@@ -82,26 +87,119 @@ export default class SIG extends RR {
|
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
getRFCs() {
|
|
85
|
-
return [2535]
|
|
90
|
+
return [2535, 3755]
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
getTypeId() {
|
|
89
94
|
return 24
|
|
90
95
|
}
|
|
91
96
|
|
|
97
|
+
getCanonical() {
|
|
98
|
+
return {
|
|
99
|
+
owner: 'example.com.',
|
|
100
|
+
ttl: 3600,
|
|
101
|
+
class: 'IN',
|
|
102
|
+
type: 'SIG',
|
|
103
|
+
'type covered': 1,
|
|
104
|
+
algorithm: 5,
|
|
105
|
+
labels: 3,
|
|
106
|
+
'original ttl': 3600,
|
|
107
|
+
'signature expiration': 1045053120,
|
|
108
|
+
'signature inception': 1042461120,
|
|
109
|
+
'key tag': 12345,
|
|
110
|
+
'signers name': 'example.com.',
|
|
111
|
+
signature: 'ABCDEF...',
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
92
115
|
/****** IMPORTERS *******/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
|
|
117
|
+
fromBind({ bindline }) {
|
|
118
|
+
// example.com. 3600 IN SIG TypeCovered Algorithm Labels OrigTTL SigExpiration SigInception KeyTag SignersName ( Signature )
|
|
119
|
+
const parts = bindline.trim().split(/\s+/)
|
|
120
|
+
|
|
121
|
+
return new SIG({
|
|
122
|
+
owner: parts[0],
|
|
123
|
+
ttl: parseInt(parts[1], 10),
|
|
124
|
+
class: parts[2],
|
|
125
|
+
type: 'SIG',
|
|
126
|
+
'type covered': parseInt(parts[4], 10),
|
|
127
|
+
algorithm: parseInt(parts[5], 10),
|
|
128
|
+
labels: parseInt(parts[6], 10),
|
|
129
|
+
'original ttl': parseInt(parts[7], 10),
|
|
130
|
+
'signature expiration': parseInt(parts[8], 10),
|
|
131
|
+
'signature inception': parseInt(parts[9], 10),
|
|
132
|
+
'key tag': parseInt(parts[10], 10),
|
|
133
|
+
'signers name': parts[11],
|
|
134
|
+
signature: parts
|
|
135
|
+
.slice(12)
|
|
136
|
+
.filter((a) => a !== '(' && a !== ')')
|
|
137
|
+
.join(' ')
|
|
138
|
+
.trim(),
|
|
139
|
+
})
|
|
140
|
+
}
|
|
103
141
|
|
|
104
142
|
/****** EXPORTERS *******/
|
|
143
|
+
toTinydns() {
|
|
144
|
+
const dataRe = new RegExp(/[\r\n\t:]/, 'g')
|
|
145
|
+
|
|
146
|
+
return this.getTinydnsGeneric(
|
|
147
|
+
TINYDNS.UInt16toOctal(this.get('type covered')) +
|
|
148
|
+
TINYDNS.UInt8toOctal(this.get('algorithm')) +
|
|
149
|
+
TINYDNS.UInt8toOctal(this.get('labels')) +
|
|
150
|
+
TINYDNS.UInt32toOctal(this.get('original ttl')) +
|
|
151
|
+
TINYDNS.UInt32toOctal(this.get('signature expiration')) +
|
|
152
|
+
TINYDNS.UInt32toOctal(this.get('signature inception')) +
|
|
153
|
+
TINYDNS.UInt16toOctal(this.get('key tag')) +
|
|
154
|
+
TINYDNS.packDomainName(this.get('signers name')) +
|
|
155
|
+
TINYDNS.escapeOctal(dataRe, this.get('signature')),
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fromTinydns({ tinyline }) {
|
|
160
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.substring(1).split(':')
|
|
161
|
+
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('SIG fromTinydns, invalid n')
|
|
162
|
+
|
|
163
|
+
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
164
|
+
|
|
165
|
+
const typeCovered = bytes.readUInt16BE(0)
|
|
166
|
+
const algorithm = bytes.readUInt8(2)
|
|
167
|
+
const labels = bytes.readUInt8(3)
|
|
168
|
+
const originalTtl = bytes.readUInt32BE(4)
|
|
169
|
+
const signatureExpiration = bytes.readUInt32BE(8)
|
|
170
|
+
const signatureInception = bytes.readUInt32BE(12)
|
|
171
|
+
const keyTag = bytes.readUInt16BE(16)
|
|
172
|
+
|
|
173
|
+
// parse signers name from binary buffer starting at offset 18
|
|
174
|
+
let pos = 18
|
|
175
|
+
const labelsArr = []
|
|
176
|
+
while (pos < bytes.length) {
|
|
177
|
+
const len = bytes.readUInt8(pos++)
|
|
178
|
+
if (len === 0) break
|
|
179
|
+
labelsArr.push(bytes.slice(pos, pos + len).toString())
|
|
180
|
+
pos += len
|
|
181
|
+
}
|
|
182
|
+
const signersName = `${labelsArr.join('.')}.`
|
|
183
|
+
|
|
184
|
+
const signature = bytes.slice(pos).toString()
|
|
185
|
+
|
|
186
|
+
return new SIG({
|
|
187
|
+
owner: this.fullyQualify(fqdn),
|
|
188
|
+
ttl: parseInt(ttl, 10),
|
|
189
|
+
type: 'SIG',
|
|
190
|
+
'type covered': typeCovered,
|
|
191
|
+
algorithm,
|
|
192
|
+
labels,
|
|
193
|
+
'original ttl': originalTtl,
|
|
194
|
+
'signature expiration': signatureExpiration,
|
|
195
|
+
'signature inception': signatureInception,
|
|
196
|
+
'key tag': keyTag,
|
|
197
|
+
'signers name': signersName,
|
|
198
|
+
signature,
|
|
199
|
+
timestamp: ts,
|
|
200
|
+
location: loc?.trim() ?? '',
|
|
201
|
+
})
|
|
202
|
+
}
|
|
105
203
|
|
|
106
204
|
toBind(zone_opts) {
|
|
107
205
|
return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} SIG${this.getRdataFields()
|