'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); }); };