@nictool/dns-resource-record 1.6.0 → 1.7.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 +45 -6
- package/README.md +328 -198
- package/dist/dns-rr.min.js +26 -0
- package/dist/dns-rr.min.js.map +7 -0
- package/index.js +12 -11
- package/lib/binary.js +108 -0
- package/lib/bind.js +94 -0
- package/lib/dns-query.js +67 -0
- package/lib/tinydns.js +128 -44
- package/lib/wire.js +519 -0
- package/package.json +29 -10
- package/rr/TEMPLATE.js +124 -0
- package/rr/a.js +8 -48
- package/rr/aaaa.js +13 -80
- package/rr/apl.js +123 -27
- package/rr/caa.js +27 -50
- package/rr/cert.js +58 -87
- package/rr/cname.js +7 -63
- package/rr/dhcid.js +20 -30
- package/rr/dname.js +9 -36
- package/rr/dnskey.js +38 -32
- package/rr/ds.js +43 -57
- package/rr/hinfo.js +21 -40
- package/rr/hip.js +88 -26
- package/rr/https.js +28 -40
- package/rr/ipseckey.js +97 -18
- package/rr/key.js +39 -29
- package/rr/kx.js +16 -27
- package/rr/loc.js +42 -12
- package/rr/mx.js +16 -51
- package/rr/naptr.js +58 -33
- package/rr/ns.js +8 -36
- package/rr/nsec.js +89 -18
- package/rr/nsec3.js +62 -26
- package/rr/nsec3param.js +56 -53
- package/rr/nxt.js +57 -18
- package/rr/openpgpkey.js +24 -30
- package/rr/ptr.js +7 -47
- package/rr/rp.js +17 -32
- package/rr/rrsig.js +80 -85
- package/rr/sig.js +70 -45
- package/rr/smimea.js +33 -33
- package/rr/soa.js +31 -49
- package/rr/spf.js +10 -17
- package/rr/srv.js +22 -62
- package/rr/sshfp.js +25 -53
- package/rr/svcb.js +27 -40
- package/rr/tlsa.js +34 -34
- package/rr/tsig.js +87 -24
- package/rr/txt.js +40 -59
- package/rr/uri.js +19 -29
- package/rr/wks.js +154 -22
- package/rr.js +290 -109
- package/lib/readme.js +0 -84
package/rr/nsec.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
2
|
|
|
3
3
|
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
|
+
import { DNS_TYPE_IDS } from '../lib/binary.js'
|
|
4
5
|
|
|
5
6
|
export default class NSEC extends RR {
|
|
7
|
+
static typeName = 'NSEC'
|
|
8
|
+
static typeId = 47
|
|
9
|
+
static RFCs = [4034]
|
|
10
|
+
static rdataFields = ['next domain', 'type bit maps']
|
|
11
|
+
static tags = ['dnssec']
|
|
12
|
+
|
|
6
13
|
constructor(opts) {
|
|
7
14
|
super(opts)
|
|
8
15
|
if (opts === null) return
|
|
@@ -29,22 +36,6 @@ export default class NSEC extends RR {
|
|
|
29
36
|
return 'Next Secure'
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
getTags() {
|
|
33
|
-
return ['dnssec']
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
getRdataFields(arg) {
|
|
37
|
-
return ['next domain', 'type bit maps']
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
getRFCs() {
|
|
41
|
-
return [4034]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getTypeId() {
|
|
45
|
-
return 47
|
|
46
|
-
}
|
|
47
|
-
|
|
48
39
|
getCanonical() {
|
|
49
40
|
return {
|
|
50
41
|
owner: 'alfa.example.com.',
|
|
@@ -60,7 +51,7 @@ export default class NSEC extends RR {
|
|
|
60
51
|
|
|
61
52
|
fromTinydns({ tinyline }) {
|
|
62
53
|
const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
63
|
-
const binaryRdata =
|
|
54
|
+
const binaryRdata = Uint8Array.from(TINYDNS.octalToChar(rdata), (c) => c.charCodeAt(0))
|
|
64
55
|
const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
|
|
65
56
|
|
|
66
57
|
return new NSEC({
|
|
@@ -68,7 +59,7 @@ export default class NSEC extends RR {
|
|
|
68
59
|
ttl: parseInt(ttl, 10),
|
|
69
60
|
type: 'NSEC',
|
|
70
61
|
'next domain': nextDomain,
|
|
71
|
-
'type bit maps': binaryRdata.
|
|
62
|
+
'type bit maps': new TextDecoder().decode(binaryRdata.subarray(binaryLen)),
|
|
72
63
|
timestamp: ts,
|
|
73
64
|
location: loc?.trim() ?? '',
|
|
74
65
|
})
|
|
@@ -87,6 +78,19 @@ export default class NSEC extends RR {
|
|
|
87
78
|
})
|
|
88
79
|
}
|
|
89
80
|
|
|
81
|
+
fromWire({ owner, cls, ttl, rdata }) {
|
|
82
|
+
const { fqdn: nextDomain, end } = this.wireUnpackDomain(rdata, 0)
|
|
83
|
+
const typeBitMaps = nsecBitmapToTypes(rdata.subarray(end))
|
|
84
|
+
return new NSEC({
|
|
85
|
+
owner,
|
|
86
|
+
ttl,
|
|
87
|
+
class: cls,
|
|
88
|
+
type: 'NSEC',
|
|
89
|
+
'next domain': nextDomain,
|
|
90
|
+
'type bit maps': typeBitMaps,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
90
94
|
/****** EXPORTERS *******/
|
|
91
95
|
|
|
92
96
|
toTinydns() {
|
|
@@ -97,6 +101,73 @@ export default class NSEC extends RR {
|
|
|
97
101
|
TINYDNS.escapeOctal(dataRe, this.get('type bit maps')),
|
|
98
102
|
)
|
|
99
103
|
}
|
|
104
|
+
|
|
105
|
+
getWireRdata() {
|
|
106
|
+
const nameBytes = this.wirePackDomain(this.get('next domain'))
|
|
107
|
+
const bitmapBytes = typesToNsecBitmap(this.get('type bit maps'))
|
|
108
|
+
const result = new Uint8Array(nameBytes.length + bitmapBytes.length)
|
|
109
|
+
result.set(nameBytes)
|
|
110
|
+
result.set(bitmapBytes, nameBytes.length)
|
|
111
|
+
return result
|
|
112
|
+
}
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
const removeParens = (a) => !['(', ')'].includes(a)
|
|
116
|
+
|
|
117
|
+
function nsecBitmapToTypes(bitmap) {
|
|
118
|
+
const DNS_TYPE_NAMES = Object.fromEntries(Object.entries(DNS_TYPE_IDS).map(([k, v]) => [v, k]))
|
|
119
|
+
const types = []
|
|
120
|
+
let pos = 0
|
|
121
|
+
while (pos + 2 <= bitmap.length) {
|
|
122
|
+
const windowNum = bitmap[pos]
|
|
123
|
+
const bitmapLen = bitmap[pos + 1]
|
|
124
|
+
pos += 2
|
|
125
|
+
for (let i = 0; i < bitmapLen; i++) {
|
|
126
|
+
const byte = bitmap[pos + i]
|
|
127
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
128
|
+
if (byte & (0x80 >> bit)) {
|
|
129
|
+
const typeId = windowNum * 256 + i * 8 + bit
|
|
130
|
+
types.push(DNS_TYPE_NAMES[typeId] ?? `TYPE${typeId}`)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
pos += bitmapLen
|
|
135
|
+
}
|
|
136
|
+
return types.join(' ')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function typesToNsecBitmap(typeNamesStr) {
|
|
140
|
+
const typeIds = typeNamesStr
|
|
141
|
+
.trim()
|
|
142
|
+
.split(/\s+/)
|
|
143
|
+
.map((t) => {
|
|
144
|
+
if (/^TYPE\d+$/i.test(t)) return parseInt(t.slice(4), 10)
|
|
145
|
+
return DNS_TYPE_IDS[t.toUpperCase()]
|
|
146
|
+
})
|
|
147
|
+
.filter((id) => id !== undefined && id >= 0)
|
|
148
|
+
|
|
149
|
+
const windows = new Map()
|
|
150
|
+
for (const id of typeIds) {
|
|
151
|
+
const w = Math.floor(id / 256)
|
|
152
|
+
if (!windows.has(w)) windows.set(w, [])
|
|
153
|
+
windows.get(w).push(id % 256)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const blocks = []
|
|
157
|
+
for (const [wNum, bits] of [...windows.entries()].sort((a, b) => a[0] - b[0])) {
|
|
158
|
+
const maxBit = Math.max(...bits)
|
|
159
|
+
const bitmapLen = Math.floor(maxBit / 8) + 1
|
|
160
|
+
const bitmap = new Uint8Array(bitmapLen)
|
|
161
|
+
for (const b of bits) bitmap[Math.floor(b / 8)] |= 0x80 >> (b % 8)
|
|
162
|
+
blocks.push(new Uint8Array([wNum, bitmapLen, ...bitmap]))
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const total = blocks.reduce((s, b) => s + b.length, 0)
|
|
166
|
+
const result = new Uint8Array(total)
|
|
167
|
+
let pos = 0
|
|
168
|
+
for (const b of blocks) {
|
|
169
|
+
result.set(b, pos)
|
|
170
|
+
pos += b.length
|
|
171
|
+
}
|
|
172
|
+
return result
|
|
173
|
+
}
|
package/rr/nsec3.js
CHANGED
|
@@ -3,6 +3,19 @@ import RR from '../rr.js'
|
|
|
3
3
|
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
4
|
|
|
5
5
|
export default class NSEC3 extends RR {
|
|
6
|
+
static typeName = 'NSEC3'
|
|
7
|
+
static typeId = 50
|
|
8
|
+
static RFCs = [5155, 9077]
|
|
9
|
+
static rdataFields = [
|
|
10
|
+
'hash algorithm',
|
|
11
|
+
'flags',
|
|
12
|
+
'iterations',
|
|
13
|
+
'salt',
|
|
14
|
+
'next hashed owner name',
|
|
15
|
+
'type bit maps',
|
|
16
|
+
]
|
|
17
|
+
static tags = ['dnssec']
|
|
18
|
+
|
|
6
19
|
constructor(opts) {
|
|
7
20
|
super(opts)
|
|
8
21
|
if (opts === null) return
|
|
@@ -64,22 +77,6 @@ export default class NSEC3 extends RR {
|
|
|
64
77
|
return 'Next Secure'
|
|
65
78
|
}
|
|
66
79
|
|
|
67
|
-
getTags() {
|
|
68
|
-
return ['dnssec']
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
getRdataFields(arg) {
|
|
72
|
-
return ['hash algorithm', 'flags', 'iterations', 'salt', 'next hashed owner name', 'type bit maps']
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
getRFCs() {
|
|
76
|
-
return [5155, 9077]
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getTypeId() {
|
|
80
|
-
return 50
|
|
81
|
-
}
|
|
82
|
-
|
|
83
80
|
getCanonical() {
|
|
84
81
|
return {
|
|
85
82
|
owner: 'test.example.com.',
|
|
@@ -100,7 +97,10 @@ export default class NSEC3 extends RR {
|
|
|
100
97
|
fromBind({ bindline }) {
|
|
101
98
|
// test.example.com. 3600 IN NSEC3 1 1 12 aabbccdd (2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG)
|
|
102
99
|
const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
|
|
103
|
-
|
|
100
|
+
// rdata may be parenthesized or inline
|
|
101
|
+
const rdataStr = bindline.includes('(')
|
|
102
|
+
? bindline.split(/\(|\)/)[1]
|
|
103
|
+
: bindline.split(/\s+/).slice(8).join(' ')
|
|
104
104
|
|
|
105
105
|
return new NSEC3({
|
|
106
106
|
owner,
|
|
@@ -111,8 +111,8 @@ export default class NSEC3 extends RR {
|
|
|
111
111
|
flags: parseInt(flags, 10),
|
|
112
112
|
iterations: parseInt(iterations, 10),
|
|
113
113
|
salt,
|
|
114
|
-
'next hashed owner name':
|
|
115
|
-
'type bit maps':
|
|
114
|
+
'next hashed owner name': rdataStr.trim().split(/\s+/)[0],
|
|
115
|
+
'type bit maps': rdataStr.trim().split(/\s+/).slice(1).join('\t'),
|
|
116
116
|
})
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -120,11 +120,12 @@ export default class NSEC3 extends RR {
|
|
|
120
120
|
const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
121
121
|
if (n != 50) this.throwHelp('NSEC3 fromTinydns, invalid n')
|
|
122
122
|
|
|
123
|
-
const bytes =
|
|
123
|
+
const bytes = Uint8Array.from(TINYDNS.octalToChar(rdata), (c) => c.charCodeAt(0))
|
|
124
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
124
125
|
|
|
125
|
-
const hashAlgorithm = bytes
|
|
126
|
-
const flags = bytes
|
|
127
|
-
const iterations =
|
|
126
|
+
const hashAlgorithm = bytes[0]
|
|
127
|
+
const flags = bytes[1]
|
|
128
|
+
const iterations = dv.getUint16(2)
|
|
128
129
|
|
|
129
130
|
// The remaining bytes in the buffer contain:
|
|
130
131
|
// Salt Length (1 octet)
|
|
@@ -148,6 +149,23 @@ export default class NSEC3 extends RR {
|
|
|
148
149
|
})
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
fromWire({ owner, cls, ttl, rdata }) {
|
|
153
|
+
const dv = new DataView(rdata.buffer, rdata.byteOffset)
|
|
154
|
+
const { salt, nextHashedOwnerName, typeBitMaps } = parseNSEC3Buffer(rdata)
|
|
155
|
+
return new NSEC3({
|
|
156
|
+
owner,
|
|
157
|
+
ttl,
|
|
158
|
+
class: cls,
|
|
159
|
+
type: 'NSEC3',
|
|
160
|
+
'hash algorithm': rdata[0],
|
|
161
|
+
flags: rdata[1],
|
|
162
|
+
iterations: dv.getUint16(2),
|
|
163
|
+
salt,
|
|
164
|
+
'next hashed owner name': nextHashedOwnerName,
|
|
165
|
+
'type bit maps': typeBitMaps,
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
151
169
|
/****** EXPORTERS *******/
|
|
152
170
|
|
|
153
171
|
toBind(zone_opts) {
|
|
@@ -173,15 +191,33 @@ export default class NSEC3 extends RR {
|
|
|
173
191
|
TINYDNS.escapeOctal(dataRe, this.get('type bit maps')),
|
|
174
192
|
)
|
|
175
193
|
}
|
|
194
|
+
|
|
195
|
+
getWireRdata() {
|
|
196
|
+
const tail = `${this.get('salt')}${this.get('next hashed owner name')}${this.get('type bit maps')}`
|
|
197
|
+
const tailBytes = new TextEncoder().encode(tail)
|
|
198
|
+
|
|
199
|
+
const totalLen = 4 + tailBytes.length
|
|
200
|
+
const bytes = new Uint8Array(totalLen)
|
|
201
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset)
|
|
202
|
+
|
|
203
|
+
let pos = 0
|
|
204
|
+
bytes[pos++] = this.get('hash algorithm')
|
|
205
|
+
bytes[pos++] = this.get('flags')
|
|
206
|
+
dv.setUint16(pos, this.get('iterations'))
|
|
207
|
+
pos += 2
|
|
208
|
+
bytes.set(tailBytes, pos)
|
|
209
|
+
|
|
210
|
+
return bytes
|
|
211
|
+
}
|
|
176
212
|
}
|
|
177
213
|
|
|
178
214
|
function parseNSEC3Buffer(bytes) {
|
|
179
|
-
// bytes is a
|
|
215
|
+
// bytes is a Uint8Array containing the full RDATA binary (hash alg, flags, iterations, then ASCII salt + next-hashed + type bit maps)
|
|
180
216
|
// Start after the first 4 bytes (hash alg, flags, iterations)
|
|
181
|
-
const rest = bytes.
|
|
217
|
+
const rest = new TextDecoder().decode(bytes.subarray(4))
|
|
182
218
|
|
|
183
219
|
// determine expected next hashed owner name length from hash algorithm
|
|
184
|
-
const hashAlgorithm = bytes
|
|
220
|
+
const hashAlgorithm = bytes[0]
|
|
185
221
|
// common mapping: algorithm 1 => SHA-1 => 20 bytes => base32 length 32
|
|
186
222
|
const expectedLen = hashAlgorithm === 1 ? 32 : hashAlgorithm === 2 ? 52 : 32
|
|
187
223
|
|
package/rr/nsec3param.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
2
|
import * as TINYDNS from '../lib/tinydns.js'
|
|
3
|
+
import * as BINARY from '../lib/binary.js'
|
|
3
4
|
|
|
4
5
|
export default class NSEC3PARAM extends RR {
|
|
6
|
+
static typeName = 'NSEC3PARAM'
|
|
7
|
+
static typeId = 51
|
|
8
|
+
static RFCs = [5155]
|
|
9
|
+
static rdataFields = ['hash algorithm', 'flags', 'iterations', 'salt']
|
|
10
|
+
static tags = ['dnssec']
|
|
11
|
+
|
|
5
12
|
constructor(opts) {
|
|
6
13
|
super(opts)
|
|
7
14
|
if (opts === null) return
|
|
@@ -57,22 +64,6 @@ export default class NSEC3PARAM extends RR {
|
|
|
57
64
|
return 'Next Secure Parameters'
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
getTags() {
|
|
61
|
-
return ['dnssec']
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getRdataFields(arg) {
|
|
65
|
-
return ['hash algorithm', 'flags', 'iterations', 'salt']
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
getRFCs() {
|
|
69
|
-
return [5155]
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
getTypeId() {
|
|
73
|
-
return 51
|
|
74
|
-
}
|
|
75
|
-
|
|
76
67
|
getCanonical() {
|
|
77
68
|
return {
|
|
78
69
|
owner: 'example.com.',
|
|
@@ -88,62 +79,74 @@ export default class NSEC3PARAM extends RR {
|
|
|
88
79
|
|
|
89
80
|
/****** IMPORTERS *******/
|
|
90
81
|
|
|
91
|
-
fromBind({ bindline }) {
|
|
92
|
-
// test.example.com 3600 IN NSEC3PARAM <hash> <flags> <iterations> <salt>
|
|
93
|
-
// Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
|
|
94
|
-
const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
|
|
95
|
-
return new NSEC3PARAM({
|
|
96
|
-
owner,
|
|
97
|
-
ttl: parseInt(ttl, 10),
|
|
98
|
-
class: c,
|
|
99
|
-
type: type,
|
|
100
|
-
'hash algorithm': parseInt(ha, 10),
|
|
101
|
-
flags: parseInt(flags, 10),
|
|
102
|
-
iterations: parseInt(iterations, 10),
|
|
103
|
-
salt: salt,
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
82
|
fromTinydns({ tinyline }) {
|
|
108
83
|
// RDATA format: Hash Algorithm (3 octal chars) + Flags (3 octal chars) + Iterations (6 octal chars) + Salt (escaped hex string)
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
|
|
84
|
+
const { owner, typeId, rdata, ttl, timestamp, location } = this.parseTinydnsLine(tinyline)
|
|
85
|
+
if (typeId != this.getTypeId()) this.throwHelp('NSEC3PARAM fromTinydns, invalid n')
|
|
86
|
+
if (rdata.length < 4) {
|
|
87
|
+
this.throwHelp(`NSEC3PARAM: RDATA too short: ${rdata}`)
|
|
112
88
|
}
|
|
113
89
|
|
|
114
90
|
// rd may contain actual binary characters (from JS string '\\001' -> char 0x01),
|
|
115
|
-
// so convert via octalToChar and read bytes from a
|
|
116
|
-
const bytes =
|
|
91
|
+
// so convert via octalToChar and read bytes from a Uint8Array for robust parsing.
|
|
92
|
+
const bytes = TINYDNS.octalRdataToBytes(rdata)
|
|
117
93
|
|
|
118
94
|
return new NSEC3PARAM({
|
|
119
|
-
owner
|
|
120
|
-
ttl
|
|
95
|
+
owner,
|
|
96
|
+
ttl,
|
|
121
97
|
type: 'NSEC3PARAM',
|
|
122
|
-
'hash algorithm': bytes
|
|
123
|
-
flags: bytes
|
|
124
|
-
iterations: bytes
|
|
125
|
-
salt: bytes.
|
|
126
|
-
timestamp
|
|
127
|
-
location
|
|
98
|
+
'hash algorithm': bytes[0],
|
|
99
|
+
flags: bytes[1],
|
|
100
|
+
iterations: (bytes[2] << 8) | bytes[3],
|
|
101
|
+
salt: bytes[4] === 0 ? '-' : BINARY.bytesToHex(bytes.subarray(5, 5 + bytes[4])),
|
|
102
|
+
timestamp,
|
|
103
|
+
location,
|
|
128
104
|
})
|
|
129
105
|
}
|
|
130
106
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return
|
|
136
|
-
|
|
107
|
+
fromWire({ owner, cls, ttl, rdata }) {
|
|
108
|
+
const dv = new DataView(rdata.buffer, rdata.byteOffset)
|
|
109
|
+
const saltLen = rdata[4]
|
|
110
|
+
const salt = saltLen === 0 ? '-' : BINARY.bytesToHex(rdata.subarray(5, 5 + saltLen))
|
|
111
|
+
return new NSEC3PARAM({
|
|
112
|
+
owner,
|
|
113
|
+
ttl,
|
|
114
|
+
class: cls,
|
|
115
|
+
type: 'NSEC3PARAM',
|
|
116
|
+
'hash algorithm': rdata[0],
|
|
117
|
+
flags: rdata[1],
|
|
118
|
+
iterations: dv.getUint16(2),
|
|
119
|
+
salt,
|
|
120
|
+
})
|
|
137
121
|
}
|
|
138
122
|
|
|
123
|
+
/****** EXPORTERS *******/
|
|
124
|
+
|
|
139
125
|
toTinydns() {
|
|
140
|
-
const
|
|
126
|
+
const salt = this.get('salt')
|
|
127
|
+
const saltOctal =
|
|
128
|
+
salt === '-' ? TINYDNS.UInt8toOctal(0) : TINYDNS.UInt8toOctal(salt.length / 2) + TINYDNS.packHex(salt)
|
|
141
129
|
|
|
142
130
|
return this.getTinydnsGeneric(
|
|
143
131
|
TINYDNS.UInt8toOctal(this.get('hash algorithm')) +
|
|
144
132
|
TINYDNS.UInt8toOctal(this.get('flags')) +
|
|
145
133
|
TINYDNS.UInt16toOctal(this.get('iterations')) +
|
|
146
|
-
|
|
134
|
+
saltOctal,
|
|
147
135
|
)
|
|
148
136
|
}
|
|
137
|
+
|
|
138
|
+
getWireRdata() {
|
|
139
|
+
const salt = this.get('salt')
|
|
140
|
+
const saltBytes = salt === '-' ? new Uint8Array(0) : BINARY.hexToBytes(salt)
|
|
141
|
+
const bytes = new Uint8Array(4 + 1 + saltBytes.length)
|
|
142
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset)
|
|
143
|
+
|
|
144
|
+
bytes[0] = this.get('hash algorithm')
|
|
145
|
+
bytes[1] = this.get('flags')
|
|
146
|
+
dv.setUint16(2, this.get('iterations'))
|
|
147
|
+
bytes[4] = saltBytes.length
|
|
148
|
+
bytes.set(saltBytes, 5)
|
|
149
|
+
|
|
150
|
+
return bytes
|
|
151
|
+
}
|
|
149
152
|
}
|
package/rr/nxt.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
2
|
|
|
3
3
|
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
|
+
import { DNS_TYPE_IDS } from '../lib/binary.js'
|
|
4
5
|
|
|
5
6
|
export default class NXT extends RR {
|
|
7
|
+
static typeName = 'NXT'
|
|
8
|
+
static typeId = 30
|
|
9
|
+
static RFCs = [2065]
|
|
10
|
+
static rdataFields = ['next domain', 'type bit map']
|
|
11
|
+
static tags = ['obsolete']
|
|
12
|
+
|
|
6
13
|
constructor(opts) {
|
|
7
14
|
super(opts)
|
|
8
15
|
if (opts === null) return
|
|
@@ -29,22 +36,6 @@ export default class NXT extends RR {
|
|
|
29
36
|
return 'Next Secure'
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
getTags() {
|
|
33
|
-
return ['deprecated']
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
getRdataFields(arg) {
|
|
37
|
-
return ['next domain', 'type bit map']
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
getRFCs() {
|
|
41
|
-
return [2065]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getTypeId() {
|
|
45
|
-
return 30
|
|
46
|
-
}
|
|
47
|
-
|
|
48
39
|
getCanonical() {
|
|
49
40
|
return {
|
|
50
41
|
owner: 'big.example.com.',
|
|
@@ -62,7 +53,7 @@ export default class NXT extends RR {
|
|
|
62
53
|
const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
|
|
63
54
|
if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('NXT fromTinydns, invalid n')
|
|
64
55
|
|
|
65
|
-
const binaryRdata =
|
|
56
|
+
const binaryRdata = Uint8Array.from(TINYDNS.octalToChar(rdata), (c) => c.charCodeAt(0))
|
|
66
57
|
const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
|
|
67
58
|
|
|
68
59
|
return new NXT({
|
|
@@ -70,7 +61,7 @@ export default class NXT extends RR {
|
|
|
70
61
|
ttl: parseInt(ttl, 10),
|
|
71
62
|
type: 'NXT',
|
|
72
63
|
'next domain': nextDomain,
|
|
73
|
-
'type bit map': binaryRdata.
|
|
64
|
+
'type bit map': new TextDecoder().decode(binaryRdata.subarray(binaryLen)),
|
|
74
65
|
timestamp: ts,
|
|
75
66
|
location: loc?.trim() ?? '',
|
|
76
67
|
})
|
|
@@ -89,6 +80,19 @@ export default class NXT extends RR {
|
|
|
89
80
|
})
|
|
90
81
|
}
|
|
91
82
|
|
|
83
|
+
fromWire({ owner, cls, ttl, rdata }) {
|
|
84
|
+
const { fqdn: nextDomain, end } = this.wireUnpackDomain(rdata, 0)
|
|
85
|
+
const typeBitMap = nxtBitmapToTypes(rdata.subarray(end))
|
|
86
|
+
return new NXT({
|
|
87
|
+
owner,
|
|
88
|
+
ttl,
|
|
89
|
+
class: cls,
|
|
90
|
+
type: 'NXT',
|
|
91
|
+
'next domain': nextDomain,
|
|
92
|
+
'type bit map': typeBitMap,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
92
96
|
/****** EXPORTERS *******/
|
|
93
97
|
|
|
94
98
|
toTinydns() {
|
|
@@ -98,6 +102,41 @@ export default class NXT extends RR {
|
|
|
98
102
|
TINYDNS.packDomainName(this.get('next domain')) + TINYDNS.escapeOctal(dataRe, this.get('type bit map')),
|
|
99
103
|
)
|
|
100
104
|
}
|
|
105
|
+
|
|
106
|
+
getWireRdata() {
|
|
107
|
+
const nameBytes = this.wirePackDomain(this.get('next domain'))
|
|
108
|
+
const bitmapBytes = typesToNxtBitmap(this.get('type bit map'))
|
|
109
|
+
const result = new Uint8Array(nameBytes.length + bitmapBytes.length)
|
|
110
|
+
result.set(nameBytes)
|
|
111
|
+
result.set(bitmapBytes, nameBytes.length)
|
|
112
|
+
return result
|
|
113
|
+
}
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
const removeParens = (a) => !['(', ')'].includes(a)
|
|
117
|
+
|
|
118
|
+
function nxtBitmapToTypes(bitmap) {
|
|
119
|
+
const DNS_TYPE_NAMES = Object.fromEntries(Object.entries(DNS_TYPE_IDS).map(([k, v]) => [v, k]))
|
|
120
|
+
const types = []
|
|
121
|
+
for (let i = 0; i < bitmap.length; i++) {
|
|
122
|
+
const byte = bitmap[i]
|
|
123
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
124
|
+
if (byte & (0x80 >> bit)) {
|
|
125
|
+
const typeId = i * 8 + bit
|
|
126
|
+
types.push(DNS_TYPE_NAMES[typeId] ?? `TYPE${typeId}`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return types.join(' ')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function typesToNxtBitmap(typeNamesStr) {
|
|
134
|
+
const bitmap = new Uint8Array(16)
|
|
135
|
+
for (const name of typeNamesStr.trim().split(/\s+/)) {
|
|
136
|
+
const id = /^TYPE\d+$/i.test(name) ? parseInt(name.slice(4), 10) : DNS_TYPE_IDS[name.toUpperCase()]
|
|
137
|
+
if (id !== undefined && id < 128) bitmap[Math.floor(id / 8)] |= 0x80 >> (id % 8)
|
|
138
|
+
}
|
|
139
|
+
let len = bitmap.length
|
|
140
|
+
while (len > 0 && bitmap[len - 1] === 0) len--
|
|
141
|
+
return bitmap.slice(0, len)
|
|
142
|
+
}
|
package/rr/openpgpkey.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import RR from '../rr.js'
|
|
2
2
|
import * as TINYDNS from '../lib/tinydns.js'
|
|
3
|
+
import * as BINARY from '../lib/binary.js'
|
|
3
4
|
|
|
4
5
|
export default class OPENPGPKEY extends RR {
|
|
6
|
+
static typeName = 'OPENPGPKEY'
|
|
7
|
+
static typeId = 61
|
|
8
|
+
static RFCs = [4880, 7929]
|
|
9
|
+
static rdataFields = [['public key', 'base64']]
|
|
10
|
+
static tags = ['security']
|
|
11
|
+
|
|
5
12
|
constructor(opts) {
|
|
6
13
|
super(opts)
|
|
7
14
|
}
|
|
8
15
|
|
|
9
16
|
/****** Resource record specific setters *******/
|
|
10
17
|
setPublicKey(val) {
|
|
18
|
+
this.isBase64('OPENPGPKEY', 'public key', val)
|
|
11
19
|
this.set('public key', val)
|
|
12
20
|
}
|
|
13
21
|
|
|
@@ -15,29 +23,14 @@ export default class OPENPGPKEY extends RR {
|
|
|
15
23
|
return 'OpenPGP Public Key'
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
getTags() {
|
|
19
|
-
return ['security']
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
getRdataFields() {
|
|
23
|
-
return ['public key']
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getRFCs() {
|
|
27
|
-
return [4880, 7929]
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
getTypeId() {
|
|
31
|
-
return 61
|
|
32
|
-
}
|
|
33
|
-
|
|
34
26
|
getCanonical() {
|
|
35
27
|
return {
|
|
36
28
|
owner: 'matt.example.com.',
|
|
37
29
|
ttl: 3600,
|
|
38
30
|
class: 'IN',
|
|
39
31
|
type: 'OPENPGPKEY',
|
|
40
|
-
'public key':
|
|
32
|
+
'public key':
|
|
33
|
+
'AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwIXAqcOTiW7iHnQt5hwVAAAAA==',
|
|
41
34
|
}
|
|
42
35
|
}
|
|
43
36
|
|
|
@@ -50,35 +43,36 @@ export default class OPENPGPKEY extends RR {
|
|
|
50
43
|
if (!match) this.throwHelp(`unable to parse OPENPGPKEY: ${bindline}`)
|
|
51
44
|
|
|
52
45
|
const { owner, ttl, class: c, type, publickey } = match.groups
|
|
46
|
+
const keyStr = publickey.trim().replace(/\s+/g, '')
|
|
53
47
|
|
|
54
48
|
return new OPENPGPKEY({
|
|
55
49
|
owner,
|
|
56
50
|
ttl: parseInt(ttl, 10),
|
|
57
51
|
class: c,
|
|
58
52
|
type: type,
|
|
59
|
-
'public key':
|
|
53
|
+
'public key': keyStr,
|
|
60
54
|
})
|
|
61
55
|
}
|
|
62
56
|
|
|
63
57
|
fromTinydns({ tinyline }) {
|
|
64
|
-
const
|
|
58
|
+
const { owner, typeId, rdata, ttl, timestamp, location } = this.parseTinydnsLine(tinyline)
|
|
59
|
+
if (typeId != this.getTypeId()) this.throwHelp('OPENPGPKEY fromTinydns, invalid n')
|
|
65
60
|
return new OPENPGPKEY({
|
|
66
|
-
owner
|
|
67
|
-
ttl
|
|
61
|
+
owner,
|
|
62
|
+
ttl,
|
|
68
63
|
type: 'OPENPGPKEY',
|
|
69
|
-
'public key':
|
|
70
|
-
timestamp
|
|
71
|
-
location
|
|
64
|
+
'public key': TINYDNS.octalToBase64(rdata),
|
|
65
|
+
timestamp,
|
|
66
|
+
location,
|
|
72
67
|
})
|
|
73
68
|
}
|
|
74
69
|
|
|
75
70
|
/****** EXPORTERS *******/
|
|
76
71
|
toTinydns() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)
|
|
82
|
-
return this.getTinydnsGeneric(escapedPublicKey)
|
|
72
|
+
return this.getTinydnsGeneric(TINYDNS.base64toOctal(this.get('public key')))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getWireRdata() {
|
|
76
|
+
return BINARY.base64ToBytes(this.get('public key'))
|
|
83
77
|
}
|
|
84
78
|
}
|