michael@0: #! /usr/bin/env nodejs michael@0: // michael@0: // mDNSGw - Zero Configuration DNS Gateway for Mesh Networks michael@0: // Copyright © 2014 Michael Schloh von Bennewitz michael@0: // michael@0: // Permission to use, copy, modify, and/or distribute this software for michael@0: // any purpose with or without fee is hereby granted, provided that the michael@0: // above copyright notice and this permission notice appear in all copies. michael@0: // michael@0: // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL michael@0: // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED michael@0: // WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE michael@0: // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL michael@0: // DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR michael@0: // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS michael@0: // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF michael@0: // THIS SOFTWARE. michael@0: // michael@0: // This file is part of mDNSGw, a Zero configuration DNS gateway michael@0: // which can be found at http://dev.europalab.com/mdnsgw/ michael@0: // michael@0: // app.js: ECMA JavaScript implementation michael@0: // michael@0: michael@0: /*********************************************************** michael@0: | ____ _ _ ____ ____ | michael@0: | _ __ ___ | _ \| \ | / ___| / ___|_ __ | michael@0: | | '_ ` _ \| | | | \| \___ \| | _\ \ /\ / / | michael@0: | | | | | | | |_| | |\ |___) | |_| |\ V V / | michael@0: | |_| |_| |_|____/|_| \_|____/ \____| \_/\_/ | michael@0: | | michael@0: | Requirements: Redis server with standard configuration | michael@0: | NodeJS and NPM modules (see package.json) | michael@0: | | michael@0: | Execute: To start this application, launch it with the | michael@0: | script named fork.js: $ ./fork.js | michael@0: | | michael@0: | Support: http://list.europalab.com/mailman/mdnsgs/ | michael@0: | | michael@0: ***********************************************************/ michael@0: michael@0: // import module dependencies michael@0: var mdnsinst = require('mdns'); michael@0: var redisdat = require('redis'); michael@0: var nameinst = require('native-dns'); michael@0: michael@0: michael@0: // instantiate a new redis client michael@0: // http://www.rediscookbook.org/ michael@0: var rediscli = redisdat.createClient(); michael@0: rediscli.on('error', function (error) { michael@0: console.log('Error ' + error); michael@0: }); michael@0: michael@0: // clear mDNS service keys michael@0: rediscli.del('hostnames'); michael@0: // this is not working unfortunately for the loop michael@0: rediscli.keys('*', function (error, replies) { michael@0: replies.forEach(function (reply, ident) { michael@0: rediscli.del(reply, function (error, value) { michael@0: if (error) throw(error); michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: // scan all advertised mDNS service types michael@0: var browsall = mdnsinst.browseThemAll(); michael@0: browsall.on('serviceUp', function(service) { michael@0: // iterate through hosts and watch accordingly michael@0: if (service.type.name.match(/^[a-zA-Z0-9\-]+$/)) { // mdns module hack michael@0: if (service.type.protocol == 'tcp') { michael@0: var browserv = mdnsinst.createBrowser(mdnsinst.tcp(service.type.name)); michael@0: } michael@0: else if (service.type.protocol == 'udp') { michael@0: var browserv = mdnsinst.createBrowser(mdnsinst.udp(service.type.name)); michael@0: } michael@0: else if (service.type.protocol == 'sctp') { michael@0: var browserv = mdnsinst.createBrowser(mdnsinst.sctp(service.type.name)); michael@0: } michael@0: else throw(error); michael@0: michael@0: // common logic for all transports (TCP, UDP, SCTP, etcetera) michael@0: browserv.on('serviceUp', function(service) { michael@0: //console.log('service up: ', service); michael@0: //{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']} michael@0: michael@0: // insert one or more IP addresses for each hostname.local. michael@0: rediscli.hset('hostnames', service.host.replace(/\.$/, ''), service.addresses); michael@0: }); michael@0: browserv.on('serviceDown', function(service) { michael@0: //console.log('service down: ', service); michael@0: //FIXME: still need to selectively remove hosts michael@0: }); michael@0: browserv.start(); michael@0: } michael@0: }); michael@0: browsall.start(); michael@0: michael@0: // instantiate a new DNS server michael@0: var nameserv = nameinst.createServer(); michael@0: michael@0: nameserv.on('request', function (request, response) { michael@0: //console.log(request) michael@0: michael@0: // ensure that requested hostname is present michael@0: rediscli.hget('hostnames', request.question[0].name, function (error, value) { michael@0: // handle unexpected errors michael@0: if (error) throw(error); michael@0: michael@0: if (value) { // the db succeeded in finding a match michael@0: // populate the DNS response with the chosen hostname michael@0: rediscli.hkeys('hostnames', function (error, replies) { michael@0: replies.forEach(function (host, index) { michael@0: if (request.question[0].name == host) { michael@0: rediscli.hget('hostnames', host, function (error, value) { michael@0: // handle unexpected errors michael@0: if (error) throw(error); michael@0: michael@0: // FIXME: still must handle multihomed hosts michael@0: //// a host might have more than one address michael@0: //value.forEach(function (addr, iter) michael@0: // set the nameserver address michael@0: response.answer.push(nameinst.A({ michael@0: name: host, michael@0: address: value, michael@0: ttl: 600, michael@0: })); michael@0: response.send(); michael@0: }); michael@0: } michael@0: }); michael@0: }); michael@0: } michael@0: else { michael@0: response.answer.push(nameinst.A({ michael@0: name: request.question[0].name, michael@0: address: '127.0.0.1', michael@0: ttl: 600, michael@0: })); michael@0: response.send(); michael@0: } michael@0: }); michael@0: }); michael@0: michael@0: // DNS error handler logic michael@0: nameserv.on('error', function (err, buff, req, res) { michael@0: console.log('DNS problem: ', err.stack); michael@0: }); michael@0: michael@0: nameserv.serve(15353); michael@0: michael@0: //// debug print all key and value database entries michael@0: //rediscli.hgetall('hostnames', function (error, object) {console.dir(object);}); michael@0: michael@0: //// display stored mDNS service data entries michael@0: //rediscli.hkeys('hostnames', function (error, replies) { michael@0: // console.log(replies.length + ' replies:'); michael@0: // replies.forEach(function (reply, ident) { michael@0: // console.log(' ' + ident + ': ' + reply); michael@0: // }); michael@0: //}); michael@0: michael@0: //// block executes on program termination michael@0: //rediscli.quit(); // cleanup db connection michael@0: //browserv.stop(); // zombie scope too bad michael@0: //browsall.stop();