bns
Advanced tools
Comparing version 0.0.3 to 0.0.4
158
bin/dig.js
@@ -5,22 +5,124 @@ #!/usr/bin/env node | ||
const pkg = require('../package.json'); | ||
const {StubResolver} = require('../lib/resolver'); | ||
const reverse = process.argv.indexOf('-x'); | ||
const util = require('../lib/util'); | ||
if (reverse !== -1) | ||
process.argv.splice(reverse, 1); | ||
let name = null; | ||
let type = null; | ||
let host = null; | ||
let port = null; | ||
let inet6 = false; | ||
let reverse = false; | ||
let json = false; | ||
let rd = true; | ||
let edns = false; | ||
let dnssec = false; | ||
let debug = false; | ||
function log(obj) { | ||
console.log(obj.toString()); | ||
for (let i = 2; i < process.argv.length; i++) { | ||
const arg = process.argv[i]; | ||
if (arg.length === 0) | ||
throw new Error(`Unexpected argument: ${arg}.`); | ||
switch (arg) { | ||
case '-4': | ||
inet6 = false; | ||
break; | ||
case '-6': | ||
inet6 = true; | ||
break; | ||
case '-x': | ||
reverse = true; | ||
break; | ||
case '-p': | ||
port = util.parseU16(process.argv[i + 1]); | ||
i += 1; | ||
break; | ||
case '-j': | ||
json = true; | ||
break; | ||
case '-q': | ||
name = arg; | ||
break; | ||
case '-t': | ||
type = arg; | ||
break; | ||
case '-h': | ||
case '--help': | ||
case '-?': | ||
case '-v': | ||
console.log(`bns ${pkg.version}`); | ||
process.exit(0); | ||
break; | ||
case '+edns': | ||
edns = true; | ||
break; | ||
case '+noedns': | ||
edns = false; | ||
break; | ||
case '+dnssec': | ||
edns = true; | ||
dnssec = true; | ||
break; | ||
case '+nodnssec': | ||
dnssec = false; | ||
break; | ||
case '+rd': | ||
rd = true; | ||
break; | ||
case '+nord': | ||
rd = false; | ||
break; | ||
case '+debug': | ||
debug = true; | ||
break; | ||
case '+nodebug': | ||
debug = false; | ||
break; | ||
default: | ||
if (arg[0] === '@') { | ||
host = arg.substring(1); | ||
break; | ||
} | ||
if (!name) { | ||
name = arg; | ||
break; | ||
} | ||
if (!type) { | ||
type = arg; | ||
break; | ||
} | ||
throw new Error(`Unexpected argument: ${arg}.`); | ||
} | ||
} | ||
async function resolve(name, type, host, port) { | ||
const resolver = new StubResolver('udp4'); | ||
if (!name) | ||
name = '.'; | ||
resolver.on('log', (...args) => { | ||
console.error(...args); | ||
}); | ||
if (!type) | ||
type = 'A'; | ||
async function resolve(name, type, options) { | ||
const {host, port} = options; | ||
const resolver = new StubResolver(options.inet6 ? 'udp6' : 'udp4'); | ||
resolver.rd = options.rd == null ? true : options.rd; | ||
resolver.edns = Boolean(options.edns); | ||
resolver.dnssec = Boolean(options.dnssec); | ||
resolver.conf.fromSystem(); | ||
if (options.debug) { | ||
resolver.on('log', (...args) => { | ||
console.error(...args); | ||
}); | ||
} | ||
await resolver.open(); | ||
if (reverse !== -1) { | ||
if (options.reverse) { | ||
try { | ||
@@ -41,7 +143,29 @@ return await resolver.reverse(name, port, host); | ||
(async () => { | ||
const name = process.argv[2] || null; | ||
const type = process.argv[3] || null; | ||
const host = process.argv[4] || null; | ||
const port = (process.argv[5] | 0) || null; | ||
log(await resolve(name, type, host, port)); | ||
})(); | ||
const now = Date.now(); | ||
const res = await resolve(name, type, { | ||
host, | ||
port, | ||
inet6, | ||
reverse, | ||
rd, | ||
edns, | ||
dnssec, | ||
debug | ||
}); | ||
const ms = Date.now() - now; | ||
if (json) { | ||
const text = JSON.stringify(res.toJSON(), null, 2); | ||
process.stdout.write(text + '\n'); | ||
} else { | ||
const argv = process.argv.slice(2).join(' '); | ||
process.stdout.write('\n'); | ||
process.stdout.write(`; <<>> bns ${pkg.version} <<>> ${argv}\n`); | ||
process.stdout.write(res.toString(ms) + '\n'); | ||
} | ||
})().catch((err) => { | ||
console.error(err.message); | ||
process.exit(1); | ||
}); |
164
bin/rdig.js
@@ -5,22 +5,129 @@ #!/usr/bin/env node | ||
const pkg = require('../package.json'); | ||
const {RecursiveResolver} = require('../lib/resolver'); | ||
const reverse = process.argv.indexOf('-x'); | ||
const util = require('../lib/util'); | ||
if (reverse !== -1) | ||
process.argv.splice(reverse, 1); | ||
let name = null; | ||
let type = null; | ||
let host = null; | ||
let port = null; | ||
let inet6 = false; | ||
let reverse = false; | ||
let json = false; | ||
let rd = false; | ||
let edns = false; | ||
let dnssec = false; | ||
let nsec3 = false; | ||
let debug = false; | ||
function log(obj) { | ||
console.log(obj.toString()); | ||
for (let i = 2; i < process.argv.length; i++) { | ||
const arg = process.argv[i]; | ||
if (arg.length === 0) | ||
throw new Error(`Unexpected argument: ${arg}.`); | ||
switch (arg) { | ||
case '-4': | ||
inet6 = false; | ||
break; | ||
case '-6': | ||
inet6 = true; | ||
break; | ||
case '-x': | ||
reverse = true; | ||
break; | ||
case '-p': | ||
port = util.parseU16(process.argv[i + 1]); | ||
i += 1; | ||
break; | ||
case '-j': | ||
json = true; | ||
break; | ||
case '-q': | ||
name = arg; | ||
break; | ||
case '-t': | ||
type = arg; | ||
break; | ||
case '-h': | ||
case '--help': | ||
case '-?': | ||
case '-v': | ||
console.log(`bns ${pkg.version}`); | ||
process.exit(0); | ||
break; | ||
case '+edns': | ||
edns = true; | ||
break; | ||
case '+noedns': | ||
edns = false; | ||
break; | ||
case '+dnssec': | ||
edns = true; | ||
dnssec = true; | ||
break; | ||
case '+nodnssec': | ||
dnssec = false; | ||
break; | ||
case '+rd': | ||
rd = true; | ||
break; | ||
case '+nord': | ||
rd = false; | ||
break; | ||
case '+nsec3': | ||
nsec3 = true; | ||
break; | ||
case '+nonsec3': | ||
nsec3 = false; | ||
break; | ||
case '+debug': | ||
debug = true; | ||
break; | ||
case '+nodebug': | ||
debug = false; | ||
break; | ||
default: | ||
if (arg[0] === '@') { | ||
host = arg.substring(1); | ||
break; | ||
} | ||
if (!name) { | ||
name = arg; | ||
break; | ||
} | ||
if (!type) { | ||
type = arg; | ||
break; | ||
} | ||
throw new Error(`Unexpected argument: ${arg}.`); | ||
} | ||
} | ||
async function resolve(name, type, host, port) { | ||
const resolver = new RecursiveResolver('udp4'); | ||
if (!name) | ||
name = '.'; | ||
resolver.on('log', (...args) => { | ||
console.error(...args); | ||
}); | ||
if (!type) | ||
type = 'A'; | ||
async function resolve(name, type, options) { | ||
const resolver = new RecursiveResolver(options.inet6 ? 'udp6' : 'udp4'); | ||
resolver.rd = Boolean(options.rd); | ||
resolver.edns = Boolean(options.edns); | ||
resolver.dnssec = Boolean(options.dnssec); | ||
resolver.nsec3 = Boolean(options.nsec3); | ||
if (options.debug) { | ||
resolver.on('log', (...args) => { | ||
console.error(...args); | ||
}); | ||
} | ||
await resolver.open(); | ||
if (reverse !== -1) { | ||
if (options.reverse) { | ||
try { | ||
@@ -41,7 +148,30 @@ return await resolver.reverse(name); | ||
(async () => { | ||
const name = process.argv[2] || null; | ||
const type = process.argv[3] || null; | ||
const host = process.argv[4] || null; | ||
const port = (process.argv[5] | 0) || null; | ||
log(await resolve(name, type, host, port)); | ||
})(); | ||
const now = Date.now(); | ||
const res = await resolve(name, type, { | ||
host, | ||
port, | ||
inet6, | ||
reverse, | ||
rd, | ||
edns, | ||
dnssec, | ||
nsec3, | ||
debug | ||
}); | ||
const ms = Date.now() - now; | ||
if (json) { | ||
const text = JSON.stringify(res.toJSON(), null, 2); | ||
process.stdout.write(text + '\n'); | ||
} else { | ||
const argv = process.argv.slice(2).join(' '); | ||
process.stdout.write('\n'); | ||
process.stdout.write(`; <<>> bns ${pkg.version} <<>> ${argv}\n`); | ||
process.stdout.write(res.toString(ms) + '\n'); | ||
} | ||
})().catch((err) => { | ||
console.error(err.message); | ||
process.exit(1); | ||
}); |
@@ -9,21 +9,25 @@ /*! | ||
const constants = require('./constants'); | ||
const dnssec = require('./dnssec'); | ||
const encoding = require('./encoding'); | ||
const wire = require('./wire'); | ||
const util = require('./util'); | ||
const dnssec = require('./dnssec'); | ||
const hints = require('./hints'); | ||
const Hosts = require('./hosts'); | ||
const nsec3 = require('./nsec3'); | ||
const ResolvConf = require('./resolvconf'); | ||
const resolver = require('./resolver'); | ||
const server = require('./server'); | ||
const resolver = require('./resolver'); | ||
const hints = require('./hints'); | ||
const util = require('./util'); | ||
const wire = require('./wire'); | ||
exports.constants = constants; | ||
exports.dnssec = dnssec; | ||
exports.encoding = encoding; | ||
exports.wire = wire; | ||
exports.util = util; | ||
exports.dnssec = dnssec; | ||
exports.hints = hints; | ||
exports.Hosts = Hosts; | ||
exports.nsec3 = nsec3; | ||
exports.DNSServer = server.DNSServer; | ||
exports.StubServer = server.StubServer; | ||
exports.RecursiveServer = server.RecursiveServer; | ||
exports.ResolvConf = ResolvConf; | ||
exports.DNSResolver = resolver.DNSResolver; | ||
exports.StubResolver = resolver.StubResolver; | ||
exports.OSResolver = resolver.OSResolver; | ||
exports.RecursiveResolver = resolver.RecursiveResolver; | ||
@@ -33,2 +37,8 @@ exports.Cache = resolver.Cache; | ||
exports.Authority = resolver.Authority; | ||
exports.hints = hints; | ||
exports.DNSServer = server.DNSServer; | ||
exports.StubServer = server.StubServer; | ||
exports.RecursiveServer = server.RecursiveServer; | ||
exports.util = util; | ||
exports.wire = wire; |
@@ -14,2 +14,4 @@ /*! | ||
const assert = require('assert'); | ||
/** | ||
@@ -492,3 +494,117 @@ * Message Opcodes | ||
/** | ||
* Max domain name length. | ||
* @const {Number} | ||
* @default | ||
*/ | ||
const MAX_DOMAIN_LENGTH = 256; | ||
/* | ||
* Helpers | ||
*/ | ||
function toSymbol(value, map, prefix, size) { | ||
assert((value & size) === value); | ||
const symbol = map[value]; | ||
if (typeof symbol === 'string') | ||
return symbol; | ||
return `${prefix}${value.toString(10)}`; | ||
} | ||
function fromSymbol(symbol, name, map, prefix, size) { | ||
assert(typeof symbol === 'string'); | ||
if (symbol.length > 64) | ||
throw new Error(`Unknown ${name}.`); | ||
const value = map[symbol]; | ||
if (typeof value === 'number') | ||
return value; | ||
if (symbol.length <= prefix.length) | ||
throw new Error(`Unknown ${name}: ${symbol}.`); | ||
if (symbol.substring(0, prefix.length) !== prefix) | ||
throw new Error(`Unknown ${name}: ${symbol}.`); | ||
if (symbol.length > prefix.length + size) | ||
throw new Error(`Unknown ${name}.`); | ||
let word = 0; | ||
for (let i = prefix.length; i < symbol.length; i++) { | ||
const ch = symbol.charCodeAt(i) - 0x30; | ||
if (ch < 0 || ch > 9) | ||
throw new Error(`Unknown ${name}: ${symbol}.`); | ||
word *= 10; | ||
word += ch; | ||
} | ||
return word; | ||
} | ||
function opcodeToString(opcode) { | ||
return toSymbol(opcode, opcodesByVal, 'OPCODE', 0x0f); | ||
} | ||
function stringToOpcode(symbol) { | ||
return fromSymbol(symbol, 'opcode', opcodes, 'OPCODE', 2) & 0x0f; | ||
} | ||
function codeToString(code) { | ||
return toSymbol(code, codesByVal, 'RCODE', 0x0f); | ||
} | ||
function stringToCode(symbol) { | ||
return fromSymbol(symbol, 'code', codes, 'RCODE', 2) & 0x0f; | ||
} | ||
function typeToString(type) { | ||
return toSymbol(type, typesByVal, 'TYPE', 0xffff); | ||
} | ||
function stringToType(symbol) { | ||
return fromSymbol(symbol, 'type', types, 'TYPE', 5); | ||
} | ||
function classToString(class_) { | ||
return toSymbol(class_, classesByVal, 'CLASS', 0xffff); | ||
} | ||
function stringToClass(symbol) { | ||
return fromSymbol(symbol, 'class', classes, 'CLASS', 5); | ||
} | ||
function shortToString(class_) { | ||
return toSymbol(class_, shortByVal, 'CLASS', 0xffff); | ||
} | ||
function stringToShort(symbol) { | ||
return fromSymbol(symbol, 'short', short, 'CLASS', 5); | ||
} | ||
function ecodeToString(ecode) { | ||
return toSymbol(ecode, ecodesByVal, 'RCODE', 0xff); | ||
} | ||
function stringToEcode(symbol) { | ||
return fromSymbol(symbol, 'code', ecodes, 'RCODE', 3) & 0xff; | ||
} | ||
function optionToString(option) { | ||
return toSymbol(option, optionsByVal, 'OPTION', 0xffff); | ||
} | ||
function stringToOption(symbol) { | ||
return fromSymbol(symbol, 'option', options, 'OPTION', 5); | ||
} | ||
/* | ||
* Expose | ||
@@ -516,1 +632,17 @@ */ | ||
exports.YEAR68 = YEAR68; | ||
exports.MAX_DOMAIN_LENGTH = MAX_DOMAIN_LENGTH; | ||
exports.opcodeToString = opcodeToString; | ||
exports.stringToOpcode = stringToOpcode; | ||
exports.codeToString = codeToString; | ||
exports.stringToCode = stringToCode; | ||
exports.typeToString = typeToString; | ||
exports.stringToType = stringToType; | ||
exports.classToString = classToString; | ||
exports.stringToClass = stringToClass; | ||
exports.shortToString = shortToString; | ||
exports.stringToShort = stringToShort; | ||
exports.ecodeToString = ecodeToString; | ||
exports.stringToEcode = stringToEcode; | ||
exports.optionToString = optionToString; | ||
exports.stringToOption = stringToOption; |
@@ -495,7 +495,4 @@ /*! | ||
dnssec.verifyRRSIG = function verifyRRSIG(msg, keyMap) { | ||
const sections = [msg.answer]; | ||
const sections = [msg.answer, msg.authority]; | ||
if (msg.aa) | ||
sections.push(msg.authority); | ||
for (const section of sections) { | ||
@@ -511,2 +508,5 @@ if (section.length === 0) | ||
if (rr.type === types.NS) | ||
continue; | ||
set.add(rr.type); | ||
@@ -513,0 +513,0 @@ } |
@@ -219,4 +219,11 @@ /*! | ||
if (socket && socket.pending === 0) | ||
socket.close(); | ||
socket.destroy(); | ||
} | ||
kill(port, host) { | ||
const key = IP.toHost(host, port); | ||
const socket = this.sockets.get(key); | ||
if (socket) | ||
socket.destroy(); | ||
} | ||
} | ||
@@ -293,2 +300,3 @@ | ||
this.connected = false; | ||
this.destroyed = false; | ||
this.parser = new Parser(); | ||
@@ -445,5 +453,6 @@ this.buffered = 0; | ||
destroy() { | ||
if (!this.connected) | ||
if (this.destroyed) | ||
return this; | ||
this.destroyed = true; | ||
this.cleanup(); | ||
@@ -450,0 +459,0 @@ this.socket.destroy(); |
@@ -7,3 +7,3 @@ /*! | ||
* Parts of this software are based on miekg/dns and golang/go: | ||
* https://github.com/miekg/dns/blob/master/dnssec.go | ||
* https://github.com/miekg/dns/blob/master/nsecx.go | ||
* | ||
@@ -10,0 +10,0 @@ * Parts of this software are based on solvere: |
@@ -14,3 +14,3 @@ /*! | ||
const EventEmitter = require('events'); | ||
const IP = require('binet'); | ||
const Heap = require('bheep'); | ||
const encoding = require('./encoding'); | ||
@@ -22,4 +22,5 @@ const wire = require('./wire'); | ||
const {Client} = require('./net'); | ||
const Heap = require('bheep'); | ||
const rootZone = require('./hints'); | ||
const Hosts = require('./hosts'); | ||
const ResolvConf = require('./resolvconf'); | ||
@@ -33,2 +34,4 @@ const { | ||
types, | ||
MAX_DOMAIN_LENGTH, | ||
typeToString, | ||
codes | ||
@@ -43,2 +46,3 @@ } = wire; | ||
randomItem, | ||
equal, | ||
isSubdomain, | ||
@@ -48,37 +52,2 @@ now | ||
/* | ||
* Constants | ||
*/ | ||
const OPENDNS_IPV4 = [ | ||
'208.67.222.222', // resolver1.opendns.com | ||
'208.67.220.220', // resolver2.opendns.com | ||
'208.67.222.220', // resolver3.opendns.com | ||
'208.67.220.222' // resolver4.opendns.com | ||
]; | ||
const OPENDNS_IPV6 = [ | ||
'2620:0:ccc::2', | ||
'2620:0:ccd::2' | ||
]; | ||
const GOOGLE_IPV4 = [ | ||
'8.8.8.8', | ||
'8.8.4.4' | ||
]; | ||
const GOOGLE_IPV6 = [ | ||
'2001:4860:4860::8888', | ||
'2001:4860:4860::8844' | ||
]; | ||
const MAX_DOMAIN_LENGTH = 256; | ||
const MAX_REFERRALS = 20; | ||
const MAX_RETRIES = 5; | ||
const MAX_CACHE_SIZE = 20 << 20; | ||
// Make eslint happy. | ||
OPENDNS_IPV4; | ||
OPENDNS_IPV6; | ||
/** | ||
@@ -96,2 +65,4 @@ * DNSResolver | ||
this.timer = null; | ||
this.maxRetries = 5; | ||
this.rd = false; | ||
this.edns = true; | ||
@@ -132,3 +103,3 @@ this.dnssec = true; | ||
this.socket.setSendBufferSize(4096); | ||
this.timer = setInterval(() => this.timeout(), 5000); | ||
this.timer = setInterval(() => this.timeout(), 1000); | ||
} | ||
@@ -153,7 +124,12 @@ | ||
timeout() { | ||
const time = now(); | ||
const now = Date.now(); | ||
for (const item of this.pending.values()) { | ||
if (time > item.time + 10) | ||
this.retry(item, item.rinfo); | ||
const {id, time, rinfo} = item; | ||
const {address} = rinfo; | ||
if (now > time + 2000) { | ||
this.log('Retrying (%s): %d...', address, id); | ||
this.retry(item, false); | ||
} | ||
} | ||
@@ -166,7 +142,18 @@ } | ||
retry(item, rinfo, tcp) { | ||
if (tcp == null) | ||
tcp = rinfo.tcp; | ||
verify(msg, host, port) { | ||
return true; | ||
} | ||
if (item.retries >= MAX_RETRIES) { | ||
append(msg, host, port) { | ||
return msg; | ||
} | ||
retry(item, forceTCP) { | ||
const {rinfo} = item; | ||
const {port, address} = rinfo; | ||
if (rinfo.tcp) | ||
this.socket.kill(port, address); | ||
if (item.retries >= this.maxRetries) { | ||
this.pending.delete(item.id); | ||
@@ -177,4 +164,7 @@ item.reject(new Error('Request timed out.')); | ||
if (forceTCP) | ||
rinfo.tcp = true; | ||
const {tcp} = rinfo; | ||
const msg = item.req.encode(); | ||
const {port, address} = item.rinfo; | ||
@@ -185,3 +175,3 @@ // Retry over TCP or UDP. | ||
// Update time. | ||
item.time = now(); | ||
item.time = Date.now(); | ||
item.retries += 1; | ||
@@ -198,3 +188,3 @@ } | ||
if (msg.length < 2) { | ||
this.error('Unsolicited msg.'); | ||
this.error(`Malformed message (${address}).`); | ||
return; | ||
@@ -207,3 +197,3 @@ } | ||
if (!item) { | ||
this.error(`Unsolicited msg: ${id}.`); | ||
this.error(`Unsolicited message (${address}): ${id}.`); | ||
return; | ||
@@ -214,3 +204,3 @@ } | ||
|| item.rinfo.port !== port) { | ||
this.error(`Unsolicited msg: ${id}.`); | ||
this.error(`Possible reflection attack (${address}): ${id}.`); | ||
return; | ||
@@ -249,5 +239,5 @@ } | ||
// Retry over TCP. | ||
this.log('Retrying over TCP.'); | ||
this.retry(item, rinfo, true); | ||
// Retry over TCP if truncated. | ||
this.log('Retrying over TCP (%s): %d.', address, id); | ||
this.retry(item, true); | ||
@@ -271,3 +261,4 @@ return; | ||
item.req = req; | ||
this.retry(item, rinfo); | ||
this.log('Retrying without EDNS (%s): %d.', address, id); | ||
this.retry(item, false); | ||
return; | ||
@@ -283,3 +274,4 @@ } | ||
if (res.code === codes.SERVERFAILURE) { | ||
this.retry(item, rinfo); | ||
this.log('Retrying due to failure (%s): %d.', address, id); | ||
this.retry(item, false); | ||
return; | ||
@@ -294,2 +286,8 @@ } | ||
if (!this.verify(msg, address, port)) { | ||
this.pending.delete(id); | ||
item.reject(new Error('Could not verify response.')); | ||
return; | ||
} | ||
this.pending.delete(id); | ||
@@ -309,3 +307,3 @@ | ||
const msg = req.encode(); | ||
const msg = this.append(req.encode(), host, port); | ||
const tcp = msg.length >= 4096; | ||
@@ -325,3 +323,3 @@ | ||
}, | ||
time: now(), | ||
time: Date.now(), | ||
resolve, | ||
@@ -333,11 +331,10 @@ reject | ||
async query(qs, port, host, rd) { | ||
async query(qs, port, host) { | ||
assert(qs instanceof Question); | ||
assert(typeof port === 'number'); | ||
assert(typeof host === 'string'); | ||
assert(typeof rd === 'boolean'); | ||
const req = new Message(); | ||
req.opcode = opcodes.QUERY; | ||
req.rd = rd; | ||
req.rd = this.rd; | ||
req.question.push(qs); | ||
@@ -351,10 +348,10 @@ | ||
async lookup(name, type, port, host, rd) { | ||
async lookup(name, type, port, host) { | ||
const qs = new Question(name, type); | ||
return this.query(qs, port, host, rd); | ||
return this.query(qs, port, host); | ||
} | ||
async reverse(addr, port, host, rd) { | ||
async reverse(addr, port, host) { | ||
const name = encoding.reverse(addr); | ||
return this.lookup(name, types.PTR, port, host, rd); | ||
return this.lookup(name, types.PTR, port, host); | ||
} | ||
@@ -371,18 +368,36 @@ } | ||
super(options); | ||
this.rd = true; | ||
this.conf = new ResolvConf(); | ||
} | ||
getServers() { | ||
return this.conf.getServers(); | ||
} | ||
setServers(servers) { | ||
this.conf.setServers(servers); | ||
return this; | ||
} | ||
randomServer() { | ||
return this.conf.randomServer(this.inet6); | ||
} | ||
async resolve(qs, port, host) { | ||
if (host == null) { | ||
const addr = this.randomServer(); | ||
host = addr.host; | ||
if (port == null) | ||
port = addr.port; | ||
} | ||
if (port == null) | ||
port = 53; | ||
if (host == null) { | ||
host = randomItem(this.inet6 ? GOOGLE_IPV6 : GOOGLE_IPV4); | ||
host = IP.normalize(host); | ||
port = 53; | ||
} | ||
assert(typeof port === 'number'); | ||
assert(typeof host === 'string'); | ||
return this.query(qs, port, host, true, null); | ||
return this.query(qs, port, host); | ||
} | ||
@@ -395,3 +410,3 @@ | ||
async reverse(addr, port, host, rd) { | ||
async reverse(addr, port, host) { | ||
const name = encoding.reverse(addr); | ||
@@ -403,2 +418,45 @@ return this.lookup(name, types.PTR, port, host); | ||
/** | ||
* OSResolver | ||
* @extends StubResolver | ||
*/ | ||
class OSResolver extends StubResolver { | ||
constructor(options) { | ||
super(options); | ||
this.conf = ResolvConf.fromSystem(); | ||
this.hosts = Hosts.fromSystem(); | ||
} | ||
async query(qs, port, host) { | ||
assert(qs instanceof Question); | ||
assert(typeof port === 'number'); | ||
assert(typeof host === 'string'); | ||
const {name, type} = qs; | ||
const answer = this.hosts.query(name, type); | ||
if (answer) { | ||
const res = new Message(); | ||
res.id = (Math.random() * 0x10000) >>> 0; | ||
res.opcode = opcodes.QUERY; | ||
res.code = codes.NOERROR; | ||
res.qr = true; | ||
res.rd = true; | ||
res.ra = true; | ||
res.ad = true; | ||
res.question = [qs]; | ||
res.answer = answer; | ||
if (this.edns) | ||
res.setEDNS(4096, this.dnssec); | ||
return res; | ||
} | ||
return super.query(qs, port, host); | ||
} | ||
} | ||
/** | ||
* Hints | ||
@@ -539,2 +597,3 @@ */ | ||
super(options); | ||
this.rd = false; | ||
this.cache = new Cache(); | ||
@@ -554,7 +613,7 @@ this.hints = Hints.fromZone(rootZone); | ||
if (cache) { | ||
this.log('Cache hit for %s/%d.', qs.name, qs.type); | ||
this.log('Cache hit for %s/%s.', qs.name, typeToString(qs.type)); | ||
return [cache, true]; | ||
} | ||
const res = await this.query(qs, port, host, false); | ||
const res = await this.query(qs, port, host); | ||
@@ -632,11 +691,17 @@ return [res, false]; | ||
if (ds.length === 0) | ||
throw new Error('Invalid DNSKEY (DS absent).'); | ||
if (ds.length === 0) { | ||
this.log('Invalid DNSKEY (DS absent).'); | ||
return null; | ||
} | ||
if (!hit) { | ||
if (!dnssec.verifyDS(keyMap, ds)) | ||
throw new Error('Invalid DNSKEY (DS mismatch).'); | ||
if (!dnssec.verifyDS(keyMap, ds)) { | ||
this.log('Invalid DNSKEY (DS mismatch).'); | ||
return null; | ||
} | ||
if (!dnssec.verifyRRSIG(res, keyMap)) | ||
throw new Error('Invalid RRSIG.'); | ||
if (!dnssec.verifyRRSIG(res, keyMap)) { | ||
this.log('Invalid RRSIG (keys).'); | ||
return null; | ||
} | ||
} | ||
@@ -659,8 +724,15 @@ | ||
if (keyMap.size === 0) | ||
throw new Error('No DNSKEY found.'); | ||
if (!keyMap) | ||
return false; | ||
if (!dnssec.verifyRRSIG(msg, keyMap)) | ||
if (keyMap.size === 0) { | ||
this.log('No DNSKEY found.'); | ||
return false; | ||
} | ||
if (!dnssec.verifyRRSIG(msg, keyMap)) { | ||
this.log('Invalid RRSIG.'); | ||
return false; | ||
} | ||
this.log('Validated DNSSEC signatures.'); | ||
@@ -677,7 +749,7 @@ | ||
if (rr.type === types.NS) | ||
nsmap.set(rr.data.ns, rr.name); | ||
nsmap.set(rr.data.ns.toLowerCase(), rr.name.toLowerCase()); | ||
} | ||
for (const rr of additional) { | ||
const zone = nsmap.get(rr.name); | ||
const zone = nsmap.get(rr.name.toLowerCase()); | ||
@@ -876,2 +948,8 @@ if (!zone) | ||
if (this.nsec3 && rc.chain && rc.res.code === codes.NXDOMAIN) { | ||
const nsec = extractSet(rc.res.authority, '', types.NSEC3); | ||
if (!nsec3.verifyNameError(rc.qs, nsec)) | ||
throw new Error('NSEC missing coverage'); | ||
} | ||
if (rc.res.answer.length > 0 | ||
@@ -890,8 +968,2 @@ && (rc.res.code === codes.NOERROR | ||
if (this.nsec3 && rc.chain && rc.res.code === codes.NXDOMAIN) { | ||
const nsec = extractSet(rc.res.authority, '', types.NSEC3); | ||
if (!nsec3.verifyNameError(rc.qs, nsec)) | ||
throw new Error('NSEC missing coverage'); | ||
} | ||
return false; | ||
@@ -901,3 +973,3 @@ } | ||
async iterate(rc) { | ||
this.log('Querying %s/%d.', rc.qs.name, rc.qs.type); | ||
this.log('Querying %s/%s.', rc.qs.name, typeToString(rc.qs.type)); | ||
@@ -909,6 +981,10 @@ for (;;) { | ||
assert(rc.hops <= MAX_REFERRALS); | ||
assert(rc.hops <= rc.maxReferrals); | ||
this.log('Traversed zones: %s for %s/%d.', | ||
rc.zones.join(', '), rc.question.name, rc.question.type); | ||
this.log( | ||
'Traversed zones: %s for %s/%s.', | ||
rc.zones.join(', '), | ||
rc.question.name, | ||
typeToString(rc.question.type) | ||
); | ||
@@ -945,3 +1021,4 @@ if (rc.res.answer.length > 0) { | ||
this.log('Finishing resolving %s/%d (hops=%d).', qs.name, qs.type, rc.hops); | ||
this.log('Finishing resolving %s/%s (hops=%d).', | ||
qs.name, typeToString(qs.type), rc.hops); | ||
@@ -993,2 +1070,3 @@ return rc.toAnswer(); | ||
this.hit = false; | ||
this.maxReferrals = 20; | ||
this.switchZone(ns); | ||
@@ -1015,3 +1093,3 @@ } | ||
hop() { | ||
if (this.hops >= MAX_REFERRALS) | ||
if (this.hops >= this.maxReferrals) | ||
throw new Error('Maximum referrals exceeded.'); | ||
@@ -1026,7 +1104,8 @@ | ||
res.id = this.res.id; | ||
res.opcode = this.res.opcode; | ||
res.code = this.res.code; | ||
res.qr = true; | ||
res.opcode = this.res.opcode; | ||
res.ra = true; | ||
res.ad = this.chain; | ||
res.code = this.res.code; | ||
res.question = [this.question]; | ||
@@ -1036,2 +1115,3 @@ res.answer = this.res.answer.slice(); | ||
res.additional = this.res.additional.slice(); | ||
res.edns = this.res.edns.clone(); | ||
@@ -1051,2 +1131,3 @@ return res; | ||
this.size = 0; | ||
this.maxSize = 5 << 20; | ||
} | ||
@@ -1072,3 +1153,3 @@ | ||
prune() { | ||
while (this.size > MAX_CACHE_SIZE) { | ||
while (this.size > this.maxSize) { | ||
const [id, deadline] = this.queue.shift(); | ||
@@ -1195,11 +1276,23 @@ const entry = this.get(id); | ||
function collapseChain(qname, records) { | ||
function collapseChain(name, records) { | ||
const chased = []; | ||
const map = new Map(); | ||
const sigs = new Map(); | ||
for (const rr of records) { | ||
if (rr.type === types.CNAME) | ||
map.set(rr.name, rr); | ||
const rd = rr.data; | ||
if (rr.type === types.CNAME) { | ||
map.set(rr.name.toLowerCase(), rr); | ||
continue; | ||
} | ||
if (rr.type === types.RRSIG) { | ||
if (rd.typeCovered === types.CNAME) | ||
sigs.set(rr.name.toLowerCase(), rr); | ||
continue; | ||
} | ||
} | ||
let qname = name.toLowerCase(); | ||
let canonical = ''; | ||
@@ -1209,2 +1302,3 @@ | ||
const cname = map.get(qname); | ||
const sig = sigs.get(qname); | ||
@@ -1215,4 +1309,8 @@ if (!cname) | ||
canonical = cname.data.target; | ||
qname = canonical; | ||
qname = canonical.toLowerCase(); | ||
chased.push(cname); | ||
if (sig) | ||
chased.push(sig); | ||
} | ||
@@ -1230,6 +1328,10 @@ | ||
if (rrs.length > 1) { | ||
if (!hasAll(rrs, types.CNAME) || qs.type === types.CNAME) | ||
if (!hasAll(rrs, types.CNAME) | ||
|| qs.type === types.CNAME) { | ||
return ['', null]; | ||
const [sname, chased] = collapseChain(qs.name, rrs); | ||
return [sname, chased]; | ||
} | ||
const [alias, chased] = collapseChain(qs.name, answer); | ||
return [alias, chased]; | ||
} | ||
@@ -1242,6 +1344,10 @@ | ||
case types.CNAME: { | ||
if (qs.type === types.CNAME || qs.name !== rr.name) | ||
if (qs.type === types.CNAME | ||
|| !equal(qs.name, rr.name)) { | ||
return ['', null]; | ||
return [rd.target, rrs]; | ||
} | ||
return [rd.target, answer]; | ||
} | ||
case types.DNAME: { | ||
@@ -1255,8 +1361,8 @@ if (qs.type === types.DNAME) | ||
const bottom = qs.name.slice(0, -rr.name.length); | ||
const sname = bottom + rd.target; | ||
const alias = bottom + rd.target; | ||
if (sname.length > MAX_DOMAIN_LENGTH) | ||
if (alias.length > MAX_DOMAIN_LENGTH) | ||
throw new Error('DNAME too long.'); | ||
return [sname, rrs]; | ||
return [alias, answer]; | ||
} | ||
@@ -1316,6 +1422,6 @@ } | ||
if (util.equal(rr.name, name)) | ||
if (equal(rr.name, name)) | ||
continue; | ||
if (util.isSubdomain(rr.name, name)) | ||
if (isSubdomain(rr.name, name)) | ||
continue; | ||
@@ -1338,2 +1444,3 @@ | ||
exports.StubResolver = StubResolver; | ||
exports.OSResolver = OSResolver; | ||
exports.RecursiveResolver = RecursiveResolver; |
@@ -19,4 +19,13 @@ /*! | ||
const encoding = require('./encoding'); | ||
const {types, typesByVal, YEAR68, options} = require('./constants'); | ||
const constants = require('./constants'); | ||
const { | ||
types, | ||
YEAR68, | ||
options, | ||
typeToString, | ||
stringToType, | ||
typesByVal | ||
} = constants; | ||
/* | ||
@@ -27,3 +36,3 @@ * Schemas | ||
const UNKNOWNSchema = [ | ||
['data', 'hex'] | ||
['data', 'hex-end'] | ||
]; | ||
@@ -78,3 +87,3 @@ | ||
['protocol', 'u8'], | ||
['bitmap', 'hex'] // ?? | ||
['bitmap', 'hex-end'] // ?? | ||
]; | ||
@@ -130,3 +139,3 @@ | ||
const NSAPSchema = [ | ||
['nsap', 'hex'] // ?? | ||
['nsap', 'hex-end'] // ?? | ||
]; | ||
@@ -145,3 +154,3 @@ | ||
['signerName', 'name'], | ||
['signature', 'base64'] | ||
['signature', 'base64-end'] | ||
]; | ||
@@ -153,3 +162,3 @@ | ||
['algorithm', 'u8'], | ||
['publicKey', 'base64'] | ||
['publicKey', 'base64-end'] | ||
]; | ||
@@ -185,11 +194,11 @@ | ||
['nextDomain', 'name'], | ||
['typeBitmap', 'nsec3'] | ||
['typeBitmap', 'nsec'] | ||
]; | ||
const EIDSchema = [ | ||
['endpoint', 'hex'] | ||
['endpoint', 'hex-end'] | ||
]; | ||
const NIMLOCSchema = [ | ||
['locator', 'hex'] | ||
['locator', 'hex-end'] | ||
]; | ||
@@ -206,3 +215,3 @@ | ||
['format', 'u8'], | ||
['address', 'hex'] // ?? | ||
['address', 'hex-end'] // ?? | ||
]; | ||
@@ -228,3 +237,3 @@ | ||
['algorithm', 'u8'], | ||
['certificate', 'base64'] | ||
['certificate', 'base64-end'] | ||
]; | ||
@@ -245,3 +254,3 @@ | ||
['n', 'u8'], | ||
['afd', 'hex'] // ?? | ||
['afd', 'hex-end'] // ?? | ||
]; | ||
@@ -253,3 +262,3 @@ | ||
['digestType', 'u8'], | ||
['digest', 'hex'] | ||
['digest', 'hex-end'] | ||
]; | ||
@@ -260,3 +269,3 @@ | ||
['keyType', 'u8'], | ||
['fingerprint', 'hex'] | ||
['fingerprint', 'hex-end'] | ||
]; | ||
@@ -269,3 +278,3 @@ | ||
['target', 'string'], | ||
['publicKey', 'base64'] | ||
['publicKey', 'base64-end'] | ||
]; | ||
@@ -277,3 +286,3 @@ | ||
['nextDomain', 'name'], | ||
['typeBitmap', 'nsec3'] | ||
['typeBitmap', 'nsec'] | ||
]; | ||
@@ -284,3 +293,3 @@ | ||
const DHCIDSchema = [ | ||
['digest', 'base64'] | ||
['digest', 'base64-end'] | ||
]; | ||
@@ -294,3 +303,3 @@ | ||
['nextDomain', 'base32'], | ||
['typeBitmap', 'nsec3'] | ||
['typeBitmap', 'nsec'] | ||
]; | ||
@@ -309,3 +318,3 @@ | ||
['matchingType', 'u8'], | ||
['certificate', 'hex'] | ||
['certificate', 'hex-end'] | ||
]; | ||
@@ -319,7 +328,7 @@ | ||
['publicKey', 'base64'], | ||
['rendezvousServers', 'names'] | ||
['rendezvousServers', 'servers'] | ||
]; | ||
const NINFOSchema = [ | ||
['zsData', 'stxt'] | ||
['zsData', 'txt'] | ||
]; | ||
@@ -339,3 +348,3 @@ | ||
const OPENPGPKEYSchema = [ | ||
['publicKey', 'base64'] | ||
['publicKey', 'base64-end'] | ||
]; | ||
@@ -346,3 +355,3 @@ | ||
['flags', 'u16'], | ||
['typeBitmap', 'nsec3'] | ||
['typeBitmap', 'nsec'] | ||
]; | ||
@@ -410,4 +419,4 @@ | ||
['algorithm', 'name'], | ||
['inception', 'u32'], // time? | ||
['expiration', 'u32'], // time? | ||
['inception', 'u32'], | ||
['expiration', 'u32'], | ||
['mode', 'u16'], | ||
@@ -421,4 +430,4 @@ ['error', 'u16'], | ||
['algorithm', 'name'], | ||
['timeSigned', 'u48'], // time? | ||
['fudge', 'u16'], // time? | ||
['timeSigned', 'u48'], | ||
['fudge', 'u16'], | ||
['mac', 'hex'], | ||
@@ -436,3 +445,3 @@ ['origID', 'u16'], | ||
['digestType', 'u8'], | ||
['digest', 'hex'] | ||
['digest', 'hex-end'] | ||
]; | ||
@@ -455,7 +464,7 @@ | ||
const NSIDSchema = [ | ||
['nsid', 'hex'] | ||
['nsid', 'hex-end'] | ||
]; | ||
const DAUSchema = [ | ||
['algCode', 'hex'] | ||
['algCode', 'hex-end'] | ||
]; | ||
@@ -479,3 +488,3 @@ | ||
const COOKIESchema = [ | ||
['cookie', 'hex'] | ||
['cookie', 'hex-end'] | ||
]; | ||
@@ -489,7 +498,7 @@ | ||
const PADDINGSchema = [ | ||
['padding', 'hex'] | ||
['padding', 'hex-end'] | ||
]; | ||
const LOCALSchema = [ | ||
['data', 'hex'] | ||
['data', 'hex-end'] | ||
]; | ||
@@ -638,6 +647,8 @@ | ||
switch (type) { | ||
case 'base64': | ||
case 'names': | ||
case 'nsec3': | ||
case 'txt': { | ||
case 'hex-end': | ||
case 'base64-end': | ||
case 'servers': | ||
case 'nsec': | ||
case 'txt': | ||
case 'octet': { | ||
const left = parts.slice(i).join(' '); | ||
@@ -679,3 +690,3 @@ rd[name] = readType(type, left); | ||
} | ||
case 'names': { | ||
case 'servers': { | ||
const names = part.split(' '); | ||
@@ -705,2 +716,8 @@ for (const name of names) | ||
} | ||
case 'hex-end': { | ||
const hex = part.replace(/\s+/g, ''); | ||
const data = Buffer.from(hex, 'hex'); | ||
assert(data.length === (hex.length >>> 1)); | ||
return data; | ||
} | ||
case 'base32': { | ||
@@ -710,3 +727,7 @@ return base32.decodeHex(part); | ||
case 'base64': { | ||
const b64 = part.replace(/ +/, ''); | ||
assert(/^[A-Za-z0-9+\/=]+$/.test(part)); | ||
return Buffer.from(part, 'base64'); | ||
} | ||
case 'base64-end': { | ||
const b64 = part.replace(/\s+/g, ''); | ||
assert(/^[A-Za-z0-9+\/=]+$/.test(b64)); | ||
@@ -749,11 +770,8 @@ return Buffer.from(b64, 'base64'); | ||
} | ||
case 'nsec3': { | ||
case 'nsec': { | ||
const tns = part.split(' '); | ||
const ts = []; | ||
for (const tn of tns) { | ||
const t = types[tn]; | ||
assert(t != null); | ||
ts.push(t); | ||
} | ||
for (const tn of tns) | ||
ts.push(stringToType(tn)); | ||
@@ -766,53 +784,18 @@ return encoding.toBitmap(ts); | ||
case 'type': { | ||
const type = types[part]; | ||
assert(type != null); | ||
return type; | ||
return stringToType(part); | ||
} | ||
case 'u8': { | ||
const n = parseInt(part, 10); | ||
assert((n & 0xff) === n); | ||
return n; | ||
return util.parseU8(part); | ||
} | ||
case 'u16': { | ||
const n = parseInt(part, 10); | ||
assert((n & 0xffff) === n); | ||
return n; | ||
return util.parseU16(part); | ||
} | ||
case 'u32': { | ||
const n = parseInt(part, 10); | ||
assert((n >>> 0) === n); | ||
return n; | ||
return util.parseU32(part); | ||
} | ||
case 'u48': { | ||
const n = parseInt(part, 10); | ||
assert(n >= 0 && n <= 0xffffffffffff); | ||
return n; | ||
return util.parseU48(part); | ||
} | ||
case 'u64': { | ||
let hi = 0; | ||
let lo = 0; | ||
for (let i = 0; i < part.length; i++) { | ||
let ch = part.charCodeAt(i); | ||
if (ch < 0x30 || ch > 0x39) | ||
throw new Error('Invalid string (parse error).'); | ||
ch -= 0x30; | ||
lo *= 10; | ||
lo += ch; | ||
hi *= 10; | ||
if (lo > 0xffffffff) { | ||
ch = lo % 0x100000000; | ||
hi += (lo - ch) / 0x100000000; | ||
lo = ch; | ||
} | ||
if (hi > 0xffffffff) | ||
throw new Error('Invalid string (overflow).'); | ||
} | ||
const [hi, lo] = util.parseU64(part); | ||
const out = Buffer.allocUnsafe(8); | ||
@@ -823,18 +806,2 @@ out.writeUInt32BE(hi, true); | ||
} | ||
case 'bool': { | ||
assert(part === '0' || part === '1'); | ||
return part === '1'; | ||
} | ||
case 'nodes': { | ||
const parts = part.split(':'); | ||
const nodes = []; | ||
for (const part of parts) { | ||
const data = Buffer.from(part, 'hex'); | ||
assert(data.length === (part.length >>> 1)); | ||
nodes.push(data); | ||
} | ||
return nodes; | ||
} | ||
default: { | ||
@@ -852,3 +819,3 @@ throw new Error('Unknown type.'); | ||
} | ||
case 'names': { | ||
case 'servers': { | ||
assert(Array.isArray(value)); | ||
@@ -867,10 +834,26 @@ return value.join(' '); | ||
assert(Buffer.isBuffer(value)); | ||
return value.toString('hex'); | ||
return value.toString('hex').toUpperCase(); | ||
} | ||
case 'hex-end': { | ||
assert(Buffer.isBuffer(value)); | ||
const hex = value.toString('hex').toUpperCase(); | ||
const out = []; | ||
for (let i = 0; i < hex.length; i += 56) | ||
out.push(hex.substring(i, i + 56)); | ||
return out.join(' '); | ||
} | ||
case 'base32': { | ||
assert(Buffer.isBuffer(value)); | ||
return base32.encodeHex(value); | ||
return base32.encodeHex(value).toUpperCase(); | ||
} | ||
case 'base64': { | ||
assert(Buffer.isBuffer(value)); | ||
return value.toString('base64'); | ||
} | ||
case 'base64-end': { | ||
assert(Buffer.isBuffer(value)); | ||
const b64 = value.toString('base64'); | ||
@@ -894,8 +877,11 @@ const out = []; | ||
assert(Array.isArray(value)); | ||
const out = []; | ||
for (const str of value) | ||
out.push(JSON.stringify(str)); | ||
return out.join(' '); | ||
} | ||
case 'nsec3': { | ||
case 'nsec': { | ||
assert(Buffer.isBuffer(value)); | ||
@@ -907,5 +893,4 @@ | ||
for (const t of ts) { | ||
const tn = typesByVal[t]; | ||
if (tn != null) | ||
tns.push(tn); | ||
if (typesByVal[t]) | ||
tns.push(typeToString(t)); | ||
} | ||
@@ -916,23 +901,22 @@ | ||
case 'time': { | ||
assert(typeof value === 'number'); | ||
return timeToString(value); | ||
} | ||
case 'type': { | ||
assert(typeof value === 'number'); | ||
return typesByVal[value] || 'UNKNOWN'; | ||
return typeToString(value); | ||
} | ||
case 'u8': { | ||
assert(typeof value === 'number'); | ||
assert((value & 0xff) === value); | ||
return value.toString(10); | ||
} | ||
case 'u16': { | ||
assert(typeof value === 'number'); | ||
assert((value & 0xffff) === value); | ||
return value.toString(10); | ||
} | ||
case 'u32': { | ||
assert(typeof value === 'number'); | ||
assert((value >>> 0) === value); | ||
return value.toString(10); | ||
} | ||
case 'u48': { | ||
assert(typeof value === 'number'); | ||
assert(Number.isSafeInteger(value)); | ||
assert(value >= 0 && value <= 0xffffffffffff); | ||
return value.toString(10); | ||
@@ -942,36 +926,6 @@ } | ||
assert(Buffer.isBuffer(value) && value.length === 8); | ||
let hi = value.readUInt32BE(0, true); | ||
let lo = value.readUInt32BE(4, true); | ||
let str = ''; | ||
do { | ||
const mhi = hi % 10; | ||
hi -= mhi; | ||
hi /= 10; | ||
lo += mhi * 0x100000000; | ||
const mlo = lo % 10; | ||
lo -= mlo; | ||
lo /= 10; | ||
let ch = mlo; | ||
ch += 0x30; | ||
str = String.fromCharCode(ch) + str; | ||
} while (lo > 0 || hi > 0); | ||
return str; | ||
const hi = value.readUInt32BE(0, true); | ||
const lo = value.readUInt32BE(4, true); | ||
return util.serializeU64(hi, lo); | ||
} | ||
case 'bool': { | ||
assert(typeof value === 'boolean'); | ||
return value ? '1' : '0'; | ||
} | ||
case 'nodes': { | ||
const out = []; | ||
for (const node of value) | ||
out.push(node.toString('hex')); | ||
return out.join(':'); | ||
} | ||
default: { | ||
@@ -1017,3 +971,3 @@ throw new Error('Unknown type.'); | ||
} | ||
case 'names': { | ||
case 'servers': { | ||
assert(Array.isArray(value)); | ||
@@ -1041,3 +995,4 @@ const names = []; | ||
} | ||
case 'hex': { | ||
case 'hex': | ||
case 'hex-end': { | ||
assert(typeof value === 'string'); | ||
@@ -1052,3 +1007,4 @@ const data = Buffer.from(value, 'hex'); | ||
} | ||
case 'base64': { | ||
case 'base64': | ||
case 'base64-end': { | ||
assert(typeof value === 'string'); | ||
@@ -1078,3 +1034,3 @@ assert(/^[A-Za-z0-9+\/=]+$/.test(value)); | ||
} | ||
case 'nsec3': { | ||
case 'nsec': { | ||
return encoding.toBitmap(value); | ||
@@ -1086,5 +1042,3 @@ } | ||
case 'type': { | ||
const type = types[value]; | ||
assert(type != null); | ||
return type; | ||
return stringToType(value); | ||
} | ||
@@ -1104,4 +1058,4 @@ case 'u8': { | ||
case 'u48': { | ||
assert(Number.isSafeInteger(value)); | ||
assert(value >= 0 && value <= 0xffffffffffff); | ||
assert((value % 1) === 0); | ||
return value; | ||
@@ -1116,17 +1070,2 @@ } | ||
} | ||
case 'bool': { | ||
assert(typeof value === 'boolean'); | ||
return value; | ||
} | ||
case 'nodes': { | ||
assert(Array.isArray(value)); | ||
const nodes = []; | ||
for (const item of value) { | ||
assert(typeof item === 'string'); | ||
const data = Buffer.from(item, 'hex'); | ||
assert(data.length === (item.length >>> 1)); | ||
nodes.push(data); | ||
} | ||
return nodes; | ||
} | ||
default: { | ||
@@ -1144,3 +1083,3 @@ throw new Error('Unknown type.'); | ||
} | ||
case 'names': { | ||
case 'servers': { | ||
assert(Array.isArray(value)); | ||
@@ -1157,3 +1096,4 @@ return value; | ||
} | ||
case 'hex': { | ||
case 'hex': | ||
case 'hex-end': { | ||
assert(Buffer.isBuffer(value)); | ||
@@ -1166,3 +1106,4 @@ return value.toString('hex'); | ||
} | ||
case 'base64': { | ||
case 'base64': | ||
case 'base64-end': { | ||
assert(Buffer.isBuffer(value)); | ||
@@ -1183,3 +1124,3 @@ return value.toString('base64'); | ||
} | ||
case 'nsec3': { | ||
case 'nsec': { | ||
assert(Buffer.isBuffer(value)); | ||
@@ -1194,18 +1135,19 @@ return encoding.fromBitmap(value); | ||
assert(typeof value === 'number'); | ||
return typesByVal[value] || 'UNKNOWN'; | ||
return typeToString(value); | ||
} | ||
case 'u8': { | ||
assert(typeof value === 'number'); | ||
assert((value & 0xff) === value); | ||
return value; | ||
} | ||
case 'u16': { | ||
assert(typeof value === 'number'); | ||
assert((value & 0xffff) === value); | ||
return value; | ||
} | ||
case 'u32': { | ||
assert(typeof value === 'number'); | ||
assert((value >>> 0) === value); | ||
return value; | ||
} | ||
case 'u48': { | ||
assert(typeof value === 'number'); | ||
assert(Number.isSafeInteger(value)); | ||
assert(value >= 0 && value <= 0xffffffffffff); | ||
return value; | ||
@@ -1217,16 +1159,2 @@ } | ||
} | ||
case 'bool': { | ||
assert(typeof value === 'boolean'); | ||
return value; | ||
} | ||
case 'nodes': { | ||
assert(Array.isArray(value)); | ||
const json = []; | ||
for (const node of value) | ||
json.push(node.toString('hex')); | ||
return json; | ||
} | ||
default: { | ||
@@ -1250,9 +1178,8 @@ throw new Error('Unknown type.'); | ||
function unpad(str, start, end) { | ||
const s = str.substring(start, end); | ||
assert(/^\d+$/.test(s)); | ||
return parseInt(s, 10); | ||
const num = str.substring(start, end); | ||
return util.parseU16(num); | ||
} | ||
function timeToString(t) { | ||
assert(typeof t === 'number' && isFinite(t) && t >= 0); | ||
assert(Number.isSafeInteger(t) && t >= 0); | ||
@@ -1259,0 +1186,0 @@ const div = (t - util.now()) / YEAR68; |
@@ -27,3 +27,3 @@ /*! | ||
const {port, address, tcp} = this.rinfo; | ||
const msg = this.encode(!tcp); | ||
const msg = this.server.sign(this.encode(!tcp), address, port); | ||
return this.server.send(msg, 0, msg.length, port, address, tcp); | ||
@@ -141,2 +141,6 @@ } | ||
sign(msg, host, port) { | ||
return msg; | ||
} | ||
async handle(msg, rinfo) { | ||
@@ -163,3 +167,3 @@ const req = Message.decode(msg); | ||
const {port, address, tcp} = rinfo; | ||
const msg = res.encode(!tcp); | ||
const msg = this.sign(res.encode(!tcp), address, port); | ||
this.server.send(msg, 0, msg.length, port, address, tcp); | ||
@@ -166,0 +170,0 @@ this.emit('query', req, res, rinfo); |
135
lib/util.js
@@ -7,3 +7,4 @@ /*! | ||
* Parts of this software are based on miekg/dns and golang/go: | ||
* https://github.com/miekg/dns/blob/master/dnssec.go | ||
* https://github.com/miekg/dns/blob/master/labels.go | ||
* https://github.com/miekg/dns/blob/master/dnsutil/util.go | ||
*/ | ||
@@ -78,3 +79,3 @@ | ||
if (a !== b) | ||
if (!util.equal(a, b)) | ||
return n; | ||
@@ -91,3 +92,3 @@ | ||
if (a !== b) | ||
if (!util.equal(a, b)) | ||
break; | ||
@@ -207,3 +208,3 @@ | ||
let ai = a.charCodeAt(i); | ||
let bi = a.charCodeAt(i); | ||
let bi = b.charCodeAt(i); | ||
@@ -461,3 +462,3 @@ if (ai >= 0x41 && ai <= 0x5a) | ||
|| rr.class !== class_ | ||
|| rr.name !== name) { | ||
|| !util.equal(rr.name, name)) { | ||
return false; | ||
@@ -473,7 +474,7 @@ } | ||
const map = new Set(types); | ||
const set = new Set(types); | ||
const out = []; | ||
for (const rr of records) { | ||
if (!map.has(rr.type)) | ||
if (!set.has(rr.type)) | ||
out.push(rr); | ||
@@ -489,8 +490,8 @@ } | ||
const map = new Set(types); | ||
const set = new Set(types); | ||
const out = []; | ||
for (const rr of records) { | ||
if (map.has(rr.type)) { | ||
if (name !== '' && name !== rr.name) | ||
if (set.has(rr.type)) { | ||
if (name !== '' && !util.equal(rr.name, name)) | ||
continue; | ||
@@ -543,27 +544,113 @@ out.push(rr); | ||
util.digDate = function digDate(time) { | ||
const d = time != null ? new Date(time) : new Date(); | ||
const d = time != null ? new Date(time * 1000) : new Date(); | ||
const str = d.toString(); | ||
const parts = str.split(' '); | ||
const [day, month, date, year, ts, , tz] = parts; | ||
const z = tz.slice(1, -1); | ||
return `${day} ${month} ${date} ${ts} ${z} ${year}`; | ||
}; | ||
let [ | ||
day, | ||
month, | ||
date, | ||
year, | ||
ts, | ||
, | ||
tz | ||
] = parts; | ||
util.parseInteger = function parseInteger(str, max, size) { | ||
assert(typeof str === 'string'); | ||
tz = tz.slice(1, -1); | ||
let word = 0; | ||
return `${day} ${month} ${date} ${ts} ${tz} ${year}`; | ||
if (str.length > size) | ||
throw new Error('Invalid integer.'); | ||
for (let i = 0; i < str.length; i++) { | ||
const ch = str.charCodeAt(i) - 0x30; | ||
if (ch < 0 || ch > 9) | ||
throw new Error('Invalid integer.'); | ||
word *= 10; | ||
word += ch; | ||
if (word > max) | ||
throw new Error('Invalid integer.'); | ||
} | ||
return word; | ||
}; | ||
util.dir = function dir(obj) { | ||
util.parseU8 = function parseU8(str) { | ||
return util.parseInteger(str, 0xff, 3); | ||
}; | ||
util.parseU16 = function parseU16(str) { | ||
return util.parseInteger(str, 0xffff, 5); | ||
}; | ||
util.parseU32 = function parseU32(str) { | ||
return util.parseInteger(str, 0xffffffff, 10); | ||
}; | ||
util.parseU48 = function parseU48(str) { | ||
return util.parseInteger(str, 0xffffffffffff, 15); | ||
}; | ||
util.parseU64 = function parseU64(str) { | ||
assert(typeof str === 'string'); | ||
if (str.length > 20) | ||
throw new Error('Invalid integer.'); | ||
let hi = 0; | ||
let lo = 0; | ||
for (let i = 0; i < str.length; i++) { | ||
const ch = str.charCodeAt(i) - 0x30; | ||
if (ch < 0 || ch > 9) | ||
throw new Error('Invalid integer.'); | ||
lo *= 10; | ||
lo += ch; | ||
hi *= 10; | ||
if (lo > 0xffffffff) { | ||
const m = lo % 0x100000000; | ||
hi += (lo - m) / 0x100000000; | ||
lo = m; | ||
} | ||
if (hi > 0xffffffff) | ||
throw new Error('Invalid integer.'); | ||
} | ||
return [hi, lo]; | ||
}; | ||
util.serializeU64 = function serializeU64(hi, lo) { | ||
assert((hi >>> 0) === hi); | ||
assert((lo >>> 0) === lo); | ||
let str = ''; | ||
do { | ||
const mhi = hi % 10; | ||
hi -= mhi; | ||
hi /= 10; | ||
lo += mhi * 0x100000000; | ||
const mlo = lo % 10; | ||
lo -= mlo; | ||
lo /= 10; | ||
const ch = mlo + 0x30; | ||
str = String.fromCharCode(ch) + str; | ||
} while (lo > 0 || hi > 0); | ||
return str; | ||
}; | ||
util.dir = function dir(obj, inspect = true) { | ||
console.dir(obj, { | ||
depth: 20, | ||
colors: true, | ||
customInspect: true | ||
customInspect: inspect | ||
}); | ||
}; |
{ | ||
"name": "bns", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "DNS bike-shed", | ||
@@ -31,2 +31,3 @@ "keywords": [ | ||
"dependencies": { | ||
"bfile": "~0.0.2", | ||
"bheep": "~0.0.1", | ||
@@ -56,3 +57,3 @@ "binet": "~0.1.0", | ||
"browser": { | ||
"./lib/crypto": "./lib/crypto.js" | ||
"./lib/crypto": "./lib/crypto-browser.js" | ||
}, | ||
@@ -59,0 +60,0 @@ "browserify": { |
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
283044
31
11517
8
6
+ Addedbfile@~0.0.2
+ Addedbfile@0.0.2(transitive)