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.

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

mercurial