Skip to content

Instantly share code, notes, and snippets.

@billywhizz
Last active December 4, 2018 23:58
Show Gist options
  • Save billywhizz/3f612e0bb9758e7fe80e629e3dc6b2c2 to your computer and use it in GitHub Desktop.
Save billywhizz/3f612e0bb9758e7fe80e629e3dc6b2c2 to your computer and use it in GitHub Desktop.
dv8 DNS client example

Running

## clone the gist
git clone [email protected]:3f612e0bb9758e7fe80e629e3dc6b2c2.git
cd 3f612e0bb9758e7fe80e629e3dc6b2c2
## the docker command to use
DV8_DNS="docker run -it --rm --name=dv8-dns --net=host -v $(pwd):/app billywhizz/dv8:0.0.5a dv8 ./dns.js"
## query google 8.8.8.8 dns server using UDP on port 53
$DV8_DNS www.google.com
## query local dnsmasq using UDP on port 53
$DV8_DNS www.google.com 127.0.0.53
## override port number in third param
$DV8_DNS www.google.com 127.0.0.53 53
const { UDP } = module('udp', {})
/*
DNS Client Example
To run:
docker run -it --rm --net=host -v $(pwd):/app billywhizz/dv8:0.0.5a dv8 ./dns.js api.billywhizz.io
*/
class Client {
constructor (address, port) {
const requests = {}
const sock = new UDP()
const buf = Buffer.alloc(65536)
const view = new DataView(buf.bytes)
const bytes = new Uint8Array(buf.bytes)
this.requests = requests
this.address = address
this.port = port
this.id = (Math.floor(Math.random() * 10) & 0xffff)
this.sock = sock
this.buf = buf
this.view = view
this.bytes = bytes
const voidFn = () => {}
sock.setup(buf, buf)
sock.onClose(voidFn)
sock.onSend(voidFn)
sock.onMessage((len, address, port) => {
const message = this.parseMessage(len)
const resolve = requests[message.id]
if (resolve) {
delete requests[message.id]
resolve({ address, port, message })
}
})
sock.bind('0.0.0.0', 0)
sock.start()
}
stop () {
return this.sock.start()
}
start () {
return this.sock.stop()
}
close () {
return this.sock.close()
}
readSection (start, off, sections) {
const { view, buf } = this
let qnameSize = 0
while (1) {
qnameSize = view.getUint8(off++)
if (qnameSize === 0) break
sections.push(buf.read(off, qnameSize))
off += qnameSize
}
return off
}
parseMessage (len) {
const { bytes, view } = this
const id = view.getUint16(0)
const flags = view.getUint16(2)
const question = []
const answer = []
const start = 12
let off = start
let qcount = view.getUint16(4)
while (qcount--) {
const sections = []
off = this.readSection(start, off, sections)
question.push({ qtype: view.getUint16(off), qclass: view.getUint16(off + 2), name: sections })
off += 4
}
let ancount = view.getUint16(6)
while (ancount--) {
const name = view.getUint16(off)
const sections = []
// TODO: check first 2 bits - if 0b11 then this is a reference to a field elswhere in the message
this.readSection(start, name & 0b11111111111111, sections)
off += 2
const qtype = view.getUint16(off)
off += 2
const qclass = view.getUint16(off)
off += 2
const ttl = view.getUint32(off)
off += 4
const rdLength = view.getUint16(off)
off += 2
if (qtype === 5) {
let start = off
const cnames = []
off = this.readSection(start, off, cnames)
answer.push({ name: sections, qtype, qclass, ttl, cnames })
} else if (qtype === 1) {
answer.push({ name: sections, qtype, qclass, ttl, ip: bytes.slice(off, off + rdLength) })
off += rdLength
}
}
return { id, flags, QR: (flags >> 15) & 0x1, opCode: (flags >> 11) & 0b1111, AA: (flags >> 10) & 0b1, TC: (flags >> 9) & 0b1, RD: (flags >> 8) & 0b1, RA: (flags >> 7) & 0b1, Z: (flags >> 6) & 0b111, RCODE: (flags >> 4) & 0b1111, question, answer }
}
createMessage (domain, qtype = 1, qclass = 1) {
const { buf, view, bytes, id } = this
view.setUint16(0, id)
view.setUint16(2, 0b0000000101000000)
view.setUint16(4, 1)
view.setUint16(6, 0)
view.setUint16(8, 0)
view.setUint16(10, 0)
let off = 12
const parts = domain.split('.')
for (const part of parts) {
view.setUint8(off++, part.length)
buf.write(part, off)
off += part.length
}
bytes[off++] = 0
view.setUint16(off, qtype)
off += 2
view.setUint16(off, qclass)
off += 2
return off
}
query (name) {
const { requests, sock, address, port } = this
const message = this.createMessage(name)
const promise = new Promise(resolve => {
requests[this.id++] = resolve
sock.send(message, address, port)
})
return promise
}
}
async function run () {
const [ , , host = 'www.example.com', serverIP = '8.8.8.8', serverPort = 53 ] = process.args
const dns = new Client(serverIP, serverPort)
const result = await dns.query(host)
console.log(JSON.stringify(result, null, ' '))
dns.stop()
dns.close()
}
run().catch(err => console.log(err.toString()))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment