From 1f8e44947fd2a9048f0108dde63a27588c1a9b34 Mon Sep 17 00:00:00 2001 From: tigerbot Date: Tue, 23 May 2017 16:23:43 -0600 Subject: [PATCH] added simple mDNS responder --- bin/goldilocks.js | 3 + lib/goldilocks.js | 4 ++ lib/mdns.js | 144 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 152 insertions(+) create mode 100644 lib/mdns.js diff --git a/bin/goldilocks.js b/bin/goldilocks.js index b19d5e0..205074c 100755 --- a/bin/goldilocks.js +++ b/bin/goldilocks.js @@ -73,6 +73,9 @@ function readConfigAndRun(args) { if (!config.dns) { config.dns = { modules: { name: 'proxy', port: 3053 } }; } + if (!config.mdns) { + config.mdns = { port: 5353, broadcast: '224.0.0.251', ttl: 300 }; + } if (!config.tcp) { config.tcp = {}; } diff --git a/lib/goldilocks.js b/lib/goldilocks.js index e938bfd..be18c92 100644 --- a/lib/goldilocks.js +++ b/lib/goldilocks.js @@ -225,5 +225,9 @@ module.exports.create = function (deps, config) { } } + if (!config.mdns.disabled) { + require('./mdns').start(deps, config); + } + return PromiseA.all(listenPromises); }; diff --git a/lib/mdns.js b/lib/mdns.js new file mode 100644 index 0000000..cc08ccd --- /dev/null +++ b/lib/mdns.js @@ -0,0 +1,144 @@ +'use strict'; + +var PromiseA = require('bluebird'); +var fs = PromiseA.promisifyAll(require('fs')); +var idFilename = require('path').join(__dirname, '..', 'var', 'mdns-id'); +var queryName = '_cloud._tcp.local'; + +var randomId = { + get: function () { + return fs.readFileAsync(idFilename) + .catch(function (err) { + if (err.code !== 'ENOENT') { + return PromiseA.reject(err); + } + var id = require('crypto').randomBytes(5).toString('hex'); + return randomId.set(id); + }); + } + +, set: function (value) { + return fs.writeFileAsync(idFilename, value) + .then(function () { + return value; + }); + } +}; + +function createResponse(name, packet, ttl) { + var rpacket = { + header: { + id: packet.header.id + , qr: 1 + , opcode: 0 + , aa: 1 + , tc: 0 + , rd: 0 + , ra: 0 + , res1: 0 + , res2: 0 + , res3: 0 + , rcode: 0 + , } + , question: packet.question + , answer: [] + , authority: [] + , additional: [] + , edns_options: [] + }; + + rpacket.answer.push({ + name: queryName + , typeName: 'PTR' + , ttl: ttl + , className: 'IN' + , data: name + '.' + queryName + }); + + var ifaces = require('./local-ip').find(); + Object.keys(ifaces).forEach(function (iname) { + var iface = ifaces[iname]; + + iface.ipv4.forEach(function (addr) { + rpacket.additional.push({ + name: name + '.local' + , typeName: 'A' + , ttl: ttl + , className: 'IN' + , address: addr.address + }); + }); + + iface.ipv6.forEach(function (addr) { + rpacket.additional.push({ + name: name + '.local' + , typeName: 'AAAA' + , ttl: ttl + , className: 'IN' + , address: addr.address + }); + }); + }); + + rpacket.additional.push({ + name: name + '.' + queryName + , typeName: 'SRV' + , ttl: ttl + , className: 'IN' + , priority: 1 + , weight: 0 + , port: 443 + , target: name + ".local" + }); + rpacket.additional.push({ + name: name + '._device-info._tcp.local' + , typeName: 'TXT' + , ttl: ttl + , className: 'IN' + , data: ["model=CloudHome1,1", "dappsvers=1"] + }); + + return require('dns-suite').DNSPacket.write(rpacket); +} + +module.exports.start = function (deps, config) { + var socket = require('dgram').createSocket({ type: 'udp4', reuseAddr: true }); + var dns = require('dns-suite'); + + socket.on('message', function (message, rinfo) { + // console.log('Received %d bytes from %s:%d', message.length, rinfo.address, rinfo.port); + + var packet; + try { + packet = dns.DNSPacket.parse(message); + } + catch (er) { + // `dns-suite` actually errors on a lot of the packets floating around in our network, + // so don't bother logging any errors. (We still use `dns-suite` because unlike `dns-js` + // it can successfully craft the one packet we want to send.) + return; + } + + // Only respond to queries. + if (packet.header.qr !== 0) { + return; + } + // Only respond if they were asking for cloud devices. + if (packet.question.length !== 1 || packet.question[0].name !== queryName) { + return; + } + + randomId.get().then(function (name) { + var resp = createResponse(name, packet, config.mdns.ttl); + socket.send(resp, config.mdns.port, config.mdns.broadcast); + }); + }); + + socket.bind(config.mdns.port, function () { + var addr = this.address(); + console.log('bound on UDP %s:%d for mDNS', addr.address, addr.port); + + socket.setBroadcast(true); + socket.addMembership(config.mdns.broadcast); + }); +}; diff --git a/package.json b/package.json index 67be583..0f2629a 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "bluebird": "^3.4.6", "body-parser": "git+https://github.com/expressjs/body-parser.git#1.16.1", "commander": "^2.9.0", + "dns-suite": "git+https://git@git.daplie.com:Daplie/dns-suite#v1", "express": "git+https://github.com/expressjs/express.git#4.x", "finalhandler": "^0.4.0", "greenlock": "git+https://git.daplie.com/Daplie/node-greenlock.git#master",