@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/key.js
CHANGED
|
@@ -63,11 +63,24 @@ export default class KEY extends RR {
|
|
|
63
63
|
return 25
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
getCanonical() {
|
|
67
|
+
return {
|
|
68
|
+
owner: 'example.com.',
|
|
69
|
+
ttl: 3600,
|
|
70
|
+
class: 'IN',
|
|
71
|
+
type: 'KEY',
|
|
72
|
+
flags: 256,
|
|
73
|
+
protocol: 3,
|
|
74
|
+
algorithm: 5,
|
|
75
|
+
publickey: 'AQPSKAsj8...',
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
/****** IMPORTERS *******/
|
|
67
80
|
|
|
68
|
-
fromBind(
|
|
81
|
+
fromBind({ bindline }) {
|
|
69
82
|
// test.example.com 3600 IN KEY Flags Protocol Algorithm PublicKey
|
|
70
|
-
const [owner, ttl, c, type, flags, protocol, algorithm] =
|
|
83
|
+
const [owner, ttl, c, type, flags, protocol, algorithm] = bindline.split(/\s+/)
|
|
71
84
|
return new KEY({
|
|
72
85
|
owner,
|
|
73
86
|
ttl: parseInt(ttl, 10),
|
|
@@ -76,31 +89,27 @@ export default class KEY extends RR {
|
|
|
76
89
|
flags: parseInt(flags, 10),
|
|
77
90
|
protocol: parseInt(protocol, 10),
|
|
78
91
|
algorithm: parseInt(algorithm, 10),
|
|
79
|
-
publickey:
|
|
92
|
+
publickey: bindline.split(/\s+/).slice(7).join(' ').trim(),
|
|
80
93
|
})
|
|
81
94
|
}
|
|
82
95
|
|
|
83
|
-
fromTinydns(
|
|
84
|
-
const rd = opts.rd
|
|
85
|
-
|
|
96
|
+
fromTinydns({ tinyline }) {
|
|
86
97
|
// RDATA format: Flags (6 octal chars) + Protocol (3 octal chars) + Algorithm (3 octal chars) + Public Key (escaped data)
|
|
98
|
+
const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
87
99
|
if (rd.length < 12) {
|
|
88
100
|
this.throwHelp(`KEY: RDATA too short: ${rd}`)
|
|
89
101
|
}
|
|
90
102
|
|
|
91
|
-
const flags = TINYDNS.octalToUInt16(rd.substring(0, 6))
|
|
92
|
-
const protocol = TINYDNS.octalToUInt8(rd.substring(6, 9))
|
|
93
|
-
const algorithm = TINYDNS.octalToUInt8(rd.substring(9, 12))
|
|
94
|
-
const publicKeyData = TINYDNS.unescapeOctal(rd.substring(12))
|
|
95
|
-
|
|
96
103
|
return new KEY({
|
|
97
|
-
owner: this.fullyQualify(
|
|
98
|
-
ttl: parseInt(
|
|
104
|
+
owner: this.fullyQualify(owner),
|
|
105
|
+
ttl: parseInt(ttl, 10),
|
|
99
106
|
type: 'KEY',
|
|
100
|
-
flags:
|
|
101
|
-
protocol:
|
|
102
|
-
algorithm:
|
|
103
|
-
publickey:
|
|
107
|
+
flags: TINYDNS.octalToUInt16(rd.slice(0, 6)),
|
|
108
|
+
protocol: TINYDNS.octalToUInt8(rd.slice(6, 9)),
|
|
109
|
+
algorithm: TINYDNS.octalToUInt8(rd.slice(9, 12)),
|
|
110
|
+
publickey: TINYDNS.unescapeOctal(rd.slice(12)),
|
|
111
|
+
timestamp: ts,
|
|
112
|
+
location: loc?.trim() ?? '',
|
|
104
113
|
})
|
|
105
114
|
}
|
|
106
115
|
|
package/rr/kx.js
CHANGED
|
@@ -51,9 +51,25 @@ export default class KX extends RR {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/****** IMPORTERS *******/
|
|
54
|
-
|
|
54
|
+
fromTinydns({ tinyline }) {
|
|
55
|
+
// KX via generic, :fqdn:36:rdata:ttl:timestamp:lo
|
|
56
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
57
|
+
if (n != 36) this.throwHelp('KX fromTinydns, invalid n')
|
|
58
|
+
|
|
59
|
+
return new KX({
|
|
60
|
+
owner: this.fullyQualify(fqdn),
|
|
61
|
+
ttl: parseInt(ttl, 10),
|
|
62
|
+
type: 'KX',
|
|
63
|
+
preference: TINYDNS.octalToUInt16(rdata.slice(0, 8)),
|
|
64
|
+
exchanger: TINYDNS.unpackDomainName(rdata.slice(8))[0],
|
|
65
|
+
timestamp: ts,
|
|
66
|
+
location: loc?.trim() ?? '',
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fromBind({ bindline }) {
|
|
55
71
|
// test.example.com 3600 IN KX preference exchanger
|
|
56
|
-
const [owner, ttl, c, type, preference, exchanger] =
|
|
72
|
+
const [owner, ttl, c, type, preference, exchanger] = bindline.split(/\s+/)
|
|
57
73
|
return new KX({
|
|
58
74
|
owner,
|
|
59
75
|
ttl: parseInt(ttl, 10),
|
package/rr/loc.js
CHANGED
|
@@ -48,6 +48,16 @@ export default class LOC extends RR {
|
|
|
48
48
|
return 29
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
getCanonical() {
|
|
52
|
+
return {
|
|
53
|
+
owner: 'example.com.',
|
|
54
|
+
ttl: 3600,
|
|
55
|
+
class: 'IN',
|
|
56
|
+
type: 'LOC',
|
|
57
|
+
address: '52 22 23.000 N 4 53 32.000 E 10m 100m 10m 2m',
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
parseLoc(string) {
|
|
52
62
|
// d1 [m1 [s1]]
|
|
53
63
|
const dms = '(\\d+)\\s+(?:(\\d+)\\s+)?(?:([\\d.]+)\\s+)?'
|
|
@@ -85,22 +95,22 @@ export default class LOC extends RR {
|
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
/****** IMPORTERS *******/
|
|
88
|
-
fromTinydns(
|
|
98
|
+
fromTinydns({ tinyline }) {
|
|
89
99
|
// LOC via generic, :fqdn:n:rdata:ttl:timestamp:lo
|
|
90
|
-
const [fqdn, n, rdata, ttl, ts, loc] =
|
|
100
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
91
101
|
if (n != 29) this.throwHelp('LOC fromTinydns, invalid n')
|
|
92
102
|
|
|
93
103
|
// divide by 100 is to convert cm to meters
|
|
94
104
|
const l = {
|
|
95
|
-
version: TINYDNS.octalToUInt8(rdata.
|
|
96
|
-
size: this.fromExponent(TINYDNS.octalToUInt8(rdata.
|
|
105
|
+
version: TINYDNS.octalToUInt8(rdata.slice(0, 4)),
|
|
106
|
+
size: this.fromExponent(TINYDNS.octalToUInt8(rdata.slice(4, 8))),
|
|
97
107
|
precision: {
|
|
98
|
-
horizontal: this.fromExponent(TINYDNS.octalToUInt8(rdata.
|
|
99
|
-
vertical: this.fromExponent(TINYDNS.octalToUInt8(rdata.
|
|
108
|
+
horizontal: this.fromExponent(TINYDNS.octalToUInt8(rdata.slice(8, 12))),
|
|
109
|
+
vertical: this.fromExponent(TINYDNS.octalToUInt8(rdata.slice(12, 16))),
|
|
100
110
|
},
|
|
101
|
-
latitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.
|
|
102
|
-
longitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.
|
|
103
|
-
altitude: TINYDNS.octalToUInt32(rdata.
|
|
111
|
+
latitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.slice(16, 32)), 'lat'),
|
|
112
|
+
longitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.slice(32, 48)), 'lon'),
|
|
113
|
+
altitude: TINYDNS.octalToUInt32(rdata.slice(48, 64)) - REF.ALTITUDE,
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
return new LOC({
|
|
@@ -109,24 +119,24 @@ export default class LOC extends RR {
|
|
|
109
119
|
address: this.toHuman(l),
|
|
110
120
|
ttl: parseInt(ttl, 10),
|
|
111
121
|
timestamp: ts,
|
|
112
|
-
location: loc
|
|
122
|
+
location: loc?.trim() ?? '',
|
|
113
123
|
})
|
|
114
124
|
}
|
|
115
125
|
|
|
116
|
-
fromBind(
|
|
117
|
-
const [owner, ttl, c, type] =
|
|
126
|
+
fromBind({ bindline }) {
|
|
127
|
+
const [owner, ttl, c, type] = bindline.split(/\s+/)
|
|
118
128
|
|
|
119
129
|
return new LOC({
|
|
120
130
|
owner,
|
|
121
131
|
ttl: parseInt(ttl, 10),
|
|
122
132
|
class: c,
|
|
123
133
|
type: type,
|
|
124
|
-
address:
|
|
134
|
+
address: bindline.split(/\s+/).slice(4).join(' ').trim(),
|
|
125
135
|
})
|
|
126
136
|
}
|
|
127
137
|
|
|
128
138
|
dmsToArcSec(obj) {
|
|
129
|
-
let retval = obj.degrees * CONV.deg + (obj.minutes
|
|
139
|
+
let retval = obj.degrees * CONV.deg + (obj.minutes ?? 0) * CONV.min + (obj.seconds ?? 0) * CONV.sec
|
|
130
140
|
switch (obj.hemisphere.toUpperCase()) {
|
|
131
141
|
case 'W':
|
|
132
142
|
case 'S':
|
package/rr/mx.js
CHANGED
|
@@ -29,6 +29,10 @@ export default class MX extends RR {
|
|
|
29
29
|
return 'Mail Exchanger'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
getTags() {
|
|
33
|
+
return ['common']
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
getRdataFields(arg) {
|
|
33
37
|
return ['preference', 'exchange']
|
|
34
38
|
}
|
|
@@ -47,16 +51,16 @@ export default class MX extends RR {
|
|
|
47
51
|
ttl: 43200,
|
|
48
52
|
class: 'IN',
|
|
49
53
|
type: 'MX',
|
|
50
|
-
preference:
|
|
54
|
+
preference: 10,
|
|
51
55
|
exchange: 'mail.example.com.',
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
/****** IMPORTERS *******/
|
|
56
|
-
fromTinydns(
|
|
60
|
+
fromTinydns({ tinyline }) {
|
|
57
61
|
// @fqdn:ip:x:dist:ttl:timestamp:lo
|
|
58
62
|
// eslint-disable-next-line no-unused-vars
|
|
59
|
-
const [owner, ip, x, preference, ttl, ts, loc] =
|
|
63
|
+
const [owner, ip, x, preference, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
60
64
|
|
|
61
65
|
return new MX({
|
|
62
66
|
type: 'MX',
|
|
@@ -65,13 +69,13 @@ export default class MX extends RR {
|
|
|
65
69
|
preference: parseInt(preference, 10) || 0,
|
|
66
70
|
ttl: parseInt(ttl, 10),
|
|
67
71
|
timestamp: ts,
|
|
68
|
-
location: loc
|
|
72
|
+
location: loc?.trim() ?? '',
|
|
69
73
|
})
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
fromBind(
|
|
76
|
+
fromBind({ bindline }) {
|
|
73
77
|
// test.example.com 3600 IN MX preference exchange
|
|
74
|
-
const [owner, ttl, c, type, preference, exchange] =
|
|
78
|
+
const [owner, ttl, c, type, preference, exchange] = bindline.split(/\s+/)
|
|
75
79
|
|
|
76
80
|
return new MX({
|
|
77
81
|
owner,
|
|
@@ -84,6 +88,12 @@ export default class MX extends RR {
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
/****** EXPORTERS *******/
|
|
91
|
+
getWireRdata() {
|
|
92
|
+
const pref = Buffer.alloc(2)
|
|
93
|
+
pref.writeUInt16BE(this.get('preference'))
|
|
94
|
+
return Buffer.concat([pref, this.wirePackDomain(this.get('exchange'))])
|
|
95
|
+
}
|
|
96
|
+
|
|
87
97
|
toBind(zone_opts) {
|
|
88
98
|
return `${this.getPrefix(zone_opts)}\t${this.get('preference')}\t${this.getFQDN('exchange', zone_opts)}\n`
|
|
89
99
|
}
|
package/rr/naptr.js
CHANGED
|
@@ -21,13 +21,28 @@ export default class NAPTR extends RR {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
getRFCs() {
|
|
24
|
-
return [2915, 3403]
|
|
24
|
+
return [2915, 3403, 4848]
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
getTypeId() {
|
|
28
28
|
return 35
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
getCanonical() {
|
|
32
|
+
return {
|
|
33
|
+
owner: 'cid.urn.arpa.',
|
|
34
|
+
ttl: 3600,
|
|
35
|
+
class: 'IN',
|
|
36
|
+
type: 'NAPTR',
|
|
37
|
+
order: 100,
|
|
38
|
+
preference: 10,
|
|
39
|
+
flags: 'S',
|
|
40
|
+
service: 'z3950+N2L+N2R',
|
|
41
|
+
regexp: '',
|
|
42
|
+
replacement: 'gatekeeper.example.com.',
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
/****** Resource record specific setters *******/
|
|
32
47
|
setOrder(val) {
|
|
33
48
|
this.is16bitInt('NAPTR', 'order', val)
|
|
@@ -62,9 +77,9 @@ export default class NAPTR extends RR {
|
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
/****** IMPORTERS *******/
|
|
65
|
-
fromTinydns(
|
|
80
|
+
fromTinydns({ tinyline }) {
|
|
66
81
|
// NAPTR via generic, :fqdn:n:rdata:ttl:timestamp:lo
|
|
67
|
-
const [fqdn, n, rdata, ttl, ts, loc] =
|
|
82
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
68
83
|
if (n != 35) this.throwHelp('NAPTR fromTinydns, invalid n')
|
|
69
84
|
|
|
70
85
|
const binRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
@@ -74,7 +89,7 @@ export default class NAPTR extends RR {
|
|
|
74
89
|
owner: this.fullyQualify(fqdn),
|
|
75
90
|
ttl: parseInt(ttl, 10),
|
|
76
91
|
timestamp: ts,
|
|
77
|
-
location: loc
|
|
92
|
+
location: loc?.trim() ?? '',
|
|
78
93
|
order: binRdata.readUInt16BE(0, 2),
|
|
79
94
|
preference: binRdata.readUInt16BE(2, 4),
|
|
80
95
|
}
|
|
@@ -102,26 +117,30 @@ export default class NAPTR extends RR {
|
|
|
102
117
|
return new NAPTR(rec)
|
|
103
118
|
}
|
|
104
119
|
|
|
105
|
-
fromBind(
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
const replacement = str.trim().split(/\s+/).pop()
|
|
120
|
+
fromBind({ bindline }) {
|
|
121
|
+
const regex =
|
|
122
|
+
/^(?<owner>\S+)\s+(?<ttl>\d+)\s+(?<class>\S+)\s+(?<type>NAPTR)\s+(?<order>\d+)\s+(?<preference>\d+)\s+["'](?<flags>[^"']*)["']\s+["'](?<service>[^"']*)["']\s+["'](?<regexp>[^"']*)["']\s+(?<replacement>\S+)$/
|
|
123
|
+
|
|
124
|
+
const match = bindline.trim().match(regex)
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
if (!match) {
|
|
127
|
+
throw new Error(`Invalid NAPTR BIND line: ${bindline}`)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { owner, ttl, type, order, preference, flags, service, regexp, replacement } = match.groups
|
|
131
|
+
|
|
132
|
+
return new NAPTR({
|
|
133
|
+
owner: this.fullyQualify(owner),
|
|
114
134
|
ttl: parseInt(ttl, 10),
|
|
115
|
-
class:
|
|
116
|
-
type
|
|
135
|
+
class: match.groups.class,
|
|
136
|
+
type,
|
|
117
137
|
order: parseInt(order, 10),
|
|
118
138
|
preference: parseInt(preference, 10),
|
|
119
|
-
flags
|
|
120
|
-
service
|
|
121
|
-
regexp
|
|
122
|
-
replacement
|
|
123
|
-
}
|
|
124
|
-
return new NAPTR(bits)
|
|
139
|
+
flags,
|
|
140
|
+
service,
|
|
141
|
+
regexp,
|
|
142
|
+
replacement,
|
|
143
|
+
})
|
|
125
144
|
}
|
|
126
145
|
|
|
127
146
|
/****** EXPORTERS *******/
|
package/rr/ns.js
CHANGED
|
@@ -21,6 +21,10 @@ export default class NS extends RR {
|
|
|
21
21
|
return 'Name Server'
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
getTags() {
|
|
25
|
+
return ['common']
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
getRdataFields(arg) {
|
|
25
29
|
return ['dname']
|
|
26
30
|
}
|
|
@@ -33,11 +37,21 @@ export default class NS extends RR {
|
|
|
33
37
|
return 2
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
getCanonical() {
|
|
41
|
+
return {
|
|
42
|
+
owner: 'example.com.',
|
|
43
|
+
ttl: 3600,
|
|
44
|
+
class: 'IN',
|
|
45
|
+
type: 'NS',
|
|
46
|
+
dname: 'ns1.example.com.',
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
36
50
|
/****** IMPORTERS *******/
|
|
37
|
-
fromTinydns(
|
|
51
|
+
fromTinydns({ tinyline }) {
|
|
38
52
|
// &fqdn:ip:x:ttl:timestamp:lo
|
|
39
53
|
// eslint-disable-next-line no-unused-vars
|
|
40
|
-
const [fqdn, ip, dname, ttl, ts, loc] =
|
|
54
|
+
const [fqdn, ip, dname, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
41
55
|
|
|
42
56
|
return new NS({
|
|
43
57
|
type: 'NS',
|
|
@@ -45,13 +59,13 @@ export default class NS extends RR {
|
|
|
45
59
|
dname: this.fullyQualify(/\./.test(dname) ? dname : `${dname}.ns.${fqdn}`),
|
|
46
60
|
ttl: parseInt(ttl, 10),
|
|
47
61
|
timestamp: ts,
|
|
48
|
-
location: loc
|
|
62
|
+
location: loc?.trim() ?? '',
|
|
49
63
|
})
|
|
50
64
|
}
|
|
51
65
|
|
|
52
|
-
fromBind(
|
|
66
|
+
fromBind({ bindline }) {
|
|
53
67
|
// test.example.com 3600 IN NS dname
|
|
54
|
-
const [owner, ttl, c, type, dname] =
|
|
68
|
+
const [owner, ttl, c, type, dname] = bindline.split(/\s+/)
|
|
55
69
|
|
|
56
70
|
return new NS({
|
|
57
71
|
owner,
|
|
@@ -63,6 +77,10 @@ export default class NS extends RR {
|
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
/****** EXPORTERS *******/
|
|
80
|
+
getWireRdata() {
|
|
81
|
+
return this.wirePackDomain(this.get('dname'))
|
|
82
|
+
}
|
|
83
|
+
|
|
66
84
|
toBind(zone_opts) {
|
|
67
85
|
return `${this.getPrefix(zone_opts)}\t${this.getFQDN('dname', zone_opts)}\n`
|
|
68
86
|
}
|
package/rr/nsec.js
CHANGED
|
@@ -29,6 +29,10 @@ export default class NSEC extends RR {
|
|
|
29
29
|
return 'Next Secure'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
getTags() {
|
|
33
|
+
return ['dnssec']
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
getRdataFields(arg) {
|
|
33
37
|
return ['next domain', 'type bit maps']
|
|
34
38
|
}
|
|
@@ -41,10 +45,21 @@ export default class NSEC extends RR {
|
|
|
41
45
|
return 47
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
getCanonical() {
|
|
49
|
+
return {
|
|
50
|
+
owner: 'alfa.example.com.',
|
|
51
|
+
ttl: 3600,
|
|
52
|
+
class: 'IN',
|
|
53
|
+
type: 'NSEC',
|
|
54
|
+
'next domain': 'host.example.com.',
|
|
55
|
+
'type bit maps': 'A MX RRSIG NSEC TYPE1234',
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
44
59
|
/****** IMPORTERS *******/
|
|
45
60
|
|
|
46
|
-
fromTinydns(
|
|
47
|
-
const [owner, _typeId, rdata, ttl, ts, loc] =
|
|
61
|
+
fromTinydns({ tinyline }) {
|
|
62
|
+
const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
48
63
|
const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
49
64
|
const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
|
|
50
65
|
|
|
@@ -55,20 +70,20 @@ export default class NSEC extends RR {
|
|
|
55
70
|
'next domain': nextDomain,
|
|
56
71
|
'type bit maps': binaryRdata.slice(binaryLen).toString(),
|
|
57
72
|
timestamp: ts,
|
|
58
|
-
location: loc
|
|
73
|
+
location: loc?.trim() ?? '',
|
|
59
74
|
})
|
|
60
75
|
}
|
|
61
76
|
|
|
62
|
-
fromBind(
|
|
77
|
+
fromBind({ bindline }) {
|
|
63
78
|
// test.example.com 3600 IN NSEC NextDomain TypeBitMaps
|
|
64
|
-
const [owner, ttl, c, type, next] =
|
|
79
|
+
const [owner, ttl, c, type, next] = bindline.split(/\s+/)
|
|
65
80
|
return new NSEC({
|
|
66
81
|
owner,
|
|
67
82
|
ttl: parseInt(ttl, 10),
|
|
68
83
|
class: c,
|
|
69
84
|
type: type,
|
|
70
85
|
'next domain': next,
|
|
71
|
-
'type bit maps':
|
|
86
|
+
'type bit maps': bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
|
|
72
87
|
})
|
|
73
88
|
}
|
|
74
89
|
|
package/rr/nsec3.js
CHANGED
|
@@ -64,6 +64,10 @@ export default class NSEC3 extends RR {
|
|
|
64
64
|
return 'Next Secure'
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
getTags() {
|
|
68
|
+
return ['dnssec']
|
|
69
|
+
}
|
|
70
|
+
|
|
67
71
|
getRdataFields(arg) {
|
|
68
72
|
return ['hash algorithm', 'flags', 'iterations', 'salt', 'next hashed owner name', 'type bit maps']
|
|
69
73
|
}
|
|
@@ -76,12 +80,27 @@ export default class NSEC3 extends RR {
|
|
|
76
80
|
return 50
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
getCanonical() {
|
|
84
|
+
return {
|
|
85
|
+
owner: 'test.example.com.',
|
|
86
|
+
ttl: 3600,
|
|
87
|
+
class: 'IN',
|
|
88
|
+
type: 'NSEC3',
|
|
89
|
+
'hash algorithm': 1,
|
|
90
|
+
flags: 1,
|
|
91
|
+
iterations: 12,
|
|
92
|
+
salt: 'aabbccdd',
|
|
93
|
+
'next hashed owner name': '2vptu5timamqttgl4luu9kg21e0aor3s',
|
|
94
|
+
'type bit maps': 'A RRSIG',
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
79
98
|
/****** IMPORTERS *******/
|
|
80
99
|
|
|
81
|
-
fromBind(
|
|
100
|
+
fromBind({ bindline }) {
|
|
82
101
|
// 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 =
|
|
102
|
+
const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
|
|
103
|
+
const rdata = bindline.split(/\(|\)/)[1]
|
|
85
104
|
|
|
86
105
|
return new NSEC3({
|
|
87
106
|
owner,
|
|
@@ -97,8 +116,8 @@ export default class NSEC3 extends RR {
|
|
|
97
116
|
})
|
|
98
117
|
}
|
|
99
118
|
|
|
100
|
-
fromTinydns(
|
|
101
|
-
const [fqdn, n, rdata, ttl, ts, loc] =
|
|
119
|
+
fromTinydns({ tinyline }) {
|
|
120
|
+
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
102
121
|
if (n != 50) this.throwHelp('NSEC3 fromTinydns, invalid n')
|
|
103
122
|
|
|
104
123
|
const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
@@ -112,7 +131,7 @@ export default class NSEC3 extends RR {
|
|
|
112
131
|
// Salt (variable length based on Salt Length)
|
|
113
132
|
// Next Hashed Owner Name (variable length)
|
|
114
133
|
// Type Bit Maps (variable length)
|
|
115
|
-
const { salt, nextHashedOwnerName, typeBitMaps } =
|
|
134
|
+
const { salt, nextHashedOwnerName, typeBitMaps } = parseNSEC3Buffer(bytes)
|
|
116
135
|
|
|
117
136
|
return new NSEC3({
|
|
118
137
|
owner: this.fullyQualify(fqdn),
|
|
@@ -125,7 +144,7 @@ export default class NSEC3 extends RR {
|
|
|
125
144
|
'next hashed owner name': nextHashedOwnerName,
|
|
126
145
|
'type bit maps': typeBitMaps,
|
|
127
146
|
timestamp: ts,
|
|
128
|
-
location: loc
|
|
147
|
+
location: loc?.trim() ?? '',
|
|
129
148
|
})
|
|
130
149
|
}
|
|
131
150
|
|
|
@@ -155,3 +174,48 @@ export default class NSEC3 extends RR {
|
|
|
155
174
|
)
|
|
156
175
|
}
|
|
157
176
|
}
|
|
177
|
+
|
|
178
|
+
function parseNSEC3Buffer(bytes) {
|
|
179
|
+
// bytes is a Buffer containing the full RDATA binary (hash alg, flags, iterations, then ASCII salt + next-hashed + type bit maps)
|
|
180
|
+
// Start after the first 4 bytes (hash alg, flags, iterations)
|
|
181
|
+
const rest = bytes.slice(4).toString('utf8')
|
|
182
|
+
|
|
183
|
+
// determine expected next hashed owner name length from hash algorithm
|
|
184
|
+
const hashAlgorithm = bytes.readUInt8(0)
|
|
185
|
+
// common mapping: algorithm 1 => SHA-1 => 20 bytes => base32 length 32
|
|
186
|
+
const expectedLen = hashAlgorithm === 1 ? 32 : hashAlgorithm === 2 ? 52 : 32
|
|
187
|
+
|
|
188
|
+
// salt length is ambiguous in this representation; try to find a split where
|
|
189
|
+
// the following segment matches expected base32 length
|
|
190
|
+
let salt = ''
|
|
191
|
+
let nextHashedOwnerName = ''
|
|
192
|
+
let typeBitMaps = ''
|
|
193
|
+
|
|
194
|
+
const maxSl = Math.min(64, rest.length)
|
|
195
|
+
for (let sl = maxSl; sl >= 1; sl--) {
|
|
196
|
+
const candNext = rest.slice(sl, sl + expectedLen)
|
|
197
|
+
if (candNext.length !== expectedLen) continue
|
|
198
|
+
if (!/^[0-9a-z]+$/.test(candNext)) continue
|
|
199
|
+
// candidate looks like a base32 name; accept and treat remainder as type bit maps
|
|
200
|
+
const saltCandidate = rest.slice(0, sl)
|
|
201
|
+
if (!/^[0-9A-Fa-f]+$/.test(saltCandidate)) continue
|
|
202
|
+
salt = saltCandidate
|
|
203
|
+
nextHashedOwnerName = candNext
|
|
204
|
+
typeBitMaps = rest.slice(sl + expectedLen)
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// fallback: if we couldn't find a split, treat everything up to first non-hex as salt
|
|
209
|
+
if (!nextHashedOwnerName) {
|
|
210
|
+
const saltMatch = rest.match(/^([0-9A-Fa-f]*)/)
|
|
211
|
+
salt = saltMatch ? saltMatch[1] : ''
|
|
212
|
+
nextHashedOwnerName = rest.slice(salt.length)
|
|
213
|
+
typeBitMaps = ''
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
salt,
|
|
218
|
+
nextHashedOwnerName,
|
|
219
|
+
typeBitMaps,
|
|
220
|
+
}
|
|
221
|
+
}
|