UNPKG

domotz-remote-pawn

Version:

Domotz Agent

203 lines (166 loc) 5.53 kB
var nat = require('../nat-upnp'); var async = require('async'); var client = exports; function Client() { this.ssdp = nat.ssdp.create(); this.timeout = 1800; } client.create = function create() { return new Client(); }; function normalizeOptions(options) { function toObject(addr) { if (typeof addr === 'number') return { port: addr }; if (typeof addr === 'string' && !isNaN(addr)) return { port: Number(addr) }; if (typeof addr === 'object') return addr; return {}; } return { remote: toObject(options.public), internal: toObject(options.private) }; } Client.prototype.portMapping = function portMapping(options, callback) { if (!callback) callback = function() {}; this.findGateway(function(err, gateway, address) { if (err) return callback(err); var ports = normalizeOptions(options); var ttl = 60 * 30; if (typeof options.ttl === 'number') { ttl = options.ttl; } if (typeof options.ttl === 'string' && !isNaN(options.ttl)) { ttl = Number(options.ttl); } gateway.run('AddPortMapping', [ [ 'NewRemoteHost', ports.remote.host ], [ 'NewExternalPort', ports.remote.port ], [ 'NewProtocol', options.protocol ? options.protocol.toUpperCase() : 'TCP' ], [ 'NewInternalPort', ports.internal.port ], [ 'NewInternalClient', ports.internal.host || address ], [ 'NewEnabled', 1 ], [ 'NewPortMappingDescription', options.description || 'node:nat:upnp' ], [ 'NewLeaseDuration', ttl ] ], callback); }); }; Client.prototype.portUnmapping = function portMapping(options, callback) { if (!callback) callback = function() {}; this.findGateway(function(err, gateway/*, address*/) { if (err) return callback(err); var ports = normalizeOptions(options); gateway.run('DeletePortMapping', [ [ 'NewRemoteHost', ports.remote.host ], [ 'NewExternalPort', ports.remote.port ], [ 'NewProtocol', options.protocol ? options.protocol.toUpperCase() : 'TCP' ] ], callback); }); }; Client.prototype.getMappings = function getMappings(options, callback) { if (typeof options === 'function') { callback = options; options = null; } if (!options) options = {}; this.findGateway(function(err, gateway, address) { if (err) return callback(err); var i = 0; var end = false; var results = []; async.whilst(function() { return !end; }, function(callback) { gateway.run('GetGenericPortMappingEntry', [ [ 'NewPortMappingIndex', i++ ] ], function(err, data) { if (err) { // If we got an error on index 0, ignore it in case this router starts indicies on 1 if (i !== 1) { end = true; } return callback(null); } var key; Object.keys(data).some(function(k) { if (!/:GetGenericPortMappingEntryResponse/.test(k)) return false; key = k; return true; }); data = data[key]; var result = { public: { host: typeof data.NewRemoteHost === 'string' && data.NewRemoteHost || '', port: parseInt(data.NewExternalPort, 10) }, private: { host: data.NewInternalClient, port: parseInt(data.NewInternalPort, 10) }, protocol: data.NewProtocol.toLowerCase(), enabled: data.NewEnabled === '1', description: data.NewPortMappingDescription, ttl: parseInt(data.NewLeaseDuration, 10) }; result.local = result.private.host === address; results.push(result); callback(null); }); }, function(err) { if (err) return callback(err); if (options.local) { results = results.filter(function(item) { return item.local; }); } if (options.description) { results = results.filter(function(item) { if (typeof item.description !== 'string') return; if (options.description instanceof RegExp) { return item.description.match(options.description) !== null; } else { return item.description.indexOf(options.description) !== -1; } }); } callback(null, results); }); }); }; Client.prototype.externalIp = function externalIp(callback) { this.findGateway(function(err, gateway/*, address*/) { if (err) return callback(err); gateway.run('GetExternalIPAddress', [], function(err, data) { if (err) return callback(err); var key; Object.keys(data).some(function(k) { if (!/:GetExternalIPAddressResponse$/.test(k)) return false; key = k; return true; }); if (!key) return callback(Error('Incorrect response')); callback(null, data[key].NewExternalIPAddress); }); }); }; Client.prototype.findGateway = function findGateway(callback) { var timeout; var timeouted = false; var p = this.ssdp.search( 'urn:schemas-upnp-org:device:InternetGatewayDevice:1' ); timeout = setTimeout(function() { timeouted = true; p.emit('end'); callback(new Error('timeout')); }, this.timeout); p.on('device', function (info, address) { if (timeouted) return; p.emit('end'); clearTimeout(timeout); // Create gateway callback(null, nat.device.create(info.location), address); }); }; Client.prototype.close = function close() { this.ssdp.close(); };