UNPKG

domotz-remote-pawn

Version:

Domotz Agent

164 lines (147 loc) 6.72 kB
/** This file is part of Domotz Agent. * Copyright (C) 2018 Domotz Ltd * * Domotz Agent is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Domotz Agent is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>. **/ /** * * Created by Andrea Azzara <a.azzara@domotz.com> on 20/03/18. */ const SSDP_SEARCH_TIMEOUT = 5000; const CACHE_EXPIRATION_HOURS = 0.5; // 30 Minutes module.exports.factory = function (resourceLocator, engineRequest) { var myConsole = resourceLocator.log.decorateLogs(); var natUpnp = resourceLocator.natUpnp; var lodash = resourceLocator.lodash; var async = resourceLocator.async; var interfacesBindingStorage = resourceLocator.interfacesBindingStorage; var expirationInNHours = resourceLocator.httpOutCache.expirationInNHours; function discover() { myConsole.info('Discover called'); var client = natUpnp.createClient(); var searchUtil = client.ssdp.search('urn:schemas-upnp-org:device:InternetGatewayDevice:1'); setTimeout(function () { searchUtil.emit('end'); }, SSDP_SEARCH_TIMEOUT); searchUtil.on('device', function (info) { var gateway = natUpnp.device.create(info.location); myConsole.info('Gateway found: %s', gateway.description); factory._discoverAndReportGatewayMappings(gateway); }); } function _discoverAndReportGatewayMappings(gateway) { if (!gateway || !gateway.description) { return; } myConsole.debug('discoverAndReportGatewayMappings'); var gatewayIpRegexp = /^http[s]?\:\/\/(\d*\.\d*\.\d*\.\d*)\:\d*\/.*$/gm; var parsedGatewayDescription = gateway.description.match(gatewayIpRegexp); if (parsedGatewayDescription !== null && parsedGatewayDescription.length > 0) { var gatewayIp = gatewayIpRegexp.exec(gateway.description)[1]; var gatewayMac = interfacesBindingStorage.getMacFromIp(gatewayIp); if (gatewayMac !== undefined) { factory._discoverGatewayMappings(gateway, function (results) { factory._postResults(gatewayMac, results); }); } else { myConsole.debug('No matching MAC for device: %s', gatewayIp); } } } function _discoverGatewayMappings(gateway, callback) { var i = 0; var finished = false; var results = {}; async.whilst( function () { return !finished; }, function (done) { 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) { finished = true; } done(null); return; } var key = null; var found = Object.keys(data).some(function (k) { if (!/:GetGenericPortMappingEntryResponse/.test(k)) { return false; } key = k; return true; }); if (!found) { done(null); return; } data = data[key]; var hostIp = data.NewInternalClient; var hostMac = interfacesBindingStorage.getMacFromIp(hostIp); if (hostMac !== undefined) { var internalPort = parseInt(data.NewInternalPort, 10); var externalPort = parseInt(data.NewExternalPort, 10); var protocol = data.NewProtocol.toUpperCase(); var description = data.NewPortMappingDescription; myConsole.debug('Found port forwarding: %s %s->%s:%s', protocol, externalPort, hostIp, internalPort); if (results[hostMac + ':' + externalPort] === undefined) { results[hostMac + ':' + externalPort] = { mac: hostMac, int_port: internalPort, ext_port: externalPort, protocol: protocol, description: data.NewPortMappingDescription, }; } else { var protocols = [results[hostMac + ':' + externalPort].protocol, protocol].sort().join('/'); // TCP/UDP results[hostMac + ':' + externalPort].protocol = protocols; if (description !== results[hostMac + ':' + externalPort].description) { var descriptions = [results[hostMac + ':' + externalPort].description, description].sort().join(', '); results[hostMac + ':' + externalPort].description = descriptions; } } } done(null); }); }, function () { callback(lodash.values(results)); } ); } function _postResults(gatewayMac, results) { myConsole.debug('Preparing engine request with payload: %s', JSON.stringify(results)); var options = { cache_until: expirationInNHours(CACHE_EXPIRATION_HOURS), }; engineRequest.cachedSendWithPromise( '/subnet/ip/device/' + gatewayMac + '/protocol/upnp-igd/forward', 'PUT', results, undefined, undefined, options ); } var factory = { discover: discover, _discoverAndReportGatewayMappings: _discoverAndReportGatewayMappings, _discoverGatewayMappings: _discoverGatewayMappings, _postResults: _postResults, }; return factory; };