natman.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. var natpmp = require('nat-pmp'),
  2. natupnp = require('nat-upnp'),
  3. netroute = require('netroute');
  4. flatiron = require('flatiron'),
  5. os = require('os'),
  6. colors = require('colors'),
  7. app = flatiron.app,
  8. natman = exports;
  9. var Nat = exports.Nat = function (options) {
  10. this.clients = {
  11. pmp: natpmp.connect(options.gateway),
  12. upnp: natupnp.createClient()
  13. };
  14. return this;
  15. }
  16. natman.createNat = function (options, callback) {
  17. options = options || {};
  18. options.gateway = options.gateway || netroute.getGateway();
  19. options.private = options.private || 22;
  20. options.public = options.public || 8888;
  21. options.ttl = options.ttl || 3600;
  22. var nat = new Nat(options);
  23. nat.open(options);
  24. };
  25. Nat.prototype.open = function (options, callback) {
  26. this.map(options, callback);
  27. };
  28. Nat.prototype.map = function (options, callback) {
  29. var self = this;
  30. callback = callback || function () {};
  31. app.log.info('Creating NAT...'.green);
  32. app.log.warn('Using gateway: '.yellow + options.gateway.magenta);
  33. app.log.help('You can manually specify a gateway with -g argument'.cyan)
  34. app.log.warn('Attempting to connect... '.yellow);
  35. function onConnect(client, external) {
  36. var portInfo = {
  37. private: options.private,
  38. public: options.public,
  39. ttl: client === self.clients.upnp ? 0 : options.ttl
  40. };
  41. app.log.info('Connected to WAN address: '.green +
  42. external.magenta)
  43. app.log.warn('Mapping '.yellow + external.magenta +
  44. ':'.magenta + options.public.toString().magenta +
  45. ' => '.yellow + 'localhost:'.magenta +
  46. options.private.toString().magenta);
  47. return client.portMapping(portInfo, function(err, info){
  48. if(err) {
  49. app.log.error(err);
  50. return callback(err);
  51. }
  52. // UPNP Returns server's response in info object
  53. if (!info.public) info = options;
  54. if(Number(options.public) !== Number(info.public)) {
  55. app.log.error(external.magenta + ':'.magenta +
  56. options.public.toString().magenta + ' unavailable'.red);
  57. app.log.warn('Auto-assigning public port...'.yellow);
  58. app.log.info('Mapped '.green + external.magenta +
  59. ':'.magenta + info.public.toString().magenta +
  60. ' => '.green + 'localhost:'.magenta +
  61. options.private.toString().magenta);
  62. }
  63. app.log.info('NAT established.'.green);
  64. app.log.warn('To stop forwarding, you may have to restart any exposed services'.yellow);
  65. app.log.info('Press CTRL-C to stop the NAT...');
  66. process.once('SIGINT', function() {
  67. client.portUnmapping(portInfo, function() {
  68. // Ignore errors for now
  69. process.exit(0);
  70. });
  71. });
  72. return callback(err, info);
  73. });
  74. }
  75. var once = false,
  76. waiting = Object.keys(this.clients).length;
  77. Object.keys(this.clients).forEach(function(key) {
  78. var client = self.clients[key];
  79. client.externalIp(function(err, external) {
  80. waiting--;
  81. if(once) return;
  82. // Ignore errors if we have reserve choices
  83. if(err && waiting > 0) return;
  84. // Invoke callback only once
  85. once = true;
  86. if(err) {
  87. app.log.error(err);
  88. return callback(err);
  89. }
  90. if (typeof external === 'string') {
  91. onConnect(client, external);
  92. } else {
  93. onConnect(client, external.ip.join('.'));
  94. }
  95. });
  96. });
  97. };