src/lib/app.js

changeset 5
ee8de27ff264
parent 3
42e83b3431d9
child 10
f48fa3532729
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/lib/app.js	Wed Aug 13 21:02:03 2014 +0200
     1.3 @@ -0,0 +1,212 @@
     1.4 +#! /usr/bin/env nodejs
     1.5 +//
     1.6 +//  mDNSGw - Zero Configuration DNS Gateway for Mesh Networks
     1.7 +//  Copyright © 2014 Michael Schloh von Bennewitz <michael@schloh.com>
     1.8 +//
     1.9 +//  Permission to use, copy, modify, and/or distribute this software for
    1.10 +//  any purpose with or without fee is hereby granted, provided that the
    1.11 +//  above copyright notice and this permission notice appear in all copies.
    1.12 +//
    1.13 +//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
    1.14 +//  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
    1.15 +//  WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
    1.16 +//  AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
    1.17 +//  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
    1.18 +//  PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
    1.19 +//  ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
    1.20 +//  THIS SOFTWARE.
    1.21 +//
    1.22 +//  This file is part of mDNSGw, a Zero configuration DNS gateway
    1.23 +//  which can be found at http://dev.europalab.com/mdnsgw/
    1.24 +//
    1.25 +//  app.js: ECMA JavaScript implementation
    1.26 +//
    1.27 +
    1.28 +/***********************************************************
    1.29 +|                  ____  _   _ ____   ____                 |
    1.30 +|        _ __ ___ |  _ \| \ | / ___| / ___|_      __       |
    1.31 +|       | '_ ` _ \| | | |  \| \___ \| |  _\ \ /\ / /       |
    1.32 +|       | | | | | | |_| | |\  |___) | |_| |\ V  V /        |
    1.33 +|       |_| |_| |_|____/|_| \_|____/ \____| \_/\_/         |
    1.34 +|                                                          |
    1.35 +| Requirements: Redis server with standard configuration   |
    1.36 +|               NodeJS and NPM modules (see package.json)  |
    1.37 +|                                                          |
    1.38 +| Execute: To start this application, launch it with the   |
    1.39 +|          script named fork.js: $ ./fork.js               |
    1.40 +|                                                          |
    1.41 +| Support: http://list.europalab.com/mailman/mdnsgs/       |
    1.42 +|                                                          |
    1.43 +***********************************************************/
    1.44 +
    1.45 +// import module dependencies
    1.46 +var mdnsinst = require('mdns');
    1.47 +var redisdat = require('redis');
    1.48 +var nameinst = require('native-dns');
    1.49 +
    1.50 +
    1.51 +// install POSIX signal handlers
    1.52 +process.on('SIGUSR2', function() {
    1.53 +  console.log('SIGUSR2: Dumping mDNSGw entries at', Date());
    1.54 +  rediscli.hgetall('hostnames', function (error, object) {console.dir(object);});
    1.55 +});
    1.56 +process.on('SIGHUP', function() {
    1.57 +  console.log('SIGHUP: Cleared all database entries at', Date());
    1.58 +  cleardb();
    1.59 +});
    1.60 +
    1.61 +// instantiate a new redis client
    1.62 +// http://www.rediscookbook.org/
    1.63 +var rediscli = redisdat.createClient();
    1.64 +rediscli.on('error', function (error) {
    1.65 +  console.log('Error ' + error);
    1.66 +});
    1.67 +
    1.68 +// clear mDNS service keys
    1.69 +function cleardb () {
    1.70 +  rediscli.del('hostnames');
    1.71 +  // this is not working unfortunately for the loop
    1.72 +  rediscli.keys('*', function (error, replies) {
    1.73 +    replies.forEach(function (reply, ident) {
    1.74 +      rediscli.del(reply, function (error, value) {
    1.75 +        if (error) throw(error);
    1.76 +      });
    1.77 +    });
    1.78 +  });
    1.79 +}
    1.80 +
    1.81 +// scan all advertised mDNS service types
    1.82 +cleardb(); // clear old data first
    1.83 +var browsall = mdnsinst.browseThemAll();
    1.84 +browsall.on('serviceUp', function(service) {
    1.85 +    // iterate through hosts and watch accordingly
    1.86 +    if (service.type.name.match(/^[a-zA-Z0-9\-]+$/)) { // mdns module hack
    1.87 +      if (service.type.protocol == 'tcp') {
    1.88 +        var browserv = mdnsinst.createBrowser(mdnsinst.tcp(service.type.name));
    1.89 +      }
    1.90 +      else if (service.type.protocol == 'udp') {
    1.91 +        var browserv = mdnsinst.createBrowser(mdnsinst.udp(service.type.name));
    1.92 +      }
    1.93 +      else if (service.type.protocol == 'sctp') {
    1.94 +        var browserv = mdnsinst.createBrowser(mdnsinst.sctp(service.type.name));
    1.95 +      }
    1.96 +      else throw(error);
    1.97 +
    1.98 +      // common logic for all transports (TCP, UDP, SCTP, etcetera)
    1.99 +      browserv.on('serviceUp', function(service) {
   1.100 +        //console.log('service up: ', service);
   1.101 +        //{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']}
   1.102 +
   1.103 +        // insert one or more IP addresses for each hostname.local.
   1.104 +        rediscli.hset('hostnames', service.host.replace(/\.$/, ''), service.addresses);
   1.105 +      });
   1.106 +      browserv.on('serviceDown', function(service) {
   1.107 +        console.log('service down: ', service);
   1.108 +        //FIXME: still need to selectively remove hosts
   1.109 +      });
   1.110 +      browserv.on('serviceChanged', function(service) {
   1.111 +        console.log('service changed: ', service);
   1.112 +        //FIXME: still need to selectively update hosts
   1.113 +      });
   1.114 +      browserv.start();
   1.115 +    }
   1.116 +});
   1.117 +browsall.start();
   1.118 +
   1.119 +// instantiate a new DNS server
   1.120 +var nameserv = nameinst.createServer();
   1.121 +
   1.122 +nameserv.on('request', function (request, response) {
   1.123 +  //console.log(request)
   1.124 +
   1.125 +  // ensure that requested hostname is present
   1.126 +  rediscli.hget('hostnames', request.question[0].name, function (error, value) {
   1.127 +    // handle unexpected errors
   1.128 +    if (error) throw(error);
   1.129 +
   1.130 +    if (value) { // the db succeeded in finding a match
   1.131 +      // populate the DNS response with the chosen hostname
   1.132 +      rediscli.hkeys('hostnames', function (error, replies) {
   1.133 +          replies.forEach(function (host, index) {
   1.134 +            if (request.question[0].name == host) {
   1.135 +              rediscli.hget('hostnames', host, function (error, value) {
   1.136 +                // handle unexpected errors
   1.137 +                if (error) throw(error);
   1.138 +
   1.139 +                // FIXME: still must handle multihomed hosts
   1.140 +                //// a host might have more than one address
   1.141 +                //value.forEach(function (addr, iter)
   1.142 +                // set the nameserver address
   1.143 +                response.answer.push(nameinst.A({
   1.144 +                  name: host,
   1.145 +                  address: value,
   1.146 +                  ttl: 600,
   1.147 +                }));
   1.148 +                response.send();
   1.149 +              });
   1.150 +            }
   1.151 +          });
   1.152 +      });
   1.153 +    }
   1.154 +    else {
   1.155 +      response.answer.push(nameinst.A({
   1.156 +      name: request.question[0].name,
   1.157 +      address: '127.0.0.1',
   1.158 +      ttl: 600,
   1.159 +      }));
   1.160 +      response.send();
   1.161 +    }
   1.162 +  });
   1.163 +});
   1.164 +
   1.165 +// DNS error handler logic
   1.166 +nameserv.on('error', function (err, buff, req, res) {
   1.167 +  console.log('DNS problem: ', err.stack);
   1.168 +});
   1.169 +
   1.170 +//// debug process user
   1.171 +//console.log('Start.');
   1.172 +//console.log(process.env.USER);
   1.173 +//console.log(process.env.SUDO_USER);
   1.174 +//console.log('Done.');
   1.175 +//
   1.176 +// <1024 must run privileged
   1.177 +var nudpport = 53; // default DNS
   1.178 +if (nudpport < 1024 && process.getuid() !== 0) {
   1.179 +  //console.log('Serving on port <1024 from an unprivileged user.\nChange to root if using a privileged port number.')
   1.180 +  throw new Error('Serving on port <1024 from an unprivileged user.')
   1.181 +}
   1.182 +
   1.183 +// start the DNS process
   1.184 +nameserv.serve(nudpport, function () {
   1.185 +  try {
   1.186 +    console.log('Starting mDNSGw on', Date());
   1.187 +    process.stdout.write('Old UID: ' + process.getuid() + ', Old GID: ' + process.getgid() + '... ');
   1.188 +    process.umask('0644');
   1.189 +    process.setgid('daemon');
   1.190 +    if (process.env.SUDO_USER)
   1.191 +      process.setuid(process.env.SUDO_USER);
   1.192 +    else
   1.193 +      process.setuid('daemon');
   1.194 +    console.log('New UID: ' + process.getuid() + ', New GID: ' + process.getgid());
   1.195 +  } catch (err) {
   1.196 +    console.log('Cowardly refusing to keep the process alive as root.');
   1.197 +    process.exit(1);
   1.198 +  }
   1.199 +});
   1.200 +
   1.201 +//// debug print all key and value database entries
   1.202 +//rediscli.hgetall('hostnames', function (error, object) {console.dir(object);});
   1.203 +
   1.204 +//// display stored mDNS service data entries
   1.205 +//rediscli.hkeys('hostnames', function (error, replies) {
   1.206 +//  console.log(replies.length + ' replies:');
   1.207 +//  replies.forEach(function (reply, ident) {
   1.208 +//    console.log('    ' + ident + ': ' + reply);
   1.209 +//  });
   1.210 +//});
   1.211 +
   1.212 +//// block executes on program termination
   1.213 +//rediscli.quit();  // cleanup db connection
   1.214 +//browserv.stop();  // zombie scope too bad
   1.215 +//browsall.stop();

mercurial