| 
									
										
										
										
											2017-05-23 16:23:43 -06:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var PromiseA = require('bluebird'); | 
					
						
							|  |  |  | var fs = PromiseA.promisifyAll(require('fs')); | 
					
						
							| 
									
										
										
										
											2017-06-01 13:06:52 -06:00
										 |  |  | var path = require('path'); | 
					
						
							|  |  |  | var idFilename = path.join(__dirname, '..', 'var', 'mdns-id'); | 
					
						
							| 
									
										
										
										
											2017-05-23 16:23:43 -06:00
										 |  |  | 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) { | 
					
						
							| 
									
										
										
										
											2017-06-01 13:06:52 -06:00
										 |  |  |     return fs.mkdirAsync(path.dirname(idFilename)).catch(function (err) { | 
					
						
							|  |  |  |       if (err.code !== 'EEXIST') { | 
					
						
							|  |  |  |         console.error('failed to mkdir', path.dirname(idFilename), err.toString()); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }).then(function () { | 
					
						
							|  |  |  |       return fs.writeFileAsync(idFilename, value).then(function () { | 
					
						
							| 
									
										
										
										
											2017-05-23 16:23:43 -06:00
										 |  |  |         return value; | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-06-01 13:06:52 -06:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-05-23 16:23:43 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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'); | 
					
						
							| 
									
										
										
										
											2017-05-30 12:35:29 -06:00
										 |  |  |   var nextBroadcast = -1; | 
					
						
							| 
									
										
										
										
											2017-05-23 16:23:43 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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); | 
					
						
							| 
									
										
										
										
											2017-05-30 12:35:29 -06:00
										 |  |  |       var now = Date.now(); | 
					
						
							|  |  |  |       if (now > nextBroadcast) { | 
					
						
							|  |  |  |         socket.send(resp, config.mdns.port, config.mdns.broadcast); | 
					
						
							|  |  |  |         nextBroadcast = now + config.mdns.ttl * 1000; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         socket.send(resp, rinfo.port, rinfo.address); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-05-23 16:23:43 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; |