@nictool/dns-resource-record
Version:
DNS Resource Records
194 lines (167 loc) • 4.8 kB
JavaScript
export const SPECIAL_CHARS = {
'+': ['A'],
'-': [undefined], // disabled RR
'%': ['location'],
'.': ['SOA', 'NS', 'A'],
'&': ['NS', 'A'],
'=': ['A', 'PTR'],
'@': ['MX', 'A'],
'#': ['comment'],
"'": ['TXT'],
'^': ['PTR'],
C: ['CNAME'],
Z: ['SOA'],
':': ['generic'],
3: ['AAAA'],
6: ['AAAA', 'PTR'],
S: ['SRV'],
}
const octalRe = new RegExp(/\\(?:[1-7][0-7]{0,2}|[0-7]{2,3})/, 'g')
export function escapeOctal(re, str) {
let escaped = ''
str.split(/(.{1})/g).map((c) => {
escaped += re.test(c) ? charToOctal(c) : c
})
return escaped
}
export function octalToChar(str) {
// relace instances of \NNN with ASCII
return str.replace(octalRe, (o) =>
String.fromCharCode(parseInt(o.substring(1), 8)),
)
}
export function octalToHex(str) {
// relace instances of \NNN with Hex
return str.replace(octalRe, (o) => {
// parseInt(n, 8) -> from octal to decimal
// .toString(16) -> decimal to hex
return parseInt(o.substring(1), 8).toString(16).padStart(2, 0)
})
}
export function octalToUInt8(str) {
const b = Buffer.alloc(1)
b.writeUInt8(parseInt(str.substring(1, 4), 8), 0)
return b.readUInt8()
}
export function octalToUInt16(str) {
const b = Buffer.alloc(2)
b.writeUInt8(parseInt(str.substring(1, 4), 8), 0)
b.writeUInt8(parseInt(str.substring(5, 8), 8), 1)
return b.readUInt16BE()
}
export function octalToUInt32(str) {
const b = Buffer.alloc(4)
b.writeUInt8(parseInt(str.substring(1, 4), 8), 0)
b.writeUInt8(parseInt(str.substring(5, 8), 8), 1)
b.writeUInt8(parseInt(str.substring(9, 12), 8), 2)
b.writeUInt8(parseInt(str.substring(13, 16), 8), 3)
return b.readUInt32BE()
}
export function packString(str) {
return str
.match(/(.{1,255})/g)
.map((s) => {
const len = Buffer.alloc(1)
len.writeUInt8(s.length)
return `${UInt8toOctal(len.readUInt8(0))}${s}`
})
.join('')
}
export function unpackString(str) {
const asBuf = Buffer.from(octalToChar(str.toString()))
const res = []
let pos = 0
let len
while ((len = asBuf.readUInt8(pos))) {
// encoded length byte
pos++
res.push(asBuf.slice(pos, pos + len).toString())
pos = +(pos + len)
if (pos >= asBuf.length) break
}
return res
}
export function packDomainName(fqdn) {
const labelRegEx = new RegExp(/[^A-Za-z0-9-.]/, 'g')
// RFC 1035, 3.3 Standard RRs
// The standard wire format for DNS names. (1 octet length + octets)
let packed = ''
fqdn.split('.').map((label) => {
if (label === undefined || !label.length) return
const len = Buffer.alloc(1)
len.writeUInt8(label.length)
packed += UInt8toOctal(len.readUInt8(0))
packed += escapeOctal(labelRegEx, label)
})
packed += '\\000' // terminates with a zero length label
return packed
}
export function unpackDomainName(fqdn) {
fqdn = Buffer.from(octalToChar(fqdn.toString()))
const labels = []
let pos = 0
let len
while ((len = fqdn.readUInt8(pos))) {
// encoded length byte
pos++
labels.push(fqdn.slice(pos, pos + len).toString())
pos = +(pos + len)
}
const r = `${labels.join('.')}.`
// char position + length of last label + label length chars + null byte
const strLen = pos + len + labels.length * 4 + 1
return [r, strLen]
}
export function packHex(str) {
let r = ''
for (let i = 0; i < str.length; i = i + 2) {
// nibble off 2 hex bytes, encode to octal
r += UInt8toOctal(parseInt(str.slice(i, i + 2), 16))
}
return r
}
export function charToOctal(c) {
if (typeof c === 'number') return UInt8toOctal(c)
return UInt8toOctal(c.charCodeAt(0))
}
export function UInt8toOctal(n) {
if (n > 255) throw new Error('UInt8toOctal does not work on numbers > 255')
return `\\${parseInt(n, 10).toString(8).padStart(3, 0)}`
}
export function UInt16toOctal(n) {
let r = ''
const pri = Buffer.alloc(2)
pri.writeUInt16BE(n)
r += UInt8toOctal(pri.readUInt8(0))
r += UInt8toOctal(pri.readUInt8(1))
return r
}
export function UInt32toOctal(n) {
let r = ''
const pri = Buffer.alloc(4)
pri.writeUInt32BE(n)
for (let i = 0; i < 4; i++) {
r += UInt8toOctal(pri.readUInt8(i))
}
return r
}
export function ipv4toOctal(ip) {
return UInt32toOctal(ip.split`.`.reduce((int, value) => int * 256 + +value))
}
export function octalToIPv4(str) {
const asInt = octalToUInt32(str)
return [24, 16, 8, 0].map((n) => (asInt >> n) & 0xff).join('.')
}
export function base64toOctal(str) {
const bytes = Buffer.from(str, 'base64')
let escaped = ''
for (const b of bytes) {
escaped += /[A-Za-z0-9\-.]/.test(String.fromCharCode(b))
? String.fromCharCode(b)
: UInt8toOctal(b)
}
return escaped
}
export function octalToBase64(str) {
return Buffer.from(octalToChar(str), 'binary').toString('base64')
}