@nictool/dns-resource-record 1.6.1 → 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 +33 -0
- package/README.md +323 -199
- 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 +23 -6
- 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/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const typeMap = {}
|
|
2
|
-
|
|
3
1
|
import RR from './rr.js'
|
|
4
2
|
import A from './rr/a.js'
|
|
5
3
|
import AAAA from './rr/aaaa.js'
|
|
@@ -42,9 +40,8 @@ import TXT from './rr/txt.js'
|
|
|
42
40
|
import URI from './rr/uri.js'
|
|
43
41
|
import WKS from './rr/wks.js'
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export {
|
|
43
|
+
const typeMap = {}
|
|
44
|
+
const classes = [
|
|
48
45
|
A,
|
|
49
46
|
AAAA,
|
|
50
47
|
APL,
|
|
@@ -85,10 +82,15 @@ export {
|
|
|
85
82
|
TXT,
|
|
86
83
|
URI,
|
|
87
84
|
WKS,
|
|
88
|
-
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
for (const c of classes) {
|
|
88
|
+
const id = c.getTypeId()
|
|
89
|
+
typeMap[id] = c.typeName
|
|
90
|
+
typeMap[c.typeName] = id
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
export {
|
|
92
94
|
A,
|
|
93
95
|
AAAA,
|
|
94
96
|
APL,
|
|
@@ -129,8 +131,7 @@ for (const c of [
|
|
|
129
131
|
TXT,
|
|
130
132
|
URI,
|
|
131
133
|
WKS,
|
|
132
|
-
|
|
133
|
-
const id = new c(null).getTypeId()
|
|
134
|
-
typeMap[id] = c.name
|
|
135
|
-
typeMap[c.name] = id
|
|
134
|
+
typeMap,
|
|
136
135
|
}
|
|
136
|
+
|
|
137
|
+
export default RR
|
package/lib/binary.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export function hexToBytes(hex) {
|
|
2
|
+
return Uint8Array.from({ length: hex.length / 2 }, (_, i) => parseInt(hex.slice(i * 2, i * 2 + 2), 16))
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function bytesToHex(bytes) {
|
|
6
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function bytesToBase64(bytes) {
|
|
10
|
+
return btoa([...bytes].map((b) => String.fromCharCode(b)).join(''))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function base64ToBytes(b64) {
|
|
14
|
+
return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// DNS type name → numeric type ID mapping (subset covering known RR types)
|
|
18
|
+
export const DNS_TYPE_IDS = {
|
|
19
|
+
A: 1,
|
|
20
|
+
NS: 2,
|
|
21
|
+
MD: 3,
|
|
22
|
+
MF: 4,
|
|
23
|
+
CNAME: 5,
|
|
24
|
+
SOA: 6,
|
|
25
|
+
MB: 7,
|
|
26
|
+
MG: 8,
|
|
27
|
+
MR: 9,
|
|
28
|
+
NULL: 10,
|
|
29
|
+
WKS: 11,
|
|
30
|
+
PTR: 12,
|
|
31
|
+
HINFO: 13,
|
|
32
|
+
MINFO: 14,
|
|
33
|
+
MX: 15,
|
|
34
|
+
TXT: 16,
|
|
35
|
+
RP: 17,
|
|
36
|
+
AFSDB: 18,
|
|
37
|
+
X25: 19,
|
|
38
|
+
ISDN: 20,
|
|
39
|
+
RT: 21,
|
|
40
|
+
NSAP: 22,
|
|
41
|
+
NSAP_PTR: 23,
|
|
42
|
+
SIG: 24,
|
|
43
|
+
KEY: 25,
|
|
44
|
+
PX: 26,
|
|
45
|
+
GPOS: 27,
|
|
46
|
+
AAAA: 28,
|
|
47
|
+
LOC: 29,
|
|
48
|
+
NXT: 30,
|
|
49
|
+
EID: 31,
|
|
50
|
+
NIMLOC: 32,
|
|
51
|
+
SRV: 33,
|
|
52
|
+
ATMA: 34,
|
|
53
|
+
NAPTR: 35,
|
|
54
|
+
KX: 36,
|
|
55
|
+
CERT: 37,
|
|
56
|
+
A6: 38,
|
|
57
|
+
DNAME: 39,
|
|
58
|
+
SINK: 40,
|
|
59
|
+
OPT: 41,
|
|
60
|
+
APL: 42,
|
|
61
|
+
DS: 43,
|
|
62
|
+
SSHFP: 44,
|
|
63
|
+
IPSECKEY: 45,
|
|
64
|
+
RRSIG: 46,
|
|
65
|
+
NSEC: 47,
|
|
66
|
+
DNSKEY: 48,
|
|
67
|
+
DHCID: 49,
|
|
68
|
+
NSEC3: 50,
|
|
69
|
+
NSEC3PARAM: 51,
|
|
70
|
+
TLSA: 52,
|
|
71
|
+
SMIMEA: 53,
|
|
72
|
+
HIP: 55,
|
|
73
|
+
NINFO: 56,
|
|
74
|
+
RKEY: 57,
|
|
75
|
+
TALINK: 58,
|
|
76
|
+
CDS: 59,
|
|
77
|
+
CDNSKEY: 60,
|
|
78
|
+
OPENPGPKEY: 61,
|
|
79
|
+
CSYNC: 62,
|
|
80
|
+
ZONEMD: 63,
|
|
81
|
+
SVCB: 64,
|
|
82
|
+
HTTPS: 65,
|
|
83
|
+
SPF: 99,
|
|
84
|
+
UINFO: 100,
|
|
85
|
+
UID: 101,
|
|
86
|
+
GID: 102,
|
|
87
|
+
UNSPEC: 103,
|
|
88
|
+
NID: 104,
|
|
89
|
+
L32: 105,
|
|
90
|
+
L64: 106,
|
|
91
|
+
LP: 107,
|
|
92
|
+
EUI48: 108,
|
|
93
|
+
EUI64: 109,
|
|
94
|
+
TKEY: 249,
|
|
95
|
+
TSIG: 250,
|
|
96
|
+
IXFR: 251,
|
|
97
|
+
AXFR: 252,
|
|
98
|
+
MAILB: 253,
|
|
99
|
+
MAILA: 254,
|
|
100
|
+
ANY: 255,
|
|
101
|
+
URI: 256,
|
|
102
|
+
CAA: 257,
|
|
103
|
+
AVC: 258,
|
|
104
|
+
DOA: 259,
|
|
105
|
+
AMTRELAY: 260,
|
|
106
|
+
TA: 32768,
|
|
107
|
+
DLV: 32769,
|
|
108
|
+
}
|
package/lib/bind.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export function parseBindLine(line, RRClasses) {
|
|
2
|
+
const res = {
|
|
3
|
+
class: 'IN',
|
|
4
|
+
type: '',
|
|
5
|
+
rdata: [],
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// 1. Strip comments (not inside quotes)
|
|
9
|
+
let cleanLine = ''
|
|
10
|
+
let inQuote = false
|
|
11
|
+
for (let i = 0; i < line.length; i++) {
|
|
12
|
+
const c = line[i]
|
|
13
|
+
if (c === '"') inQuote = !inQuote
|
|
14
|
+
if (c === ';' && !inQuote) break
|
|
15
|
+
cleanLine += c
|
|
16
|
+
}
|
|
17
|
+
cleanLine = cleanLine.trim()
|
|
18
|
+
if (!cleanLine) return null
|
|
19
|
+
|
|
20
|
+
// 2. Tokenize, respecting quoted strings
|
|
21
|
+
const tokens = cleanLine.match(/(".*?"|\S+)/g) || []
|
|
22
|
+
if (tokens.length < 1) return null
|
|
23
|
+
|
|
24
|
+
// 3. Owner handling
|
|
25
|
+
if (!/^\s/.test(line)) {
|
|
26
|
+
res.owner = tokens.shift()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 4. Extract TTL, Class, and Type
|
|
30
|
+
while (tokens.length > 0) {
|
|
31
|
+
const token = tokens[0].toUpperCase()
|
|
32
|
+
|
|
33
|
+
if (RRClasses[token]) {
|
|
34
|
+
res.class = tokens.shift().toUpperCase()
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (/^\d+$/.test(token)) {
|
|
39
|
+
res.ttl = parseInt(tokens.shift(), 10)
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If it's not a Class or TTL, it must be the RR Type (A, MX, etc.)
|
|
44
|
+
res.type = tokens.shift().toUpperCase()
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 5. Remaining tokens are RDATA
|
|
49
|
+
res.rdata = tokens
|
|
50
|
+
|
|
51
|
+
return res
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function fromBind(rrInstance, opts) {
|
|
55
|
+
const { owner, ttl, cls, rdata } = opts
|
|
56
|
+
const result = {
|
|
57
|
+
owner,
|
|
58
|
+
ttl,
|
|
59
|
+
class: cls,
|
|
60
|
+
type: rrInstance.constructor.typeName,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const fields = rrInstance.getFields('rdata')
|
|
64
|
+
for (let i = 0; i < fields.length; i++) {
|
|
65
|
+
const isLastField = i === fields.length - 1
|
|
66
|
+
if (isLastField && rrInstance.isQuotedField(fields[i])) {
|
|
67
|
+
// Collect all remaining tokens for TXT/etc
|
|
68
|
+
const val = rdata
|
|
69
|
+
.slice(i)
|
|
70
|
+
.map((s) => s.replace(/^"|"$/g, ''))
|
|
71
|
+
.join('')
|
|
72
|
+
result[fields[i]] = val
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let val = rdata[i]
|
|
77
|
+
if (rrInstance.isQuotedField(fields[i])) {
|
|
78
|
+
val = val?.replace(/^"|"$/g, '')
|
|
79
|
+
} else if (/^\d+$/.test(val)) {
|
|
80
|
+
val = parseInt(val, 10)
|
|
81
|
+
}
|
|
82
|
+
result[fields[i]] = val
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return new rrInstance.constructor(result)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function toBind(rrInstance, zone_opts) {
|
|
89
|
+
const fields = rrInstance.getFields('rdata')
|
|
90
|
+
const rdata = fields
|
|
91
|
+
.map((f) => (rrInstance.isFqdnField(f) ? rrInstance.getFQDN(f, zone_opts) : rrInstance.getQuoted(f)))
|
|
92
|
+
.join('\t')
|
|
93
|
+
return `${rrInstance.getPrefix(zone_opts)}\t${rdata}\n`
|
|
94
|
+
}
|
package/lib/dns-query.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createSocket } from 'node:dgram'
|
|
2
|
+
import { createConnection } from 'node:net'
|
|
3
|
+
|
|
4
|
+
export function dnsQueryUDP(host, port, queryBuf, timeout = 5000) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const socket = createSocket('udp4')
|
|
7
|
+
const timer = setTimeout(() => {
|
|
8
|
+
socket.close()
|
|
9
|
+
reject(new Error(`DNS query timed out (${host}:${port})`))
|
|
10
|
+
}, timeout)
|
|
11
|
+
socket.on('message', (msg) => {
|
|
12
|
+
clearTimeout(timer)
|
|
13
|
+
socket.close()
|
|
14
|
+
resolve(msg)
|
|
15
|
+
})
|
|
16
|
+
socket.on('error', (err) => {
|
|
17
|
+
clearTimeout(timer)
|
|
18
|
+
socket.close()
|
|
19
|
+
reject(err)
|
|
20
|
+
})
|
|
21
|
+
socket.send(queryBuf, port, host, (err) => {
|
|
22
|
+
if (err) {
|
|
23
|
+
clearTimeout(timer)
|
|
24
|
+
socket.close()
|
|
25
|
+
reject(err)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function dnsQueryTCP(host, port, queryBuf, timeout = 5000) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const conn = createConnection({ host, port })
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
conn.destroy()
|
|
36
|
+
reject(new Error(`DNS TCP query timed out (${host}:${port})`))
|
|
37
|
+
}, timeout)
|
|
38
|
+
let received = Buffer.alloc(0)
|
|
39
|
+
conn.on('connect', () => {
|
|
40
|
+
const len = Buffer.alloc(2)
|
|
41
|
+
len.writeUInt16BE(queryBuf.length)
|
|
42
|
+
conn.write(Buffer.concat([len, queryBuf]))
|
|
43
|
+
})
|
|
44
|
+
conn.on('data', (chunk) => {
|
|
45
|
+
received = Buffer.concat([received, chunk])
|
|
46
|
+
if (received.length >= 2) {
|
|
47
|
+
const msgLen = received.readUInt16BE(0)
|
|
48
|
+
if (received.length >= 2 + msgLen) {
|
|
49
|
+
clearTimeout(timer)
|
|
50
|
+
conn.destroy()
|
|
51
|
+
resolve(received.slice(2, 2 + msgLen))
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
conn.on('error', (err) => {
|
|
56
|
+
clearTimeout(timer)
|
|
57
|
+
reject(err)
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function dnsQuery(host, port, queryBuf, timeout = 5000) {
|
|
63
|
+
const udpResult = await dnsQueryUDP(host, port, queryBuf, timeout)
|
|
64
|
+
const tc = (udpResult.readUInt16BE(2) >> 9) & 1
|
|
65
|
+
if (tc) return dnsQueryTCP(host, port, queryBuf, timeout)
|
|
66
|
+
return udpResult
|
|
67
|
+
}
|
package/lib/tinydns.js
CHANGED
|
@@ -18,6 +18,84 @@ export const SPECIAL_CHARS = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const octalRe = new RegExp(/\\(?:[1-7][0-7]{0,2}|[0-7]{2,3})/, 'g')
|
|
21
|
+
const textDecoder = new TextDecoder()
|
|
22
|
+
|
|
23
|
+
export function parseFields(tinyline, rdataCount) {
|
|
24
|
+
const parts = tinyline.slice(1).split(':')
|
|
25
|
+
const owner = parts[0]
|
|
26
|
+
const rdata = parts.slice(1, 1 + rdataCount)
|
|
27
|
+
const [ttl, timestamp, location] = parts.slice(1 + rdataCount)
|
|
28
|
+
return { owner, rdata, ttl, timestamp, location: location?.trim() ?? '' }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function parseGenericLine(tinyline) {
|
|
32
|
+
// Generic form: :owner:typeId:rdata:ttl:timestamp:location
|
|
33
|
+
const [owner, typeId, rdata, ttl, timestamp, location] = tinyline.slice(1).split(':')
|
|
34
|
+
return { owner, typeId, rdata, ttl: parseInt(ttl, 10), timestamp, location: location?.trim() ?? '' }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function octalRdataToBytes(rdata) {
|
|
38
|
+
return Uint8Array.from(octalToChar(rdata), (c) => c.charCodeAt(0))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseSvcbLikeRdata(rdata, recordType) {
|
|
42
|
+
if (rdata.length < 6) {
|
|
43
|
+
throw new Error(`${recordType}: RDATA too short: ${rdata}`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const binary = octalRdataToBytes(rdata)
|
|
47
|
+
const priority = (binary[0] << 8) | binary[1]
|
|
48
|
+
|
|
49
|
+
let pos = 2
|
|
50
|
+
const labels = []
|
|
51
|
+
while (true) {
|
|
52
|
+
const len = binary[pos]
|
|
53
|
+
pos += 1
|
|
54
|
+
if (len === 0) break
|
|
55
|
+
labels.push(textDecoder.decode(binary.subarray(pos, pos + len)))
|
|
56
|
+
pos += len
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
priority,
|
|
61
|
+
targetName: `${labels.join('.')}.`,
|
|
62
|
+
params: textDecoder.decode(binary.subarray(pos)),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function to(rrInstance) {
|
|
67
|
+
if (rrInstance.constructor.tinydnsType) {
|
|
68
|
+
const fields = rrInstance.getFields('rdata')
|
|
69
|
+
const rdata = fields
|
|
70
|
+
.map((f) => {
|
|
71
|
+
if (rrInstance.isFqdnField(f)) return rrInstance.getTinyFQDN(f)
|
|
72
|
+
return rrInstance.get(f)
|
|
73
|
+
})
|
|
74
|
+
.join(':')
|
|
75
|
+
return `${rrInstance.constructor.tinydnsType}${rrInstance.getTinyFQDN('owner')}:${rdata}:${rrInstance.getTinydnsPostamble()}\n`
|
|
76
|
+
}
|
|
77
|
+
return rrInstance.getTinydnsGeneric(bytesToOctalString(rrInstance.getWireRdata()))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function fromGeneric(rrInstance, { tinyline }) {
|
|
81
|
+
const fields = rrInstance.getFields('rdata')
|
|
82
|
+
const { owner, rdata, ttl, timestamp, location } = parseFields(tinyline, fields.length)
|
|
83
|
+
|
|
84
|
+
const result = {
|
|
85
|
+
owner: rrInstance.fullyQualify(owner),
|
|
86
|
+
type: rrInstance.constructor.typeName,
|
|
87
|
+
ttl: parseInt(ttl, 10),
|
|
88
|
+
timestamp: timestamp,
|
|
89
|
+
location: location,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < fields.length; i++) {
|
|
93
|
+
const val = rdata[i]
|
|
94
|
+
result[fields[i]] = rrInstance.isFqdnField(fields[i]) ? rrInstance.fullyQualify(val) : val
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return new rrInstance.constructor(result)
|
|
98
|
+
}
|
|
21
99
|
|
|
22
100
|
export function escapeOctal(re, str) {
|
|
23
101
|
let escaped = ''
|
|
@@ -28,7 +106,7 @@ export function escapeOctal(re, str) {
|
|
|
28
106
|
}
|
|
29
107
|
|
|
30
108
|
export function unescapeOctal(str) {
|
|
31
|
-
return
|
|
109
|
+
return octalToChar(str)
|
|
32
110
|
}
|
|
33
111
|
|
|
34
112
|
export function octalToChar(str) {
|
|
@@ -46,47 +124,38 @@ export function octalToHex(str) {
|
|
|
46
124
|
}
|
|
47
125
|
|
|
48
126
|
export function octalToUInt8(str) {
|
|
49
|
-
|
|
50
|
-
b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
|
|
51
|
-
return b.readUInt8()
|
|
127
|
+
return parseInt(str.slice(1, 4), 8) & 0xff
|
|
52
128
|
}
|
|
53
129
|
|
|
54
130
|
export function octalToUInt16(str) {
|
|
55
|
-
|
|
56
|
-
b.writeUInt8(parseInt(str.slice(1, 4), 8), 0)
|
|
57
|
-
b.writeUInt8(parseInt(str.slice(5, 8), 8), 1)
|
|
58
|
-
return b.readUInt16BE()
|
|
131
|
+
return (parseInt(str.slice(1, 4), 8) << 8) | parseInt(str.slice(5, 8), 8)
|
|
59
132
|
}
|
|
60
133
|
|
|
61
134
|
export function octalToUInt32(str) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return b.readUInt32BE()
|
|
135
|
+
const b0 = parseInt(str.slice(1, 4), 8)
|
|
136
|
+
const b1 = parseInt(str.slice(5, 8), 8)
|
|
137
|
+
const b2 = parseInt(str.slice(9, 12), 8)
|
|
138
|
+
const b3 = parseInt(str.slice(13, 16), 8)
|
|
139
|
+
return ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3) >>> 0
|
|
68
140
|
}
|
|
69
141
|
|
|
70
142
|
export function packString(str) {
|
|
71
143
|
return str
|
|
72
144
|
.match(/(.{1,255})/g)
|
|
73
|
-
.map((s) => {
|
|
74
|
-
const len = Buffer.alloc(1)
|
|
75
|
-
len.writeUInt8(s.length)
|
|
76
|
-
return `${UInt8toOctal(len.readUInt8(0))}${s}`
|
|
77
|
-
})
|
|
145
|
+
.map((s) => `${UInt8toOctal(s.length)}${s}`)
|
|
78
146
|
.join('')
|
|
79
147
|
}
|
|
80
148
|
|
|
81
149
|
export function unpackString(str) {
|
|
82
|
-
const asBuf =
|
|
150
|
+
const asBuf = Uint8Array.from(octalToChar(str.toString()), (c) => c.charCodeAt(0))
|
|
151
|
+
const dec = new TextDecoder()
|
|
83
152
|
const res = []
|
|
84
153
|
let pos = 0
|
|
85
154
|
let len
|
|
86
|
-
while ((len = asBuf
|
|
155
|
+
while ((len = asBuf[pos])) {
|
|
87
156
|
// encoded length byte
|
|
88
157
|
pos++
|
|
89
|
-
res.push(asBuf.
|
|
158
|
+
res.push(dec.decode(asBuf.subarray(pos, pos + len)))
|
|
90
159
|
pos = +(pos + len)
|
|
91
160
|
if (pos >= asBuf.length) break
|
|
92
161
|
}
|
|
@@ -102,9 +171,7 @@ export function packDomainName(fqdn) {
|
|
|
102
171
|
fqdn.split('.').forEach((label) => {
|
|
103
172
|
if (label === undefined || !label.length) return
|
|
104
173
|
|
|
105
|
-
|
|
106
|
-
len.writeUInt8(label.length)
|
|
107
|
-
packed += UInt8toOctal(len.readUInt8(0))
|
|
174
|
+
packed += UInt8toOctal(label.length)
|
|
108
175
|
|
|
109
176
|
packed += escapeOctal(labelRegEx, label)
|
|
110
177
|
})
|
|
@@ -163,28 +230,26 @@ export function charToOctal(c) {
|
|
|
163
230
|
}
|
|
164
231
|
|
|
165
232
|
export function UInt8toOctal(n) {
|
|
166
|
-
if (n > 255)
|
|
233
|
+
if (n > 255) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`UInt8toOctal: value ${n} exceeds 255 — tinydns encoders require Latin-1/byte input (code points <= 0xFF)`,
|
|
236
|
+
)
|
|
237
|
+
}
|
|
167
238
|
|
|
168
239
|
return `\\${parseInt(n, 10).toString(8).padStart(3, 0)}`
|
|
169
240
|
}
|
|
170
241
|
|
|
171
242
|
export function UInt16toOctal(n) {
|
|
172
|
-
|
|
173
|
-
const pri = Buffer.alloc(2)
|
|
174
|
-
pri.writeUInt16BE(n)
|
|
175
|
-
r += UInt8toOctal(pri.readUInt8(0))
|
|
176
|
-
r += UInt8toOctal(pri.readUInt8(1))
|
|
177
|
-
return r
|
|
243
|
+
return UInt8toOctal((n >>> 8) & 0xff) + UInt8toOctal(n & 0xff)
|
|
178
244
|
}
|
|
179
245
|
|
|
180
246
|
export function UInt32toOctal(n) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return r
|
|
247
|
+
return (
|
|
248
|
+
UInt8toOctal((n >>> 24) & 0xff) +
|
|
249
|
+
UInt8toOctal((n >>> 16) & 0xff) +
|
|
250
|
+
UInt8toOctal((n >>> 8) & 0xff) +
|
|
251
|
+
UInt8toOctal(n & 0xff)
|
|
252
|
+
)
|
|
188
253
|
}
|
|
189
254
|
|
|
190
255
|
export function ipv4toOctal(ip) {
|
|
@@ -196,8 +261,20 @@ export function octalToIPv4(str) {
|
|
|
196
261
|
return [24, 16, 8, 0].map((n) => (asInt >> n) & 0xff).join('.')
|
|
197
262
|
}
|
|
198
263
|
|
|
264
|
+
export function expandIPv6(val, delimiter = ':') {
|
|
265
|
+
const colons = val.match(/:/g)
|
|
266
|
+
if (colons?.length < 7) {
|
|
267
|
+
val = val.replace(/::/, ':'.repeat(9 - colons.length))
|
|
268
|
+
}
|
|
269
|
+
return val
|
|
270
|
+
.split(':')
|
|
271
|
+
.map((s) => s.padStart(4, '0'))
|
|
272
|
+
.join(delimiter)
|
|
273
|
+
.toLowerCase()
|
|
274
|
+
}
|
|
275
|
+
|
|
199
276
|
export function ipv6toOctal(ip) {
|
|
200
|
-
return packHex(ip
|
|
277
|
+
return packHex(expandIPv6(ip, ''))
|
|
201
278
|
}
|
|
202
279
|
|
|
203
280
|
export function octalToIPv6(str) {
|
|
@@ -207,14 +284,21 @@ export function octalToIPv6(str) {
|
|
|
207
284
|
}
|
|
208
285
|
|
|
209
286
|
export function base64toOctal(str) {
|
|
210
|
-
const
|
|
287
|
+
const binary = atob(str)
|
|
211
288
|
let escaped = ''
|
|
212
|
-
for (
|
|
213
|
-
|
|
289
|
+
for (let i = 0; i < binary.length; i++) {
|
|
290
|
+
const b = binary.charCodeAt(i)
|
|
291
|
+
escaped += /[A-Za-z0-9\-.]/.test(binary[i]) ? binary[i] : UInt8toOctal(b)
|
|
214
292
|
}
|
|
215
293
|
return escaped
|
|
216
294
|
}
|
|
217
295
|
|
|
218
296
|
export function octalToBase64(str) {
|
|
219
|
-
return
|
|
297
|
+
return btoa(octalToChar(str))
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function bytesToOctalString(bytes) {
|
|
301
|
+
let result = ''
|
|
302
|
+
for (const b of bytes) result += UInt8toOctal(b)
|
|
303
|
+
return result
|
|
220
304
|
}
|