UNPKG

domotz-remote-pawn

Version:

Domotz Agent

294 lines (237 loc) 10.4 kB
/** This file is part of Domotz Agent. * Copyright (C) 2016 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/>. * *This module executes the scanning of the known TCP Ports for the currently available * devices, * Created by tom on 18/12/15. */ var factory = function (q, proc, nmapCommands, xml2js, engineRequest, configuration, parser, interfacesBindingStorage) { var servicesByIp = {}; var retryInterval = 2000; var maxRetry = 3; var macToIp = {}; var ipToMac = {}; /*** * console overrides * **/ var my_console = new Object(); var engineMonitoredServicesList = null; my_console.debug = console.debug; my_console.info = console.info; my_console.warn = console.warn; my_console.error = console.error; function postResult() { var scanning_result = {}; for (var ip in servicesByIp) { if (servicesByIp.hasOwnProperty(ip)) { scanning_result[ipToMac[ip]] = {}; for (var port in servicesByIp[ip]) { if (servicesByIp[ip].hasOwnProperty(port)) { scanning_result[ipToMac[ip]][port] = servicesByIp[ip][port].curr_status; } } } } my_console.info("TCP_SERVICES: ******* FINAL RESULT IS : " + JSON.stringify(scanning_result)); if (!isEmpty(scanning_result)) { engineRequest.sendWithPromise('/tcp-service/status', 'PUT', scanning_result, function (response) { my_console.info("TCP_SERVICES: result sucessfully uploaded"); my_console.info('TCP_SERVICES: Monitoring cycle ended'); }); } else { my_console.info("TCP_SERVICES: AVOIDING sending update for empty paylod"); } } function showServicesStatus() { my_console.debug('TCP_SERVICES: show services status %s ...', JSON.stringify(servicesByIp)); } function getParseServiceStatus(result, ip) { try { var services_status = parser(result); my_console.debug('TCP_SERVICES (%s): Got result for ip ', ip, ip, JSON.stringify(services_status)); if (!isEmpty(services_status)) { for (var port in services_status) { if (services_status.hasOwnProperty(port)) { servicesByIp[ip][port]["curr_status"] = services_status[port]; } } } } catch (err) { my_console.warn('TCP_SERVICES (%s): WARNING IN PARSING NMAP RESULT for ip ', ip, ip) my_console.warn('TCP_SERVICES (%s): ', ip, err.message); } } function isEmpty(value) { return Object.keys(value).length === 0; } function getPortStringForIp(ip) { var portListAsString = Object.keys(servicesByIp[ip]).toString(); my_console.debug("TCP_SERVICES (%s): Port String : %s ", ip, portListAsString); return portListAsString; } function getPortStringForIpOnlyOffLine(ip) { var portListOffLine = []; for (var service in servicesByIp[ip]) { if (servicesByIp[ip].hasOwnProperty(service)) { if (servicesByIp[ip][service].curr_status !== "ONLINE" && servicesByIp[ip][service].prev_status === "ONLINE" || servicesByIp[ip][service].prev_status === "UNKNOWN") { portListOffLine.push(service); } } } var portListAsString = portListOffLine.toString(); my_console.debug("TCP_SERVICES (%s): Port String (filtered): %s", ip, portListAsString); return portListAsString; } function discoverServicesStatus(ip, portString) { var cmd = nmapCommands.getExplicitPortNmapCmd(ip, portString); my_console.info("TCP_SERVICES (%s): Scanning IP ports of device: %s", ip, ip); my_console.info("TCP_SERVICES (%s): command is '%s'", ip, cmd); var execPromise = proc.exec(cmd) .then(xml2js.toJSON) .then(function (result) { my_console.debug("TCP_SERVICES (%s): nmap scanning result: %s", ip, JSON.stringify(result)); getParseServiceStatus(result, ip); my_console.debug("TCP_SERVICES (%s): resolving promise for ip: %s", ip, ip); }) .catch(function (error) { my_console.error('TCP_SERVICES (%s): Error on collecting nmap data for ip %s', ip, ip); my_console.error(error.stack); }); return execPromise; } function discoverServicesStatusWithRetry(ip, portString) { var count = maxRetry; var deferred = q.defer(); function _retry() { if (count < maxRetry) { portString = getPortStringForIpOnlyOffLine(ip); } if (portString === "") { my_console.debug("TCP_SERVICES (%s): Exiting retry loop, no further analysis is necessary", ip); return deferred.resolve(); } if (count === 0) { my_console.debug("TCP_SERVICES (%s): Exiting retry loop, count : %s", ip, count); return deferred.resolve(); } return q.delay(retryInterval).then(function () { my_console.debug("TCP_SERVICES (%s): Trying to execute discovery : %s/%s", ip, (maxRetry - count + 1), maxRetry); count--; return discoverServicesStatus(ip, portString).then(_retry); }); } q.nextTick(_retry); return deferred.promise; } function translateToIp() { macToIp = {}; ipToMac = {}; var interfaceInfo = interfacesBindingStorage.getInterfaceInfo(); for (var mac in interfaceInfo) { if (interfaceInfo.hasOwnProperty(mac)) { macToIp[mac] = interfaceInfo[mac]; ipToMac[interfaceInfo[mac]] = mac; } } my_console.debug('TCP_SERVICES: mac translation table ' + JSON.stringify(macToIp)); } function invalidateList() { "use strict"; my_console.info("TCP_SERVICES: invalidating monitored devices list"); engineMonitoredServicesList = null; } function remotely_get_service_status(callback) { var parseResult = function (response) { my_console.info("TCP_SERVICES: using engine response %s ", JSON.stringify(response.body)); engineMonitoredServicesList = response; translateToIp(); servicesByIp = {}; if (!isEmpty(macToIp)) { for (var mac in response.body) { if (response.body.hasOwnProperty(mac)) { if (macToIp[mac]) { servicesByIp[macToIp[mac]] = {}; for (var port in response.body[mac]) { if (response.body[mac].hasOwnProperty(port)) { servicesByIp[macToIp[mac]][port] = { "prev_status": response.body[mac][port], "curr_status": "OFFLINE" // as a default, ensures that the final status is OFFLINE even if no data is actually collected about it }; } } } else { my_console.warn("TCP_SERVICES: CANNOT TRANSLATE MAC TO IP " + mac); } } } my_console.debug("TCP_SERVICES: services by id status: " + JSON.stringify(servicesByIp)); callback(); } else { my_console.warn("TCP_SERVICES: mac-ip translation table is empty, cannot proceed "); } }; if (engineMonitoredServicesList !== null) { my_console.debug('TCP_SERVICES: using cached tcp service status ...'); parseResult(engineMonitoredServicesList); }else { my_console.debug('TCP_SERVICES: requesting tcp service status to engine ...'); engineRequest.sendWithPromise('/tcp-service', 'GET', {}, parseResult); } } var locked = false; function monitor() { if (!locked) { try { locked = true; servicesByIp = {}; my_console.info('TCP_SERVICES: Monitoring cycle started ...'); remotely_get_service_status(function () { my_console.debug("TCP_SERVICES: activating monitor cycle ..."); var promises = []; for (var ip in servicesByIp) { if (servicesByIp.hasOwnProperty(ip)) { var portString = getPortStringForIp(ip); promises.push(discoverServicesStatusWithRetry(ip, portString)); } } q.all(promises) .then(function () { showServicesStatus(); postResult(); }).finally(function () { //my_console.debug(promises); }); }); } catch (err) { my_console.error('TCP_SERVICES: ' + err.name + ' ' + err.message); } finally { locked = false; } } } return { monitor: monitor, invalidateList: invalidateList }; }; module.exports.factory = factory;