mnjstwins
9/11/2018 - 1:43 PM

traceroute clone in javascript

traceroute clone in javascript

// sloppy traceroute clone
// inpired by https://blogs.oracle.com/ksplice/entry/learning_by_doing_writing_your
// and made possible by https://www.npmjs.org/package/raw-socket

var raw = require('raw-socket');
var dns = require('dns');

var target = process.argv[2] || '173.230.146.29';
var MAX_HOPS = 64;
var TIME_LIMIT = 5000;

function resolveFirst(dest, cb) {
  dns.resolve(dest, function (err, addresses) {
    if (err) throw err;
    dns.reverse(addresses[0], function(err, domains) {
      if (err) {
        cb(addresses[0], '');
      } else {
        cb(addresses[0], domains);
      }
    });
  });
}

function ping(dest, ttl, cb) {
  var sentTime;
  var buffer = new Buffer ([
          0x08, 0x00, 0x43, 0x52, 0x00, 0x01, 0x0a, 0x09,
          0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
          0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
          0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x61,
          0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69]);

  var socketLevel = raw.SocketLevel.IPPROTO_IP
  var socketOption = raw.SocketOption.IP_TTL;

  var socket = raw.createSocket();
  socket.setOption(socketLevel, socketOption, ttl);
  socket.send(buffer, 0, buffer.length, dest, function(err, bytes) {
    if (err) throw err;
    sentTime = new Date().getTime();
  });

  var timeout = true;

  socket.on("message", function (buffer, source) {
    socket.close();
    timeout = false;
    cb(buffer, source, (new Date().getTime() - sentTime));
  });

  setTimeout(function() {
    if (timeout) cb(null, '???', TIME_LIMIT);
  }, TIME_LIMIT);
}

function trace(dest, ttl) {
  ping(dest, ttl, function(buffer, source, time) {
    if (time <= TIME_LIMIT && source) {
      resolveFirst(source, function(address, domains) {
        console.log(ttl + '\t' + address + '\t' + domains + '\t' + time + 'ms');
        if (source == dest || ttl == MAX_HOPS) process.exit(0);
        trace(dest, ttl + 1);
      });
    } else {
      console.log(ttl + '\t' + source);
      if (source == dest || ttl == MAX_HOPS) process.exit(0);
      trace(dest, ttl + 1);
    }
  });
}

resolveFirst(target, function(address, domains) {
  console.log('Traceroute to', address, domains);
  trace(address, 1);
});