diff --git a/lib/goldilocks.js b/lib/goldilocks.js index e54084c..43a70b1 100644 --- a/lib/goldilocks.js +++ b/lib/goldilocks.js @@ -301,13 +301,12 @@ module.exports.create = function (deps, config) { mainPort = Object.keys(unforwarded).map(Number).sort((a, b) => a - b)[0]; } } - updateConf(); - if (!config.mdns.disabled) { - require('./mdns').start(deps, config, mainPort); - } - return { + var result = { updateConf }; + Object.defineProperty(result, 'mainPort', {enumerable: true, get: () => mainPort}); + + return result; }; diff --git a/lib/mdns.js b/lib/mdns.js index d41f25a..9372b22 100644 --- a/lib/mdns.js +++ b/lib/mdns.js @@ -2,6 +2,7 @@ var PromiseA = require('bluebird'); var queryName = '_cloud._tcp.local'; +var dnsSuite = require('dns-suite'); function createResponse(name, ownerIds, packet, ttl, mainPort) { var rpacket = { @@ -85,20 +86,19 @@ function createResponse(name, ownerIds, packet, ttl, mainPort) { }); }); - return require('dns-suite').DNSPacket.write(rpacket); + return dnsSuite.DNSPacket.write(rpacket); } -module.exports.start = function (deps, config, mainPort) { - var socket = require('dgram').createSocket({ type: 'udp4', reuseAddr: true }); - var dns = require('dns-suite'); +module.exports.create = function (deps, config) { + var socket; var nextBroadcast = -1; - socket.on('message', function (message, rinfo) { + function handlePacket(message, rinfo) { // console.log('Received %d bytes from %s:%d', message.length, rinfo.address, rinfo.port); var packet; try { - packet = dns.DNSPacket.parse(message); + packet = dnsSuite.DNSPacket.parse(message); } catch (er) { // `dns-suite` actually errors on a lot of the packets floating around in our network, @@ -108,16 +108,12 @@ module.exports.start = function (deps, config, mainPort) { } // Only respond to queries. - if (packet.header.qr !== 0) { - return; - } + 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; - } - if (packet.question[0].typeName !== 'PTR' || packet.question[0].className !== 'IN' ) { - return; - } + if (packet.question.length !== 1) { return; } + if (packet.question[0].name !== queryName) { return; } + if (packet.question[0].typeName !== 'PTR') { return; } + if (packet.question[0].className !== 'IN' ) { return; } var proms = [ deps.storage.mdnsId.get() @@ -131,7 +127,7 @@ module.exports.start = function (deps, config, mainPort) { ]; PromiseA.all(proms).then(function (results) { - var resp = createResponse(results[0], results[1], packet, config.mdns.ttl, mainPort); + var resp = createResponse(results[0], results[1], packet, config.mdns.ttl, deps.tcp.mainPort); var now = Date.now(); if (now > nextBroadcast) { socket.send(resp, config.mdns.port, config.mdns.broadcast); @@ -140,18 +136,67 @@ module.exports.start = function (deps, config, mainPort) { socket.send(resp, rinfo.port, rinfo.address); } }); - }); + } - socket.bind(config.mdns.port, function () { - var addr = this.address(); - console.log('bound on UDP %s:%d for mDNS', addr.address, addr.port); + function start() { + socket = require('dgram').createSocket({ type: 'udp4', reuseAddr: true }); + socket.on('message', handlePacket); - socket.setBroadcast(true); - socket.addMembership(config.mdns.broadcast); - // This is supposed to be a local device discovery mechanism, so we shouldn't - // need to hop through any gateways. This helps with security by making it - // much more difficult for someone to use us as part of a DDoS attack by - // spoofing the UDP address a request came from. - socket.setTTL(1); - }); + return new Promise(function (resolve, reject) { + socket.once('error', reject); + + 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); + // This is supposed to be a local device discovery mechanism, so we shouldn't + // need to hop through any gateways. This helps with security by making it + // much more difficult for someone to use us as part of a DDoS attack by + // spoofing the UDP address a request came from. + socket.setTTL(1); + + socket.removeListener('error', reject); + resolve(); + }); + }); + } + function stop() { + return new Promise(function (resolve, reject) { + socket.once('error', reject); + + socket.close(function () { + socket.removeListener('error', reject); + socket = null; + resolve(); + }); + }); + } + + function updateConf() { + var promise; + if (config.mdns.disabled) { + if (socket) { + promise = stop(); + } + } else { + if (!socket) { + promise = start(); + } else if (socket.address().port !== config.mdns.port) { + promise = stop().then(start); + } else { + // Can't check membership, so just add the current broadcast address to make sure + // it's set. Hopefully nothing bad happens by adding multiple addresses or adding + // the same address multiple times. + socket.addMembership(config.mdns.broadcast); + promise = Promise.resolve(); + } + } + } + updateConf(); + + return { + updateConf + }; }; diff --git a/lib/worker.js b/lib/worker.js index 6779244..5b05f87 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -51,6 +51,7 @@ function create(conf) { , proxy: require('./proxy-conn').create(deps, conf) , socks5: require('./socks5-server').create(deps, conf) , ddns: require('./ddns').create(deps, conf) + , mdns: require('./mdns').create(deps, conf) , udp: require('./udp').create(deps, conf) , tcp: require('./goldilocks').create(deps, conf) }; diff --git a/package-lock.json b/package-lock.json index 67f9108..e7151c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -448,7 +448,11 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "dns-suite": { - "version": "git+https://git@git.daplie.com/Daplie/dns-suite#950867c452323da776c050363b22d8f06a8ed414" + "version": "git+https://git@git.daplie.com/Daplie/dns-suite#6352cf4b516d94f0283c9c7cd024431bf974f049", + "requires": { + "bluebird": "3.5.0", + "hexdump.js": "1.0.5" + } }, "duplexer2": { "version": "0.1.4", @@ -820,6 +824,11 @@ "sntp": "1.0.9" } }, + "hexdump.js": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/hexdump.js/-/hexdump.js-1.0.5.tgz", + "integrity": "sha1-xbxlSoIvAzjzEX5fXzVgZd6HmDQ=" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",