diff -r 3de96d11e417 -r ee8de27ff264 src/lib/app.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/app.js Wed Aug 13 21:02:03 2014 +0200 @@ -0,0 +1,212 @@ +#! /usr/bin/env nodejs +// +// mDNSGw - Zero Configuration DNS Gateway for Mesh Networks +// Copyright © 2014 Michael Schloh von Bennewitz +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +// THIS SOFTWARE. +// +// This file is part of mDNSGw, a Zero configuration DNS gateway +// which can be found at http://dev.europalab.com/mdnsgw/ +// +// app.js: ECMA JavaScript implementation +// + +/*********************************************************** +| ____ _ _ ____ ____ | +| _ __ ___ | _ \| \ | / ___| / ___|_ __ | +| | '_ ` _ \| | | | \| \___ \| | _\ \ /\ / / | +| | | | | | | |_| | |\ |___) | |_| |\ V V / | +| |_| |_| |_|____/|_| \_|____/ \____| \_/\_/ | +| | +| Requirements: Redis server with standard configuration | +| NodeJS and NPM modules (see package.json) | +| | +| Execute: To start this application, launch it with the | +| script named fork.js: $ ./fork.js | +| | +| Support: http://list.europalab.com/mailman/mdnsgs/ | +| | +***********************************************************/ + +// import module dependencies +var mdnsinst = require('mdns'); +var redisdat = require('redis'); +var nameinst = require('native-dns'); + + +// install POSIX signal handlers +process.on('SIGUSR2', function() { + console.log('SIGUSR2: Dumping mDNSGw entries at', Date()); + rediscli.hgetall('hostnames', function (error, object) {console.dir(object);}); +}); +process.on('SIGHUP', function() { + console.log('SIGHUP: Cleared all database entries at', Date()); + cleardb(); +}); + +// instantiate a new redis client +// http://www.rediscookbook.org/ +var rediscli = redisdat.createClient(); +rediscli.on('error', function (error) { + console.log('Error ' + error); +}); + +// clear mDNS service keys +function cleardb () { + rediscli.del('hostnames'); + // this is not working unfortunately for the loop + rediscli.keys('*', function (error, replies) { + replies.forEach(function (reply, ident) { + rediscli.del(reply, function (error, value) { + if (error) throw(error); + }); + }); + }); +} + +// scan all advertised mDNS service types +cleardb(); // clear old data first +var browsall = mdnsinst.browseThemAll(); +browsall.on('serviceUp', function(service) { + // iterate through hosts and watch accordingly + if (service.type.name.match(/^[a-zA-Z0-9\-]+$/)) { // mdns module hack + if (service.type.protocol == 'tcp') { + var browserv = mdnsinst.createBrowser(mdnsinst.tcp(service.type.name)); + } + else if (service.type.protocol == 'udp') { + var browserv = mdnsinst.createBrowser(mdnsinst.udp(service.type.name)); + } + else if (service.type.protocol == 'sctp') { + var browserv = mdnsinst.createBrowser(mdnsinst.sctp(service.type.name)); + } + else throw(error); + + // common logic for all transports (TCP, UDP, SCTP, etcetera) + browserv.on('serviceUp', function(service) { + //console.log('service up: ', service); + //{interfaceIndex: 2, type: {name: 'ssh', protocol: 'tcp', subtypes: [], fullyQualified: true}, replyDomain: 'local.', flags: 2, name: 'hostname-mich', networkInterface: 'eth0', fullname: 'hostname-mich._ssh._tcp.local.', host: 'hostname-mich.local.', port: 22, addresses: ['192.168.1.50']} + + // insert one or more IP addresses for each hostname.local. + rediscli.hset('hostnames', service.host.replace(/\.$/, ''), service.addresses); + }); + browserv.on('serviceDown', function(service) { + console.log('service down: ', service); + //FIXME: still need to selectively remove hosts + }); + browserv.on('serviceChanged', function(service) { + console.log('service changed: ', service); + //FIXME: still need to selectively update hosts + }); + browserv.start(); + } +}); +browsall.start(); + +// instantiate a new DNS server +var nameserv = nameinst.createServer(); + +nameserv.on('request', function (request, response) { + //console.log(request) + + // ensure that requested hostname is present + rediscli.hget('hostnames', request.question[0].name, function (error, value) { + // handle unexpected errors + if (error) throw(error); + + if (value) { // the db succeeded in finding a match + // populate the DNS response with the chosen hostname + rediscli.hkeys('hostnames', function (error, replies) { + replies.forEach(function (host, index) { + if (request.question[0].name == host) { + rediscli.hget('hostnames', host, function (error, value) { + // handle unexpected errors + if (error) throw(error); + + // FIXME: still must handle multihomed hosts + //// a host might have more than one address + //value.forEach(function (addr, iter) + // set the nameserver address + response.answer.push(nameinst.A({ + name: host, + address: value, + ttl: 600, + })); + response.send(); + }); + } + }); + }); + } + else { + response.answer.push(nameinst.A({ + name: request.question[0].name, + address: '127.0.0.1', + ttl: 600, + })); + response.send(); + } + }); +}); + +// DNS error handler logic +nameserv.on('error', function (err, buff, req, res) { + console.log('DNS problem: ', err.stack); +}); + +//// debug process user +//console.log('Start.'); +//console.log(process.env.USER); +//console.log(process.env.SUDO_USER); +//console.log('Done.'); +// +// <1024 must run privileged +var nudpport = 53; // default DNS +if (nudpport < 1024 && process.getuid() !== 0) { + //console.log('Serving on port <1024 from an unprivileged user.\nChange to root if using a privileged port number.') + throw new Error('Serving on port <1024 from an unprivileged user.') +} + +// start the DNS process +nameserv.serve(nudpport, function () { + try { + console.log('Starting mDNSGw on', Date()); + process.stdout.write('Old UID: ' + process.getuid() + ', Old GID: ' + process.getgid() + '... '); + process.umask('0644'); + process.setgid('daemon'); + if (process.env.SUDO_USER) + process.setuid(process.env.SUDO_USER); + else + process.setuid('daemon'); + console.log('New UID: ' + process.getuid() + ', New GID: ' + process.getgid()); + } catch (err) { + console.log('Cowardly refusing to keep the process alive as root.'); + process.exit(1); + } +}); + +//// debug print all key and value database entries +//rediscli.hgetall('hostnames', function (error, object) {console.dir(object);}); + +//// display stored mDNS service data entries +//rediscli.hkeys('hostnames', function (error, replies) { +// console.log(replies.length + ' replies:'); +// replies.forEach(function (reply, ident) { +// console.log(' ' + ident + ': ' + reply); +// }); +//}); + +//// block executes on program termination +//rediscli.quit(); // cleanup db connection +//browserv.stop(); // zombie scope too bad +//browsall.stop();