src/app.js

Wed, 13 Aug 2014 21:00:15 +0200

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 13 Aug 2014 21:00:15 +0200
changeset 3
42e83b3431d9
parent 0
eb6d4ce6fd78
permissions
-rwxr-xr-x

Include signal handler and complete most of service daemon logic.

michael@0 1 #! /usr/bin/env nodejs
michael@0 2 //
michael@0 3 // mDNSGw - Zero Configuration DNS Gateway for Mesh Networks
michael@0 4 // Copyright © 2014 Michael Schloh von Bennewitz <michael@schloh.com>
michael@0 5 //
michael@0 6 // Permission to use, copy, modify, and/or distribute this software for
michael@0 7 // any purpose with or without fee is hereby granted, provided that the
michael@0 8 // above copyright notice and this permission notice appear in all copies.
michael@0 9 //
michael@0 10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
michael@0 11 // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
michael@0 12 // WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
michael@0 13 // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
michael@0 14 // DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
michael@0 15 // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
michael@0 16 // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
michael@0 17 // THIS SOFTWARE.
michael@0 18 //
michael@0 19 // This file is part of mDNSGw, a Zero configuration DNS gateway
michael@0 20 // which can be found at http://dev.europalab.com/mdnsgw/
michael@0 21 //
michael@0 22 // app.js: ECMA JavaScript implementation
michael@0 23 //
michael@0 24
michael@0 25 /***********************************************************
michael@0 26 | ____ _ _ ____ ____ |
michael@0 27 | _ __ ___ | _ \| \ | / ___| / ___|_ __ |
michael@0 28 | | '_ ` _ \| | | | \| \___ \| | _\ \ /\ / / |
michael@0 29 | | | | | | | |_| | |\ |___) | |_| |\ V V / |
michael@0 30 | |_| |_| |_|____/|_| \_|____/ \____| \_/\_/ |
michael@0 31 | |
michael@0 32 | Requirements: Redis server with standard configuration |
michael@0 33 | NodeJS and NPM modules (see package.json) |
michael@0 34 | |
michael@0 35 | Execute: To start this application, launch it with the |
michael@0 36 | script named fork.js: $ ./fork.js |
michael@0 37 | |
michael@0 38 | Support: http://list.europalab.com/mailman/mdnsgs/ |
michael@0 39 | |
michael@0 40 ***********************************************************/
michael@0 41
michael@0 42 // import module dependencies
michael@0 43 var mdnsinst = require('mdns');
michael@0 44 var redisdat = require('redis');
michael@0 45 var nameinst = require('native-dns');
michael@0 46
michael@0 47
michael@3 48 // install POSIX signal handlers
michael@3 49 process.on('SIGUSR2', function() {
michael@3 50 console.log('SIGUSR2: Dumping mDNSGw entries at', Date());
michael@3 51 rediscli.hgetall('hostnames', function (error, object) {console.dir(object);});
michael@3 52 });
michael@3 53 process.on('SIGHUP', function() {
michael@3 54 console.log('SIGHUP: Cleared all database entries at', Date());
michael@3 55 cleardb();
michael@3 56 });
michael@3 57
michael@0 58 // instantiate a new redis client
michael@0 59 // http://www.rediscookbook.org/
michael@0 60 var rediscli = redisdat.createClient();
michael@0 61 rediscli.on('error', function (error) {
michael@0 62 console.log('Error ' + error);
michael@0 63 });
michael@0 64
michael@0 65 // clear mDNS service keys
michael@3 66 function cleardb () {
michael@3 67 rediscli.del('hostnames');
michael@3 68 // this is not working unfortunately for the loop
michael@3 69 rediscli.keys('*', function (error, replies) {
michael@3 70 replies.forEach(function (reply, ident) {
michael@3 71 rediscli.del(reply, function (error, value) {
michael@3 72 if (error) throw(error);
michael@3 73 });
michael@0 74 });
michael@0 75 });
michael@3 76 }
michael@0 77
michael@0 78 // scan all advertised mDNS service types
michael@3 79 cleardb(); // clear old data first
michael@0 80 var browsall = mdnsinst.browseThemAll();
michael@0 81 browsall.on('serviceUp', function(service) {
michael@0 82 // iterate through hosts and watch accordingly
michael@0 83 if (service.type.name.match(/^[a-zA-Z0-9\-]+$/)) { // mdns module hack
michael@0 84 if (service.type.protocol == 'tcp') {
michael@0 85 var browserv = mdnsinst.createBrowser(mdnsinst.tcp(service.type.name));
michael@0 86 }
michael@0 87 else if (service.type.protocol == 'udp') {
michael@0 88 var browserv = mdnsinst.createBrowser(mdnsinst.udp(service.type.name));
michael@0 89 }
michael@0 90 else if (service.type.protocol == 'sctp') {
michael@0 91 var browserv = mdnsinst.createBrowser(mdnsinst.sctp(service.type.name));
michael@0 92 }
michael@0 93 else throw(error);
michael@0 94
michael@0 95 // common logic for all transports (TCP, UDP, SCTP, etcetera)
michael@0 96 browserv.on('serviceUp', function(service) {
michael@0 97 //console.log('service up: ', service);
michael@0 98 //{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 99
michael@0 100 // insert one or more IP addresses for each hostname.local.
michael@0 101 rediscli.hset('hostnames', service.host.replace(/\.$/, ''), service.addresses);
michael@0 102 });
michael@0 103 browserv.on('serviceDown', function(service) {
michael@3 104 console.log('service down: ', service);
michael@0 105 //FIXME: still need to selectively remove hosts
michael@0 106 });
michael@3 107 browserv.on('serviceChanged', function(service) {
michael@3 108 console.log('service changed: ', service);
michael@3 109 //FIXME: still need to selectively update hosts
michael@3 110 });
michael@0 111 browserv.start();
michael@0 112 }
michael@0 113 });
michael@0 114 browsall.start();
michael@0 115
michael@0 116 // instantiate a new DNS server
michael@0 117 var nameserv = nameinst.createServer();
michael@0 118
michael@0 119 nameserv.on('request', function (request, response) {
michael@0 120 //console.log(request)
michael@0 121
michael@0 122 // ensure that requested hostname is present
michael@0 123 rediscli.hget('hostnames', request.question[0].name, function (error, value) {
michael@0 124 // handle unexpected errors
michael@0 125 if (error) throw(error);
michael@0 126
michael@0 127 if (value) { // the db succeeded in finding a match
michael@0 128 // populate the DNS response with the chosen hostname
michael@0 129 rediscli.hkeys('hostnames', function (error, replies) {
michael@0 130 replies.forEach(function (host, index) {
michael@0 131 if (request.question[0].name == host) {
michael@0 132 rediscli.hget('hostnames', host, function (error, value) {
michael@0 133 // handle unexpected errors
michael@0 134 if (error) throw(error);
michael@0 135
michael@0 136 // FIXME: still must handle multihomed hosts
michael@0 137 //// a host might have more than one address
michael@0 138 //value.forEach(function (addr, iter)
michael@0 139 // set the nameserver address
michael@0 140 response.answer.push(nameinst.A({
michael@0 141 name: host,
michael@0 142 address: value,
michael@0 143 ttl: 600,
michael@0 144 }));
michael@0 145 response.send();
michael@0 146 });
michael@0 147 }
michael@0 148 });
michael@0 149 });
michael@0 150 }
michael@0 151 else {
michael@0 152 response.answer.push(nameinst.A({
michael@0 153 name: request.question[0].name,
michael@0 154 address: '127.0.0.1',
michael@0 155 ttl: 600,
michael@0 156 }));
michael@0 157 response.send();
michael@0 158 }
michael@0 159 });
michael@0 160 });
michael@0 161
michael@0 162 // DNS error handler logic
michael@0 163 nameserv.on('error', function (err, buff, req, res) {
michael@0 164 console.log('DNS problem: ', err.stack);
michael@0 165 });
michael@0 166
michael@3 167 //// debug process user
michael@3 168 //console.log('Start.');
michael@3 169 //console.log(process.env.USER);
michael@3 170 //console.log(process.env.SUDO_USER);
michael@3 171 //console.log('Done.');
michael@3 172 //
michael@3 173 // <1024 must run privileged
michael@3 174 var nudpport = 53; // default DNS
michael@3 175 if (nudpport < 1024 && process.getuid() !== 0) {
michael@3 176 //console.log('Serving on port <1024 from an unprivileged user.\nChange to root if using a privileged port number.')
michael@3 177 throw new Error('Serving on port <1024 from an unprivileged user.')
michael@3 178 }
michael@3 179
michael@3 180 // start the DNS process
michael@3 181 nameserv.serve(nudpport, function () {
michael@3 182 try {
michael@3 183 console.log('Starting mDNSGw on', Date());
michael@3 184 process.stdout.write('Old UID: ' + process.getuid() + ', Old GID: ' + process.getgid() + '... ');
michael@3 185 process.umask('0644');
michael@3 186 process.setgid('daemon');
michael@3 187 if (process.env.SUDO_USER)
michael@3 188 process.setuid(process.env.SUDO_USER);
michael@3 189 else
michael@3 190 process.setuid('daemon');
michael@3 191 console.log('New UID: ' + process.getuid() + ', New GID: ' + process.getgid());
michael@3 192 } catch (err) {
michael@3 193 console.log('Cowardly refusing to keep the process alive as root.');
michael@3 194 process.exit(1);
michael@3 195 }
michael@3 196 });
michael@0 197
michael@0 198 //// debug print all key and value database entries
michael@0 199 //rediscli.hgetall('hostnames', function (error, object) {console.dir(object);});
michael@0 200
michael@0 201 //// display stored mDNS service data entries
michael@0 202 //rediscli.hkeys('hostnames', function (error, replies) {
michael@0 203 // console.log(replies.length + ' replies:');
michael@0 204 // replies.forEach(function (reply, ident) {
michael@0 205 // console.log(' ' + ident + ': ' + reply);
michael@0 206 // });
michael@0 207 //});
michael@0 208
michael@0 209 //// block executes on program termination
michael@0 210 //rediscli.quit(); // cleanup db connection
michael@0 211 //browserv.stop(); // zombie scope too bad
michael@0 212 //browsall.stop();

mercurial