UNPKG

domotz-remote-pawn

Version:

Domotz Agent

176 lines (158 loc) 6.39 kB
/** This file is part of Domotz Agent. * Copyright (C) 2016 Domotz UK LLP * * 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 <iacopo@domotz.com> * * Listens for broadcast DHCP lease packets and accumulates the data for sending * to the backend in piggyback with the fast discovery * */ var dhcpDiscoveryFactory = function (dhcpjs, random, resourceLocator) { var myConsole = resourceLocator.log.decorateLogs(); var domotzPlatform = resourceLocator.utils.platform; var server = null; var packetCounter = 0; const maxRequestsPerSecond = resourceLocator.settingsManager.getSetting('configuration.settings.max_dhcp_requests_per_second', 100); const floodDetectionPeriod = resourceLocator.settingsManager.getSetting('configuration.settings.dhcp_flood_detection_period_s', 10); var isFloodDetectionTimerActive = false; function extractMacIfWellFormed(message) { var error; if (!message.op) { error = "Corrupted package, no 'op' field."; myConsole.verbose(error); return [error]; } if (message.op.value !== 1) { error = 'Not a BOOTREQUEST. Value: ' + message.op.value; myConsole.verbose(error); return [error]; } if (!message.options) { error = 'Not a DHCP request'; myConsole.verbose(error); return [error]; } var hwaddr = message.options.clientIdentifier || message.options.chaddr; if (!hwaddr || !hwaddr.type || hwaddr.type.value !== 1) { error = 'Not an ethernet device'; myConsole.verbose(error); return [error]; } return [null, hwaddr.address.toUpperCase()]; } function extractDataIfWellFormed(message) { try { var ret = { parameterRequestList: message.options.parameterRequestList, hostName: message.options.hostName, vendorClassIdentifier: message.options.vendorClassIdentifier, }; var atLeastOneDefined = false; Object.keys(ret).forEach(function (key) { if (ret[key] !== undefined) { atLeastOneDefined = true; } }); if (atLeastOneDefined) { return [null, ret]; } else { return ['Message does not contain discovery data', null]; } } catch (e) { return ['' + e, null]; } } function handleMessage(message) { packetCounter += 1; if (!isFloodDetectionTimerActive) { isFloodDetectionTimerActive = true; setTimeout(floodDetection, floodDetectionPeriod * resourceLocator.utils.time.SECOND); } var res = extractMacIfWellFormed(message); var error = res[0]; if (error) { return error; } var mac = res[1]; if (mac) { myConsole.info('DHCPDISCOVER Packet: source mac=%s', mac); var invalidateBindingsOnDHCP = resourceLocator.settingsManager.getSetting( 'configuration.settings.dhcp.invalidate_bindings_on_dhcp_requests', true ); if (invalidateBindingsOnDHCP) { resourceLocator.interfacesBindingStorage.invalidateBindingByMac(mac.toUpperCase()); } } var filteredMessage = extractDataIfWellFormed(message); if (filteredMessage[0]) { return filteredMessage[0]; } if (mac !== false && filteredMessage[1]) { resourceLocator.discovery.accumulator.store(mac, 'dhcp', filteredMessage[1]); } else { myConsole.verbose('Ignoring DHCP message'); } } function handleError(error) { myConsole.error('DHCP server error: ' + error.toString()); } function onListening(address) { myConsole.info('listening for DHCP packets on ' + address); } function listenForLeasePackets() { var dhcpOptions = domotzPlatform.isNodeVersionZero() ? 'udp4' : { type: 'udp4', reuseAddr: true }; var enableDHCPServer = resourceLocator.settingsManager.getSetting('configuration.settings.dhcp.enable_dhcp_server', true); if (!enableDHCPServer) { myConsole.info('DHCP server disabled by agent settings'); return; } server = dhcpjs.createServer(null, dhcpOptions, myConsole); server.on('listening', onListening); server.on('message', handleMessage); server.on('error', handleError); server.bind(); } function stopDhcpServer() { try { server.server.close(); } catch (e) { myConsole.warn('exception in dhcp socket deletion %s', JSON.stringify(e)); } myConsole.error('DHCP server stopped'); resourceLocator.periodicalTasks.stopTask('dhcpRequestsReport'); } function floodDetection() { var packetsPerSecond = packetCounter / floodDetectionPeriod; if (packetsPerSecond > maxRequestsPerSecond) { myConsole.error('The number of DHCP requests per second %d exceeded the threshold of %d', packetsPerSecond, maxRequestsPerSecond); stopDhcpServer(); } packetCounter = 0; isFloodDetectionTimerActive = false; } return { // ----- TEST ----- _handleMessage: handleMessage, // ----- TEST ----- listenForLeasePackets: listenForLeasePackets, // ----- TEST ----- _stopDhcpServer: stopDhcpServer, // ----- TEST ----- _floodDetection: floodDetection, }; }; module.exports.factory = dhcpDiscoveryFactory;