domotz-remote-pawn
Version:
Domotz Agent
323 lines (281 loc) • 12.4 kB
JavaScript
/** 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 Tommaso Latini <tommaso@domotz.com> on 27/08/15.
*/
const IPV4_BITS = 32;
const DOMOTZ_NETMASK = 24;
const SCANNED_IP_PER_NMAP = Math.pow(2, IPV4_BITS - DOMOTZ_NETMASK);
var NMAP_TYPE = {
DEFAULT: 'default',
DISABLE_ARP_PING: 'disable_arp_ping',
EXTERNAL_HOST: 'external_host',
EXTERNAL_SUBNET: 'external_subnet',
EXTERNAL_SUBNET_PING_ONLY: 'disable_arp_ping',
};
/*
The default command for external host discovery includes ICMP echo, ICMP timestamp, TCP ack/syn and UDP with source port,
it is described as the most effective command for host discovery.
source: https://nmap.org/book/host-discovery-strategies.html#host-discovery-tbl-best-combinations
*/
var DEFAULT_NMAP_COMMANDS_EXT_HOSTS = {
PE: {
enabled: true,
},
PS: {
enabled: true,
ports: [443, 80, 3389],
},
PP: {
enabled: true,
},
PA: {
enabled: true,
ports: [21],
},
PU: {
enabled: true,
ports: [40125, 161],
},
source_port: {
enabled: true,
port: 53,
},
};
var DEFAULT_NMAP_COMMANDS_EXT_SUBNET = {
PE: {
enabled: true,
},
PS: {
enabled: false,
ports: [443, 80, 3389],
},
PP: {
enabled: false,
},
PA: {
enabled: false,
ports: [21],
},
PU: {
enabled: false,
ports: [40125, 161],
},
};
var _ = require('lodash');
function getPortString(discovery) {
return _.sortBy(discovery.ports).join(',');
}
function getPortUDPString(discovery) {
return _.sortBy(discovery.udp_ports).join(',');
}
var factory = function (ip, configuration, resourceLocator) {
var myConsole = resourceLocator.log.decorateLogs();
var nmapBlackList = resourceLocator.nmapBlackList;
var discoveryCounter = resourceLocator.discovery.counter;
var settingsManager = resourceLocator.settingsManager;
var platform = resourceLocator.utils.platform;
var host = resourceLocator.discovery.host;
function createNmapPerformanceCommand(hostTimeout) {
var nmapPerformanceSettings = settingsManager.getNMAPPerformanceSettings(hostTimeout);
var nmapPerfomanceCommand = '';
for (var setting in nmapPerformanceSettings) {
if (!nmapPerformanceSettings[setting]) {
continue;
}
if (setting === 'T') {
nmapPerfomanceCommand += ' -' + setting + ' ' + nmapPerformanceSettings[setting] + ' ';
} else {
nmapPerfomanceCommand += ' --' + setting + ' ' + nmapPerformanceSettings[setting] + ' ';
}
}
return nmapPerfomanceCommand;
}
var ipv4 = ip.Address4;
var discovery = settingsManager.getDiscoverySettings();
var portString = getPortString(discovery);
var portUDPString = getPortUDPString(discovery);
var maxNetmask = discovery.max_ip_netmask;
var _getUDPCustomScriptDir = function () {
var customScriptDir = '';
if (settingsManager.getUDPScanSettings().use_nmap_custom_scripts) {
var scriptDir = resourceLocator.path.join(__dirname, 'nmap_payloads');
customScriptDir = ['--datadir', scriptDir].join(' ') + ' ';
}
return customScriptDir;
};
function getStandardNmapCmd(hostTimeout) {
var ipList = nmapBlackList.getDeviceToExclude();
var cmd = 'domotz_nmap -oX - ' +
(ipList.length ? '--exclude ' + ipList.join(',') + ' ' : ' ') +
createNmapPerformanceCommand(hostTimeout);
if (platform.needsSudo()) {
myConsole.debug('Platform %s needs sudo', process.env.DPLATFORM);
cmd = 'sudo ' + cmd;
}
return cmd;
}
function createNmapCommandExternal(baseCommand, commands) {
baseCommand += ' -sn ';
for (var command in commands) {
if (commands.hasOwnProperty(command)) {
if (!commands[command].enabled) {
continue;
}
if (command !== 'source_port') {
baseCommand += ' -' + command + (commands[command].hasOwnProperty('ports') ? commands[command].ports.join() : '') + ' ';
} else {
baseCommand += ' --source-port ' + commands[command].port + ' ';
}
}
}
return baseCommand;
}
function createNmapCommandForExternalHost(baseCommand) {
var commands = settingsManager.getSetting('configuration.settings.discovery.external_hosts.nmap_commands', DEFAULT_NMAP_COMMANDS_EXT_HOSTS);
return createNmapCommandExternal(baseCommand, commands);
}
function createNmapCommandForExternalSubnet(iface, baseCommand) {
var commands = settingsManager.getSetting(
'configuration.settings.discovery.external_subnet.nmap_extended_commands',
DEFAULT_NMAP_COMMANDS_EXT_SUBNET
);
return createNmapCommandExternal(baseCommand, iface.discovery || commands);
}
function getPingNmapCmd(iface, networkList, nmapType, hostTimeout, disableDNS) {
disableDNS = disableDNS !== undefined ? disableDNS : false;
var baseCommand = getStandardNmapCmd(hostTimeout);
if ((nmapType && nmapType !== NMAP_TYPE.EXTERNAL_HOST && nmapType !== NMAP_TYPE.EXTERNAL_SUBNET) || !nmapType) {
baseCommand += '-sP ';
}
if (!discoveryCounter.isFullDiscovery() || disableDNS) {
baseCommand += ' -n ';
}
if (!nmapType || nmapType === NMAP_TYPE.DEFAULT) {
return baseCommand + networkList;
} else if (nmapType === NMAP_TYPE.DISABLE_ARP_PING) {
return baseCommand + ' --disable-arp-ping ' + networkList;
} else if (nmapType === NMAP_TYPE.EXTERNAL_SUBNET) {
return createNmapCommandForExternalSubnet(iface, baseCommand) + networkList;
} else if (nmapType === NMAP_TYPE.EXTERNAL_HOST) {
return createNmapCommandForExternalHost(baseCommand) + networkList;
} else {
myConsole.error('getPingNmapCmd: Unrecognized nmap type');
}
}
function getScanBigNetworksNmapCmd(networkAddress) {
// Load configuration
var params = settingsManager.getBigNetworksPerformanceParams();
var timeout = params.timeout;
var tValue = params.t_value;
var maxSubnetworkMask = params.max_subnetwork_mask;
// Get some information from the network to scan
var net = new ipv4(networkAddress);
var baseIP = net.startAddress();
var netmask = net.subnetMask;
var subnetSplit = Math.max(maxSubnetworkMask, netmask);
var ipCountPerNmap = Math.pow(2, IPV4_BITS - subnetSplit); // This is constant based on the configurations
var numberOfNmapRuns = Math.pow(2, subnetSplit - netmask);
var baseCommand = getStandardNmapCmd(timeout) + '-sn -n '; // -sn, same as -sP but not deprecated
baseCommand += '-T' + tValue.toString() + ' '; // Makes scan faster (more aggressive)
baseCommand = baseCommand.replace('-oX', '-oG'); // greppable output instead of xml
var commands = [];
// Calculate the new network to scan, the netmask remains the same.
for (var i = 0; i < numberOfNmapRuns; i++) {
var networkToScan = parseInt(baseIP.bigInteger()) + i * ipCountPerNmap
var subNetwork = ipv4.fromInteger(networkToScan);
var cidr = subNetwork.address + '/' + subnetSplit;
var debugNet = new ipv4(cidr);
myConsole.verbose('%s %s %s', cidr, debugNet.startAddress().address, debugNet.endAddress().address);
commands.push(baseCommand + cidr);
}
return commands;
}
function getFastNmapCmd(addr, netmask, nmapType, ifaceName, iface) {
if (netmask < maxNetmask) {
myConsole.warn('Netmask Out of Range [%d-%d]: %d', maxNetmask, DOMOTZ_NETMASK, netmask);
netmask = maxNetmask;
}
var commands = [],
baseIP = new ipv4(addr + '/' + netmask).startAddress(),
parallel = Math.pow(2, DOMOTZ_NETMASK - netmask);
for (var i = 0; i < parallel; i++) {
var network = ipv4.fromInteger(parseInt(baseIP.bigInteger()) + i * SCANNED_IP_PER_NMAP);
var cidr = netmask > DOMOTZ_NETMASK ? netmask : DOMOTZ_NETMASK;
var cmd = getPingNmapCmd(iface, network.address + '/' + cidr, nmapType);
commands.push({ nmap_command: cmd, iface_name: ifaceName, iface: iface });
}
myConsole.info('Using netmask %s for network %s (%s nmap commands)', netmask, addr, commands.length);
return commands;
}
function getNETBIOSCmd() {
var customScriptDir = _getUDPCustomScriptDir();
return getStandardNmapCmd() + '-sU ' + customScriptDir + '--script nbstat.nse -p137 ';
}
function getRetryCmd(iface, ipList, nmapType, hostTimeout) {
var filteredIpList = ipList;
/* Filter devices by removing the ones present in L3 Discovery Deny list, in case ARP Ping is disabled */
if (nmapType === NMAP_TYPE.DISABLE_ARP_PING) {
filteredIpList = ipList.filter(function (ip) {
var isDeviceDenied = host.isDeviceIPInL3DiscoveryDenyList(ip);
if (isDeviceDenied) {
myConsole.debug('RETRY Discovery with disabled ARP PING skipped for device IP = %s because in L3 Discovery Deny List', ip);
}
return !isDeviceDenied;
});
}
return getPingNmapCmd(iface, filteredIpList.join(' '), nmapType, hostTimeout, true);
}
function getPortScanType() {
if (platform.isWindows()) {
return '-sT';
}
return '-sS';
}
function getPortNmapCmd(ip) {
// using TCP SYN scanning (https://nmap.org/book/man-port-scanning-techniques.html)
return getStandardNmapCmd() + getPortScanType() + ' -p' + portString + ' ' + ip;
}
function getPortNmapCmdMultiIp(ips) {
// using TCP SYN scanning (https://nmap.org/book/man-port-scanning-techniques.html)
return getStandardNmapCmd() + getPortScanType() + ' -p' + portString + ' ' + ips.join(' ');
}
function getPortUDPNmapCmdMultiIp(ips) {
var customScriptDir = _getUDPCustomScriptDir();
var command = getStandardNmapCmd() + customScriptDir + '-n -sU --disable-arp-ping -p' + portUDPString + ' ' + ips.join(' ');
myConsole.debug('udp_port_scanner nmap command %s', command);
return command;
}
function getExplicitPortNmapCmd(ip, explicitPortString) {
// using TCP SYN scanning (https://nmap.org/book/man-port-scanning-techniques.html)
return getStandardNmapCmd() + '-n ' + getPortScanType() + ' -Pn --disable-arp-ping -p' + explicitPortString + ' ' + ip;
}
return {
createNmapPerformanceCommand: createNmapPerformanceCommand,
getPingNmapCmd: getPingNmapCmd,
getRetryCmd: getRetryCmd,
getFastNmapCmd: getFastNmapCmd,
getPortNmapCmd: getPortNmapCmd,
getPortNmapCmdMultiIp: getPortNmapCmdMultiIp,
getPortUDPNmapCmdMultiIp: getPortUDPNmapCmdMultiIp,
getExplicitPortNmapCmd: getExplicitPortNmapCmd,
getNETBIOSCmd: getNETBIOSCmd,
getScanBigNetworksNmapCmd: getScanBigNetworksNmapCmd,
NMAP_TYPE: NMAP_TYPE,
_createNmapCommandForExternalHost: createNmapCommandForExternalHost,
};
};
module.exports.factory = factory;