UNPKG

domotz-remote-pawn

Version:

Domotz Agent

961 lines (887 loc) 36.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/>. **/ /** * Created by Tommaso Latini <tommaso@domotz.com> on 25/07/15. * * This module recover host information on a UNIX-like operating system (like * Debian, DSM, Qnap OS). * * These information are fundamental for the application, so we recover it * synchronously at boot and we exit in case of failure. * The periodical refresh is asynchronous for not blocking nodejs mainloop. */ const MODULE_NAME = 'HOST_INTERFACES - '; const MAC_VALIDATOR = /^(?!(?:ff:ff:ff:ff:ff:ff|00:00:00:00:00:00))(?:[\da-f]{2}:){5}[\da-f]{2}$/i; const DEFAULT_IP_BLACKLIST = ['10.177.0.1', '192.168.112.112']; const RECOVER_HOST_INFO_RETRY = 1000; function checkMac(mac) { if (!mac || !MAC_VALIDATOR.test(mac.toLowerCase())) { throw new Error(MODULE_NAME + ' - Invalid MAC address [' + mac + ']. ' + 'Invalid interface'); } } var factory = function (networkInterfaces, customInterfaces, resourceLocator) { var proc = resourceLocator.utils.proc; var childProcess = resourceLocator.utils.childProcess; var fileManager = resourceLocator.utils.file; var platform = resourceLocator.utils.platform; var ipUtils = resourceLocator.ip; var nmapXml2Js = resourceLocator.xml2Js; var lodash = resourceLocator.lodash; var q = resourceLocator.q; var dns = resourceLocator.networkTools.dns; var netmask = resourceLocator.networkTools.netmask; var myConsole = resourceLocator.log.decorateLogs(); function checkIp(address) { var ipAddress = new ipUtils.Address4(address); if (!ipAddress.valid || !ipAddress.v4) { throw new Error(MODULE_NAME + ' - Invalid IP address. [' + address + '] ' + 'Maybe the interface is not connected.'); } } function logInterface(name, mac, ip, netmask) { myConsole.info('(%s): MAC address: [%s] - IP address: [%s] - Netmask: [%s].', name, mac, ip, netmask); } /** * [**interfacesList] should be an hash of couple: * <interface_name>: { mac: <mac_address> } * At least mac address MUST be into interfaces list because * it is used by licence api for binding domotz agent. * * [**defaultInterface] is the name of the interface of the default gateway */ var iteration = 0; var Netmask = netmask.Netmask; const nonAscii = /[^\x00-\x7F]/g; function sanitiseNonAscii(str) { return str.replace(nonAscii, ''); } function renameNonASCIIKeys(obj) { Object.keys(obj).forEach(function (key) { if (nonAscii.test(key)) { const newKey = sanitiseNonAscii(key); obj[newKey] = obj[key]; delete obj[key]; } }); } function setIteration(i) { iteration = i; } var interfacesList = {}; var defaultGateway = null; var defaultGatewayMAC = null; var defaultInterface = null; var unicastInterfaces = null; var multicastInterfaces = null; var fullIpList = []; function mergeCustomInterfaces(tmp) { var previousCustomInterfaces = lodash.omitBy(interfacesList, function (value) { return value.custom !== true; }); interfacesList = lodash.extend(tmp, previousCustomInterfaces); } function recoverHostInfoV1() { myConsole.info('recoverHostInfoV1 ...'); return proc .spawn('ip', ['route']) .then(function (data) { var lines = data.toString().split(/\r?\n/); lines.every(function (item) { var fields = item.split(/ +/); myConsole.info('route line = > %s', item, {}); if (fields[0] === 'default' && fields[1] === 'via') { myConsole.debug('Fields = %j', fields, {}); defaultGateway = fields[2]; myConsole.info('Detected gateway => %s', defaultGateway, {}); return false; } return true; }); }) .then(networkInterfaces.getDefaultGatewayAsync) .then(function (descr) { defaultInterface = descr.interface; }) .then(networkInterfaces.getInterfacesAsync) .then(function (list) { if (!list || !list.length) { throw new Error(MODULE_NAME + ': No valid interfaces in the final list'); } var tmp = {}; list.forEach(function (nic) { var name = nic.name; logInterface(name, nic.mac, nic.ip, nic.netmask, defaultInterface); delete nic.name; tmp[name] = nic; }); mergeCustomInterfaces(tmp); }) .then(getGatewayMac); } function recoverHostInfoWinV1() { myConsole.info('recoverHostInfoWinV1'); return proc .spawn('route', ['print', '-4']) .then(function (output) { if (!output) { return q.reject(); } myConsole.debug('Parsing: %s', output); var rows = output.split('\n'); var counter = 0; var row; for (var i = 0; i < rows.length; i++) { if (rows[i].indexOf('====') === 0) { counter++; if (counter === 3) { row = rows[i + 3]; break; } } } var fields = row.split(/\s+/); myConsole.debug('Fields = %j', fields); defaultGateway = fields[3]; myConsole.info('Detected gateway => %s', defaultGateway); }) .then(networkInterfaces.getDefaultGatewayAsyncWin) .then(function (descr) { defaultInterface = descr.interface; }) .then(networkInterfaces.getInterfacesAsyncWin) .then(function (list) { if (!list || !list.length) { throw new Error(MODULE_NAME + ': No valid interfaces in the final list'); } var tmp = {}; list.forEach(function (nic) { var name = nic.name; logInterface(name, nic.mac, nic.ip, nic.netmask, defaultInterface); delete nic.name; tmp[name] = nic; }); mergeCustomInterfaces(tmp); }) .then(getGatewayMac); } function recoverHostInfoV2() { myConsole.info('recoverHostInfoV2'); var path = require('path'); return proc .spawn(path.join(process.env.DOMOTZ_BIN_DIR, 'domotz_network_utils')) .then(function (output) { myConsole.debug('raw output ' + output); var rows = output.split('\n'); var tmp = {}; var interfaceFound = false; for (var i = 0; i < rows.length && rows[i].length; i++) { if (rows[i] === '---') { // --- // Default Gateway: 192.168.77.1 (or empty) // Interface: eth1 // IPv4: 192.168.77.5 // MAC: 08:00:27:53:3a:53 // CIDR: 24 var defaultGatewayFound = false; if (!defaultGateway) { defaultGateway = rows[++i].slice(17); defaultGatewayFound = true; } else { i++; } var name = rows[++i].slice(11); var ip = rows[++i].slice(6); var mac = rows[++i].slice(5).toUpperCase(); var netmask = parseInt(rows[++i].slice(6)); try { checkIp(ip); checkMac(mac); if (defaultGateway && defaultGatewayFound) { defaultInterface = name; myConsole.info('(%s): Default Gateway on %s', name, defaultGateway); } logInterface(name, mac, ip, netmask); tmp[name] = { ip: ip, mac: mac, netmask: netmask, }; interfaceFound = true; } catch (err) { myConsole.warn(err.message); } } } if (!interfaceFound) { throw new Error('Unable to find a configured network interface'); } mergeCustomInterfaces(tmp); }) .then(function () { overrideDefaultInterface(); }) .then(fixDefaultGateway) .then(getGatewayMac); } function isInterfaceValid(iface) { if (iface.family !== 'IPv4') { return false; } if (iface.mac === '00:00:00:00:00:00') { return false; } if (DEFAULT_IP_BLACKLIST.indexOf(iface.address) !== -1) { myConsole.debug('Skip blacklisted interface with IP: %s', iface.address); return false; } return true; } function getWindowsGatewayPowershell() { function powershellExec(cmd) { cmd = 'powershell.exe "' + cmd.replace(/"/gi, '\\"') + '"'; var rawOutput = childProcess.execSync(cmd).toString(); return JSON.parse(rawOutput); } var defaultGatewayInfo = powershellExec( 'Get-CimInstance Win32_NetworkAdapterConfiguration -filter "IPEnabled=true" ' + ' | Select-Object DefaultIPGateway,Index | ConvertTo-JSON' ); if (defaultGatewayInfo.length) { defaultGatewayInfo = lodash.filter(defaultGatewayInfo, function (value) { return value.DefaultIPGateway !== null; }); defaultGatewayInfo = defaultGatewayInfo[0]; } var gatewayIP = defaultGatewayInfo.DefaultIPGateway[0]; var gatewayIfIndex = defaultGatewayInfo.Index; var getInterfaceName = 'Get-CimInstance Win32_NetworkAdapter -filter "Index=' + gatewayIfIndex + '" | Select-Object NetConnectionID,MacAddress | ConvertTo-JSON'; var ifNameOutput = powershellExec(getInterfaceName).NetConnectionID; return { gateway: gatewayIP, interface: ifNameOutput, }; } function overrideDefaultInterface() { if (platform.isNetgearRouter()) { var netgearInterface = platform.netgearRouterDefaultInterface(); if (getIP(netgearInterface)) { myConsole.info('Netgear Router platform: default interface is on %s', netgearInterface); defaultInterface = netgearInterface; } else { myConsole.error('%s not found, cannot override default interface', netgearInterface); } } } function recoverHostInfoNative() { myConsole.info('recoverHostInfoNative'); var gw; if (platform.isWindows()) { try { gw = getWindowsGatewayPowershell(); } catch (e) { myConsole.warn('Unable to parse powershell output %s', e.toString()); try { gw = require('default-gateway').v4.sync(); } catch (e) { myConsole.error('Unable to obtain default gateway'); return q.reject(); } } } else { try { gw = require('default-gateway').v4.sync(); } catch (e) { myConsole.error('Unable to obtain default gateway'); return q.reject(); } } if (!gw) { myConsole.error('Unable to obtain default gateway'); gw = {}; } if (Object.keys(gw).length > 0) { defaultGateway = gw.gateway; defaultInterface = sanitiseNonAscii(gw.interface); myConsole.info('(%s): Default Gateway on %s', defaultInterface, defaultGateway); } // exploring network interfaces var interfaces = require('os').networkInterfaces(); renameNonASCIIKeys(interfaces); var tmp = {}; var interfaceFound = false; Object.keys(interfaces).forEach(function (interfaceName) { interfaces[interfaceName].forEach(function (element) { if (isInterfaceValid(element)) { var name = interfaceName; var ip = element.address; var mac = element.mac; var addr = new Netmask(element.address + '/' + element.netmask); try { checkIp(ip); checkMac(mac); logInterface(name, mac, ip, addr.bitmask); tmp[name] = { ip: ip, mac: mac, netmask: addr.bitmask, }; interfaceFound = true; } catch (err) { myConsole.warn(err.message); } } }); }); if (!interfaceFound) { throw new Error('Unable to find a valid interface'); } mergeCustomInterfaces(tmp); overrideDefaultInterface(); fixDefaultGatewayWin() .then(getGatewayMac) .catch(function (e) { myConsole.error('Error recovering gateway information %s', JSON.stringify(e)); }); return q.resolve(); } function fixDefaultGateway() { if (defaultGateway && defaultGateway === '255.255.255.127' && fileManager.getFileStats('/sbin/route')) { myConsole.info('retrieving default gateway using route'); return proc .spawn('/sbin/route', ['-n']) .then(function (output) { if (!output) { return; } myConsole.debug('Parsing: %s', output); var row = output.split('\n')[2].replace(/\s\s+/g, ' '); defaultGateway = row.split(' ')[1]; myConsole.info('Replaced default gateway with %s using route', defaultGateway); }) .catch(function (e) { myConsole.error('Error getting default gateway using route %s', JSON.stringify(e)); }); } } function fixDefaultGatewayWin() { if (!platform.isWindows()) { return q.resolve(); } myConsole.info('retrieving default gateway using route'); return proc.spawn('route', ['print', '-4']).then(function (output) { if (!output) { return q.reject(); } myConsole.debug('Parsing: %s', output); var rows = output.split('\n'); var counter = 0; var row; for (var i = 0; i < rows.length; i++) { if (rows[i].indexOf('====') === 0) { counter++; if (counter === 3) { row = rows[i + 3]; break; } } } defaultGateway = row.split(/(\s+)/).filter(function (e) { return e.trim().length > 0; })[2]; myConsole.info('Replaced default gateway with %s using route', defaultGateway); }); } function getGatewayMac() { 'use strict'; myConsole.info('Searching for Gateway MAC address'); if (!defaultGateway) { myConsole.info('No Default Gateway MAC'); return q.reject(); } return proc .spawn('domotz_nmap', ['-sP', '-oX', '-', defaultGateway]) .then(function (output) { myConsole.debug('Parsing: %s', output); nmapXml2Js .toJSON(output) .then(function (xml) { myConsole.debug('%s', JSON.stringify(xml)); xml.nmaprun.host[0].address.every(function (addr) { if (addr.$.addrtype === 'mac') { defaultGatewayMAC = addr.$.addr; myConsole.info('Gateway MAC address: %s', defaultGatewayMAC); return false; } return true; }); }) .catch(function () { myConsole.warn('Cannot parse arp output to get default gateway MAC, using onw MAC: %s', output); defaultGatewayMAC = interfacesList[0].mac; }); }) .catch(function (e) { myConsole.error('Error getting default gateway %s', JSON.stringify(e)); }); } function refreshCustomInterfaces(customSubnets) { var networkInterfaces = lodash.omitBy(interfacesList, function (element) { return element.custom === true; }); var subnets; if (!customSubnets || Object.keys(customSubnets).length === 0) { //try to load legacy file-based interfaces. Returns empty object as a fallback subnets = customInterfaces.get(); } else { subnets = customSubnets; } interfacesList = lodash.extend(subnets, networkInterfaces); } function fallbackLinuxHostInfo() { if (networkInterfaces) { return recoverHostInfoV1().catch(function () { return recoverHostInfoV2(); }); } else { return recoverHostInfoV2(); } } function recoverHostInfo() { if (process.env.USE_RECOVER_HOST_INFO_V2) { myConsole.info('forcing recoverHostInfoV2 for testing purposes'); return recoverHostInfoV2(); } if (platform.isNetgearRouter()) { myConsole.info('forcing recoverHostInfoV2 for Netgear routers'); return recoverHostInfoV2(); } if (platform.isWindows()) { return recoverHostInfoNative().catch(function () { myConsole.warn('Windows native host info retrieval failed, falling back to recoverHostInfoWinV1'); return recoverHostInfoWinV1(); }); } else if (platform.isUbuntuCore() || (!platform.isNodeVersionZero() && fileManager.isExecutableAvailableInPATH('ip'))) { return recoverHostInfoNative().catch(function () { return fallbackLinuxHostInfo(); }); } else { myConsole.info('Using fallback methods on legacy platforms'); return fallbackLinuxHostInfo(); } } function recoverHostInfoWithRetry(deferred) { var hostInfoDeferred = deferred || q.defer(); recoverHostInfo() .then(function () { refreshCustomInterfaces(); hostInfoDeferred.resolve(); }) .catch(function () { setTimeout(function () { myConsole.warn('Failed retrieving network info, scheduling retry in %s ms', RECOVER_HOST_INFO_RETRY); recoverHostInfoWithRetry(hostInfoDeferred); }, RECOVER_HOST_INFO_RETRY); }); return hostInfoDeferred.promise; } var getCustomInterfaces = function () { return lodash.omitBy(interfacesList, function (el) { return el.custom !== true; }); }; function commonChecks(device) { var customInterfaces = getCustomInterfaces(); if (!customInterfaces) { return false; } if (Object.keys(customInterfaces).length === 0) { return false; } return device.length; } var deviceBelongsToExternalSubnet = function (device) { if (!commonChecks(device)) { return false; } for (var k = 0; k < device.length; k++) { if (isInCustomSubnet(device[k].ip)) { return true; } } return false; }; var deviceBelongsToExternalHosts = function (device) { if (!commonChecks(device)) { return false; } for (var k = 0; k < device.length; k++) { if (getExternalHostMac(device[k].ip)) { return true; } } return false; }; function getExternalHostMac(ipAddr) { var customInterfaces = getCustomInterfaces(); for (var customId in customInterfaces) { var custom = customInterfaces[customId]; if (custom.discovery_hw_address && custom.ip == ipAddr) { return custom.discovery_hw_address; } } return false; } function isAddressInInterfaceSubnet(ipAddr, interface_) { if (!interface_.ip) { return false; } var netAddress = new ipUtils.Address4(interface_.ip + '/' + interface_.netmask); if (!netAddress.valid) { return false; } if (interface_.netmask == 32 && ipAddr === interface_.ip) { return true; } var addr = new ipUtils.Address4(ipAddr); return addr.isInSubnet(netAddress); } function isInCustomSubnet(ipAddr) { var customInterfaces = getCustomInterfaces(); for (var customId in customInterfaces) { if (isAddressInInterfaceSubnet(ipAddr, customInterfaces[customId])) { return true; } } return false; } function resolveExternalHosts() { myConsole.info('(%d): Resolving external hosts ...', iteration); myConsole.debug('(%d): %s', iteration, JSON.stringify(interfacesList)); var promises = []; for (var name in interfacesList) { var i = interfacesList[name]; if (i.custom && i.hostname) { promises.push( (function (obj) { var p = q.defer(); myConsole.info('(%d): Resolving ', iteration, JSON.stringify(obj.hostname)); myConsole.debug('(%d): DNS lookup START ...', iteration); var addressFamily = 4; dns.lookup(obj.hostname, addressFamily, function (err, ip) { if (err) { myConsole.warn('(%d): Error resolving hostname %s: %s', iteration, obj.hostname, JSON.stringify(err)); p.resolve(); return; } myConsole.info('(%d): Successfully resolved hostname %s: %s', iteration, obj.hostname, JSON.stringify(ip)); if (typeof ip === 'string') { obj.ip = ip; } else { obj.ip = ip[0]; } p.resolve(); }); return p.promise; })(i) ); } } return q.all(promises); } function setFullIpList(l) { fullIpList = lodash.uniq(l); } function getInterfacesList() { lodash.forOwn(interfacesList, function (value, key) { interfacesList[key].ipList = []; }); lodash.forEach(fullIpList, function (rawIp) { var ip = new ipUtils.Address4(rawIp); if (!ip.valid) { return; } lodash.forOwn(interfacesList, function (nic) { if (nic.ip) { var nicIp = new ipUtils.Address4(nic.ip + '/' + nic.netmask); nic.broadcastAddress = nicIp.endAddress().address; if (ip.isInSubnet(nicIp)) { nic.ipList.push(rawIp); } } }); }); return interfacesList; } function getAllowedInterfacesList() { var interfaces = getInterfacesList(); if (!resourceLocator.settingsManager) { myConsole.warn('Agent Settings not initialized yet, returning deny all interfaces list'); return {}; } var policies = resourceLocator.settingsManager.getNetworkInterfacesPolicies(); var allowedInterfaces = lodash.pickBy(interfaces, function (value, key) { if (isExternalHostInterface(key)) { return false; } if (value.custom) { return true; } var match = false; policies.rules.forEach(function (rule) { var processedRule = rule.replace(/\./g, '\\.').replace(/\*/g, '.*'); processedRule = '^' + processedRule + '$'; var regexp = new RegExp(processedRule, 'i'); if (regexp.test(key)) { myConsole.info('Interface %s matched with filtering rule %s', key, processedRule); match = true; } else { myConsole.debug('Interface %s did not match with filtering rule %s', key, processedRule); } }); return policies.policy === 'deny' ? !match : match; }); var externalHostsInterfaces = lodash.pickBy(interfaces, function (value, key) { return !!isExternalHostInterface(key); }); allowedInterfaces = _extendInterfacesWithExternalHosts(allowedInterfaces, externalHostsInterfaces); return extendInterfacesWithForcedIps(allowedInterfaces); } function getUnicastInterfacesList() { if (unicastInterfaces === null && interfacesList) { return lodash.omitBy(interfacesList, function (el) { return el.custom !== true; }); } else { return unicastInterfaces; } } function getMulticastInterfacesList() { if (multicastInterfaces === null && interfacesList) { return lodash.omitBy(interfacesList, function (el) { return el.custom === true; }); } else { return multicastInterfaces; } } function addIpToFullList(ip) { if (fullIpList.indexOf(ip) < 0) { fullIpList.push(ip); } } function _extendInterfacesWithExternalHosts(allowedInterfaces, externalHostsInterfaces) { myConsole.debug('Extending %s with %s', JSON.stringify(allowedInterfaces), JSON.stringify(externalHostsInterfaces)); var name; var index = 0; var ifName = 'external_hosts_' + index; var ifChunk = {}; var current_external_hosts = 0; var max_external_hosts = resourceLocator.settingsManager.getExternalHostsChunkSize(); var length = Object.keys(externalHostsInterfaces).length; if (length > 0) { allowedInterfaces = lodash.clone(allowedInterfaces); } else { return allowedInterfaces; } ifChunk[ifName] = { custom: true, ip: null, netmask: null, mac: null, discovery_hw_address: null, ipList: [], forced_ip_list: true, }; for (name in externalHostsInterfaces) { if (externalHostsInterfaces.hasOwnProperty(name)) { current_external_hosts++; if (current_external_hosts > max_external_hosts) { index++; ifName = 'external_hosts_' + index; ifChunk[ifName] = { custom: true, ip: null, netmask: null, mac: null, discovery_hw_address: null, ipList: [], forced_ip_list: true, }; current_external_hosts = 1; } var iface = externalHostsInterfaces[name]; ifChunk[ifName].ipList.push(iface.ip); ifChunk[ifName].ip = iface.ip; ifChunk[ifName].discovery_hw_address = iface.discovery_hw_address; } } lodash.extend(allowedInterfaces, ifChunk); myConsole.debug('Allowed interfaces extended with virtual external hosts interfaces: %s', JSON.stringify(allowedInterfaces)); return allowedInterfaces; } var _extendInterfacesWithForcedIpList = function (allowedInterfaces) { var ipPolicy = resourceLocator.settingsManager.getIpPolicy(); if (ipPolicy.forced_ip_addresses.length > 0) { var allowedIpAddresses = ipPolicy.forced_ip_addresses; allowedInterfaces = lodash.clone(allowedInterfaces); var chunks = lodash.chunk(allowedIpAddresses, ipPolicy.max_ip_per_nmap); lodash.forEach(chunks, function (ipChunk, index) { var ifChunk = {}; var ifName = 'forced_' + index; ifChunk[ifName] = { forced_ip_list: true, ipList: ipChunk || [], }; lodash.extend(allowedInterfaces, ifChunk); }); } return allowedInterfaces; }; var _extendInterfacesWithForcedIpRanges = function (allowedInterfaces) { var ipPolicy = resourceLocator.settingsManager.getIpPolicy(); if (ipPolicy.forced_ip_ranges.length > 0) { var allowedIpAddressRanges = ipPolicy.forced_ip_ranges; allowedInterfaces = lodash.clone(allowedInterfaces); var ipRanges = {}; var name = 'forced_range'; ipRanges[name] = { ipRange: allowedIpAddressRanges || [], forced_ip_range: true, }; lodash.extend(allowedInterfaces, ipRanges); } return allowedInterfaces; }; var extendInterfacesWithForcedIps = function (allowedInterfaces) { allowedInterfaces = _extendInterfacesWithForcedIpList(allowedInterfaces); allowedInterfaces = _extendInterfacesWithForcedIpRanges(allowedInterfaces); return allowedInterfaces; }; var isCustomInterface = function (nic) { var iface = interfacesList[nic]; return iface.custom && iface.custom === true; }; var isExternalHostInterface = function (nic) { var iface = interfacesList[nic]; return !!(iface.custom && iface.custom === true && iface.discovery_hw_address); }; var getInternalInterface = function () { return lodash.omitBy(interfacesList, function (el) { return !el.mac; }); }; var getIP = function getIP(nic) { var selectedInterface = interfacesList[nic || defaultInterface]; if (!selectedInterface) { return; } return selectedInterface.ip; }; function isDeviceMacInL3DiscoveryDenyList(mac) { if (!mac) { return false; } if (!resourceLocator.settingsManager) { return false; } var l3DiscoveryDenyList = resourceLocator.settingsManager.getL3DiscoveryDenyList(); return (l3DiscoveryDenyList.indexOf(mac) !== -1); } function isDeviceIPInL3DiscoveryDenyList(ip) { if (!ip) { return false; } if (!resourceLocator.interfacesBindingStorage){ return false; } var mac = resourceLocator.interfacesBindingStorage.getMacFromIp(ip); return isDeviceMacInL3DiscoveryDenyList(mac); } function isMacInExternalHostsInterfaces (mac) { const customInterfaces = getCustomInterfaces(); for (var customId in customInterfaces) { var custom = customInterfaces[customId]; if (custom && custom.discovery_hw_address === mac) { return true; } } return false; } return { recoverHostInfo: recoverHostInfo, recoverHostInfoWithRetry: recoverHostInfoWithRetry, getIP: getIP, getMAC: function (nic) { try { return interfacesList[nic || defaultInterface].mac; } catch (e) { myConsole.warn('Unable to get interface Mac address'); } }, getNetmask: function (nic) { return interfacesList[nic || defaultInterface].netmask; }, getDefaultInterface: function () { return interfacesList[defaultInterface]; }, getDefaultGateway: function () { return defaultGateway; }, getDefaultGatewayMAC: function () { return defaultGatewayMAC; }, getInterfacesList: getInterfacesList, getUnicastInterfacesList: getUnicastInterfacesList, getMulticastInterfacesList: getMulticastInterfacesList, getAllowedInterfacesList: getAllowedInterfacesList, isCustomInterface: isCustomInterface, isExternalHostInterface: isExternalHostInterface, isDeviceMacInL3DiscoveryDenyList: isDeviceMacInL3DiscoveryDenyList, isDeviceIPInL3DiscoveryDenyList: isDeviceIPInL3DiscoveryDenyList, getInterface: function () { return defaultInterface; }, getInterfaceType: function (ip) { for (var iface in interfacesList) { if (interfacesList.hasOwnProperty(iface)) { var nic = interfacesList[iface]; var nicIp = new ipUtils.Address4(nic.ip + '/' + nic.netmask); var secondIp = new ipUtils.Address4(ip); if (secondIp.isInSubnet(nicIp)) { return nic.custom || false; } } } return false; }, loadCustomInterfaces: refreshCustomInterfaces, getCustomInterfaces: getCustomInterfaces, deviceBelongsToExternalSubnet: deviceBelongsToExternalSubnet, deviceBelongsToExternalHosts: deviceBelongsToExternalHosts, getExternalHostMac: getExternalHostMac, resolveExternalHosts: resolveExternalHosts, IPisInCustomSubnet: isInCustomSubnet, setFullIpList: setFullIpList, fixDefaultGateway: fixDefaultGateway, setIteration: setIteration, _recoverHostInfoNative: recoverHostInfoNative, addIpToFullList: addIpToFullList, _getWindowsGatewayFallback: getWindowsGatewayPowershell, getInternalInterfaces: getInternalInterface, isMacInExternalHostsInterfaces: isMacInExternalHostsInterfaces, }; }; module.exports.factory = factory;