UNPKG

domotz-remote-pawn

Version:

Domotz Agent

525 lines (430 loc) 21.6 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/>. **/ /** * * Created by Iacopo Papalini <ipapalini@domotz.com> on 16/06/15. */ // External Dependencies var amqplib = require('amqplib'); var async = require('async'); var bodyParser = require('body-parser'); var childProcess = require('child_process'); var crypto = require('crypto'); var deepEqual = require('deep-equal'); var dgram = require('dgram'); var dhcpjs = require('dhcpjs'); var dns = require('dns'); var express = require('express'); var events = require('events'); var favicon = require('serve-favicon'); var fs = require('fs'); var http = require('http'); var ip = require('ip'); var moment = require('moment-timezone'); var net = require('net'); var netSnmp = require('net-snmp'); var os = require('os'); var path = require('path'); var rootRequire = require('root-require'); var q = require('q'); var telnet = require('telnet-client'); var ssdp = require('node-ssdp'); var step = require('step'); var url = require('url'); var util = require('util'); var xml2js = require('xml2js'); var xml2jsProcessor = require('xml2js/lib/processors.js'); var whois = require('node-whois'); var winston = require('winston'); var ws = require("nodejs-websocket"); var _ = require('underscore'); // Internal Dependencies (no need for further initialization) var pkgJson = rootRequire('package.json'); var event = rootRequire('src/events/utils'); var web = rootRequire('src/http/utils'); var request = rootRequire('src/http/request'); var mutex = rootRequire('src/utils/mutex'); var time = rootRequire('src/utils/time'); var command = rootRequire('src/utils/command'); var dto = rootRequire('src/utils/dto'); var number = rootRequire('src/utils/number'); var dataStructure = rootRequire('src/utils/data_structure'); var scheduler = rootRequire('src/utils/scheduler'); var geoLocation = require('../network_tools/geolocation'); var commandHttpRequest = require('../commands/httpRequest'); function initFileManager() { return require('../persistence/file').factory(fs, q); } function getCacheUtils(procManager) { return require('../persistence/cache').factory(procManager, fs); } function getRandomUtils() { return require('../utils/random').factory(crypto); } function getHubConfigurator(request, file, web) { return require('../bootstrap/configuration').factory(q, request, file, web); } function initStatusManager(configuration) { return require('../bootstrap/status').factory(configuration); } function initLog(file, proc, configuration) { return require('../bootstrap/logger').factory(_, winston, util, path, file, proc, configuration); } function initExternalProcessesManager() { var Queue = require('../external_processes/queue'); var cpu = require('../external_processes/cpu').factory(_, childProcess, Queue); return require('../external_processes/proc').factory(q, fs, childProcess, cpu); } function getHostInterfaces(procManager, fileManager) { var networkInterfaces, customInterfaces = require('../discovery/host/custom').factory(_, fileManager); var netUtilsPath = path.join(process.env.DOMOTZ_BIN_DIR, 'domotz_network_utils'); var v2 = fileManager.getFileStats(netUtilsPath); if (!v2) { var nic = require('../discovery/host/nic').factory(os, util); networkInterfaces = require('../discovery/host/interfaces').factory(q, procManager, nic); } return require('../discovery/host').factory(procManager, ip, networkInterfaces, customInterfaces); } function setupUpdateManager(log, status, random, file, proc, mutex, event, engineRequest) { return require('../package/update').factory(q, path, random, http, log, status, file, proc, mutex, event, engineRequest); } function getAmqpListener(proc, status, configuration, eventHandler, resourceLocator) { var amqpParser = require('../amqp/parser'); var amqpCommand = require('../amqp/command').factory(resourceLocator, amqpParser); var amqpHandler = require('../amqp/handler').factory(amqpCommand); var amqpConnection = require('../amqp/connection').factory(amqplib, util, configuration); var amqpConsumer = require('../amqp/consumer').factory(configuration, amqpCommand, amqpHandler); var amqpChannel = require('../amqp/channel').factory(eventHandler); return require('../amqp/listener').factory(proc, status, amqpConnection, amqpConsumer, amqpChannel); } function initAvahiResolver(proc, engineRequest, devicesInfo) { var avahiCommands = require('../discovery/avahi/commands').factory(); return require('../discovery/avahi/resolve').factory(engineRequest, devicesInfo, avahiCommands, proc); } function prepareNmapTranslator() { return require('../discovery/nmap/xml2js').factory(q, xml2js); } function setupPortDiscovery(resourceLocator, nmapXml2Js, interfacesBindingStorage, nmapCommands) { var portParser = require('../discovery/ports/parser'); return require('../discovery/ports/map').factory(resourceLocator, nmapXml2Js, interfacesBindingStorage, nmapCommands, portParser); } function setupTcpServicesMonitoring(proc, nmapXml2js, nmapCommands, interfacesBindingStorage, engineRequest, configuration) { var tcpServicesParser = require('../discovery/tcp_services/parser'); return require('../discovery/tcp_services/tcp_monitor').factory(q, proc, nmapCommands, nmapXml2js, engineRequest, configuration, tcpServicesParser, interfacesBindingStorage); } function setupDiscoveryUtils(interfacesBindingStorage, devicesInfo) { return require('../discovery/utils').factory(interfacesBindingStorage, devicesInfo); } function setupProtocolsDiscoveryOrchestrator(domotzApp) { var discoverySenderFactory = require('../discovery/protocols/discovery_sender').factory; return require('../discovery/protocols/protocols_discovery_orchestrator').factory(domotzApp, discoverySenderFactory); } function setupBonjourDiscovery() { return require('../discovery/bonjour/bonjour_discovery').factory(mutex); } function setupUpnpDeviceDiscovery(resourceLocator) { var messageBuilder = require('../discovery/upnp/message').factory(resourceLocator); var responseParser = require('../discovery/upnp/parser').factory(resourceLocator, messageBuilder); var deferredRequestHandler = require('../discovery/upnp/deferred_request').factory(resourceLocator, messageBuilder); return require('../discovery/upnp/upnp_discovery').factory(resourceLocator, messageBuilder, responseParser, deferredRequestHandler); } function setupSnmpBasicDiscovery(resourceLocator) { var commandBuilder = require('../discovery/snmp/command_builder').factory(resourceLocator); var responseRendering = require('../discovery/snmp/response_processing').renderSnmpBasicData; var snmpGetterFactory = require('../commands/snmp/get.js').command; return require('../discovery/snmp/snmp_basic_discovery').factory(resourceLocator, commandBuilder, responseRendering, snmpGetterFactory); } function setupSnmpInterfaceDiscovery(resourceLocator) { var commandBuilder = require('../discovery/snmp/command_builder').factory(resourceLocator); var responseRendering = require('../discovery/snmp/response_processing').renderSnmpInterfaceData; var snmpSubtreeFactory = require('../commands/snmp/subtrees.js').command; return require('../discovery/snmp/snmp_interface_discovery').factory(resourceLocator, commandBuilder, responseRendering, snmpSubtreeFactory); } function setupDhcpDiscovery(random, discoveryUtils, engineRequest) { return require('../discovery/dhcp/dhcp_discovery').factory(dhcpjs, random, discoveryUtils, engineRequest); } function setPeriodicalTasks(status, random, time, eventHandler, configuration) { return require('../events/cron').factory(time, status, eventHandler, configuration, random); } function initAPIClient(request, configuration, endpoint) { return require('../api_client/engineRequest').factory(q, request, configuration, endpoint); } function initEvents(events, domotzApp) { return require('../events/eventHandler').factory(events, domotzApp); } function setupDeviceAnalyzer(configuration, engineRequest, devicesInfo, interfacesBindingStorage) { return require('../devices/analyze').factory(configuration, engineRequest, devicesInfo, interfacesBindingStorage); } function initDevicesCache(time, engineRequest, configuration) { return require('../devices/info').factory(q, async, time, engineRequest, configuration); } function createWebapp(status, configuration, eventHandler, domotzApp) { var routes = require('../http/routes/index').create(express, status, configuration, domotzApp); return require('../http/app').createApp(fs, http, express, eventHandler, bodyParser, favicon, path, routes); } function createWsServer(ws) { return require('../websockets/wsServer').wsServer(ws); } function setSignalHandlers(proc, log, status, engineRequest) { require('../bootstrap/signals').setHandler(proc, log, status, event, engineRequest); } function initUserEntity(userRequest, registerRequest) { return require('../api_client/entities/user').factory(userRequest, registerRequest); } function initAgentEntity(random, web, host, configuration, userRequest) { return require('../api_client/entities/agent').factory(random, url, web, host, configuration, userRequest); } function initNotConfiguredAgentEntity(host, pkgJson, registerRequest) { return require('../api_client/entities/notConfiguredAgent').factory(host, pkgJson, registerRequest); } function initSocketListener() { var requestHandler = require('../http/socketHandler'); return require('../http/socketListener').socketListener(http, fs, requestHandler); } function setupUpnpRegistry(deepEqual) { return require('../upnp/upnpRegistry').registry(deepEqual); } function getNetInfoModule(proc, file, hostInfo) { return require('../network_tools/netinfo').factory(os, path, http, dns, whois, proc, file, hostInfo); } function initUpnpEventHandler() { return require('../upnp/upnpEventHandler'); } function setupMtrTool(proc, dto) { return require('../network_tools/mtr').factory(_, proc, dto); } function initUdpListener(request, configuration) { return require('../udp/listener').factory(crypto, dgram, request, configuration); } function getUdpUtils() { return require('../udp/utils').factory(dgram); } function initControl4Driver(sshTool) { return require('../discovery/zigbee/control4').factory(async, q, sshTool, _, util); } function getSshTool(proc) { return require('../network_tools/ssh').factory(proc); } function initFingDriver(engineRequest) { return require('../network_tools/fingJamDevices').fingWrapper(childProcess, engineRequest); } function initStallWatcher(){ "use strict"; return require('../external_processes/stallWatcher').init(os, childProcess, fs); } function setupNdtServersFinder(request, random, cache, configuration) { var distanceCalculator = require('../network_tools/earthDistance').distanceCalculator(configuration); return require('../network_tools/ndtServers').factory(q, cache, configuration, request, random, distanceCalculator); } function setupSpeedTestTool(proc, dto, number) { return require('../network_tools/speedTest').factory(_, proc, dto, number); } function initDiscoveryCoordinator(util, proc, hostInfo, eventHandler, nmapCommands, nmapXml2Js, interfacesBindingStorage, engineRequest, configuration) { "use strict"; var ipDiscoveryParser = require('../discovery/ip_network/discovery_parser').factory(interfacesBindingStorage); var bindingDiscoveryFactory = require('../discovery/ip_network/bind_discovery').factory(q, util, proc, hostInfo, nmapCommands, nmapXml2Js, ipDiscoveryParser); var engineReportingStrategy = require('../discovery/ip_network/engine_report_strategy').engineIpDiscoveryReportStrategyFactory(q, engineRequest, interfacesBindingStorage, configuration); return require('../discovery/ip_network/discovery_coordinator').factory(q, configuration, eventHandler, bindingDiscoveryFactory, interfacesBindingStorage, engineReportingStrategy); } function getNmapCommands(file, configuration, interfacesBindingStorage) { var nmapBlackList = require('../discovery/nmap/blackList').factory(file, configuration, interfacesBindingStorage); return require('../discovery/nmap/commands').factory(ip, configuration, nmapBlackList); } function initBindingsCache(fileManager, engineRequest, hostInfo) { return require('../discovery/ip_network/interfaces_binding').factory(q, util, fileManager, engineRequest, hostInfo); } function setupOoklaServerFinder(domotzApp) { return require('../network_tools/ooklaServers').factory(domotzApp); } function setupConnectivityChecker(domotzApp) { return require('../network_tools/connectivity').factory(domotzApp); } function initUpnpSubscriptionManager(domotzApp) { return require('../upnp/upnpSubscriptionsManager').factory(domotzApp); } function createUpnpServer(domotzApp) { return require('../upnp/upnpHttpServer').httpServer(domotzApp); } module.exports.create = function (customLogger) { var domotzApp = {}; // Npm Modules (for command or events that needs stuff at runtime) domotzApp.async = async; domotzApp.crypto = crypto; domotzApp.dhcpjs = dhcpjs; domotzApp.fs = fs; domotzApp.http = http; domotzApp.moment = moment; domotzApp.netSnmp = netSnmp; domotzApp.net = net; domotzApp.path = path; domotzApp.q = q; domotzApp.request = request; domotzApp.telnet = telnet; domotzApp.step = step; domotzApp.ssdp = ssdp; domotzApp.url = url; domotzApp.util = util; domotzApp.xml2js = xml2js; domotzApp.xml2jsProcessor = xml2jsProcessor; domotzApp._ = _; // Utils var fileManager = initFileManager(); var random = getRandomUtils(); var procManager = initExternalProcessesManager(); var cacheUtils = getCacheUtils(procManager); domotzApp.utils = { proc: procManager, random: random, file: fileManager, mutex: mutex, scheduler: scheduler, web: web, time: time, event: event, command: command, dto: dto, number: number, cache: cacheUtils, dataStructure: dataStructure }; // Pawn Configuration var configurator = getHubConfigurator(request, fileManager, web); var configuration = configurator.loadConfiguration(); domotzApp.configurator = configurator; domotzApp.configuration = configuration; // Status Manager var status = initStatusManager(configuration); domotzApp.status = status; // Log Setup var domotzLog = initLog(fileManager, procManager, configuration); domotzApp.log = customLogger || domotzLog; // Host Information var host = getHostInterfaces(procManager, fileManager); domotzApp.discovery = { host: host }; // API Clients. For OpenAPI limit the number of Sockets http.globalAgent.maxSockets = 100; var registerRequest = initAPIClient(request, configuration, 'register'); var userRequest = initAPIClient(request, configuration, 'user'); var engineRequest = (status.get() === status.CONFIGURED) ? initAPIClient(request, configuration, 'agent') : null; domotzApp.registerRequest = registerRequest; domotzApp.userRequest = userRequest; domotzApp.engineRequest = engineRequest; http.globalAgent.maxSockets = Infinity; // Events Management var eventHandler = initEvents(events, domotzApp); domotzApp.event = eventHandler; setSignalHandlers(procManager, domotzLog, status, engineRequest); // Package Management domotzApp.pkg = { update: setupUpdateManager(domotzLog, status, random, fileManager, procManager, mutex, event, engineRequest), json: pkgJson }; // NetworkTools domotzApp.networkTools = { geolocation: geoLocation }; // Entities domotzApp.entities = { agent: initAgentEntity(random, web, host, configuration, userRequest), notConfiguredAgent: initNotConfiguredAgentEntity(host, pkgJson, registerRequest), user: initUserEntity(userRequest, registerRequest) }; // Web Sockets server domotzApp.wsServer = createWsServer(ws); // Internal Web Server domotzApp.webapp = createWebapp(status, configuration, eventHandler, domotzApp); domotzApp.distanceCalculator = require('../network_tools/earthDistance').distanceCalculator(configuration); // Periodic Tasks domotzApp.periodicalTasks = setPeriodicalTasks(status, random, time, eventHandler, configuration); if (status.get() === status.CONFIGURED) { var devicesInfo = initDevicesCache(time, engineRequest, configuration); var interfacesBindingStorage = initBindingsCache(fileManager, engineRequest, host); var discoveryUtils = setupDiscoveryUtils(interfacesBindingStorage, devicesInfo); var nmapCommands = getNmapCommands(fileManager, configuration, interfacesBindingStorage); var nmapXml2Js = prepareNmapTranslator(q, xml2js); var sshTool = getSshTool(procManager); // Amqp manager domotzApp.amqpListener = getAmqpListener(procManager, status, configuration, eventHandler, domotzApp); // Engine Clients domotzApp.heartbeatRequest = initAPIClient(request, configuration, 'agent-heartbeat'); // IP Discovery - Bindings Cache domotzApp.interfacesBindingStorage = interfacesBindingStorage; // Device Utilities domotzApp.devices = { analyze: setupDeviceAnalyzer(configuration, engineRequest, devicesInfo, interfacesBindingStorage), info: devicesInfo }; // Discovery Engine domotzApp.discovery.utils = discoveryUtils; domotzApp.discovery.avahi = initAvahiResolver(procManager, engineRequest, devicesInfo); domotzApp.discovery.port = setupPortDiscovery(domotzApp, nmapXml2Js, interfacesBindingStorage, nmapCommands); domotzApp.discovery.tcp_services_monitoring = setupTcpServicesMonitoring(procManager, nmapXml2Js, nmapCommands, interfacesBindingStorage, engineRequest, configuration); domotzApp.discovery.ip_network = { discoveryCoordinator: initDiscoveryCoordinator(util, procManager, host, eventHandler, nmapCommands, nmapXml2Js, interfacesBindingStorage, engineRequest, configuration) }; domotzApp.discovery.dhcp_discovery = setupDhcpDiscovery(random, discoveryUtils, engineRequest); domotzApp.discovery.bonjour_discovery = setupBonjourDiscovery(domotzApp); domotzApp.discovery.upnp_device_discovery = setupUpnpDeviceDiscovery(domotzApp); domotzApp.discovery.snmp_basic_discovery = setupSnmpBasicDiscovery(domotzApp); domotzApp.discovery.snmp_interface_discovery = setupSnmpInterfaceDiscovery(domotzApp); domotzApp.discovery.protocols_orchestrator = setupProtocolsDiscoveryOrchestrator(domotzApp); // Socket Http Server domotzApp.socketListener = initSocketListener(eventHandler); // Net Tools domotzApp.networkTools.netInfo = getNetInfoModule(procManager, fileManager, host); domotzApp.networkTools.mtr = setupMtrTool(procManager, dto); domotzApp.networkTools.ndtServers = setupNdtServersFinder(request, random, cacheUtils, configuration); domotzApp.networkTools.ooklaServers = setupOoklaServerFinder(domotzApp); domotzApp.networkTools.speedTest = setupSpeedTestTool(procManager, dto, number); domotzApp.networkTools.internetConnectionStatus = setupConnectivityChecker(domotzApp); domotzApp.networkTools.ssh = sshTool; // Http command interface domotzApp.commandHttpRequest = commandHttpRequest; // Upnp Server domotzApp.upnp = { registry: setupUpnpRegistry(deepEqual), eventHandler: initUpnpEventHandler(), subscriptionsManager: initUpnpSubscriptionManager(domotzApp), httpServer: createUpnpServer(domotzApp) }; // Udp Listener domotzApp.udp = { utils: getUdpUtils(), listener: initUdpListener(request, configuration) }; // Control4 domotzApp.control4Driver = initControl4Driver(sshTool); // Fing domotzApp.fingWrapper = initFingDriver(engineRequest); // Self check domotzApp.stallWatcher = initStallWatcher(); } else { console.warn("INIT - Hub is not configured (invalid or absent conf file). Running in restricted mode"); } var i = 0; for (var property in domotzApp) { if (domotzApp.hasOwnProperty(property)) { console.verbose('INIT - --> ' + property + ';'); i++; } } console.info('INIT - Finish Initialization of Domotz Application. %d modules loaded',i); return domotzApp; };