domotz-remote-pawn
Version:
Domotz Agent
121 lines (100 loc) • 4.17 kB
JavaScript
/** 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/>.
*
* Listens for broadcast DHCP lease packets and sends the data to the backend
*
* Created by Iacopo Papalini <iacopo@domotz.com>
*
* 40 seconds: the assumption is that in the meantime the fast discovery
* has produced the creation if the missing device
*/
const RETRY_INTERVAL = 35000;
const LOG_PREFIX = 'DHCP_LISTEN ';
function isWellFormed(message) {
if (message.op.value !== 1) {
// not a BOOTPREQUEST
throw new Error(LOG_PREFIX + "- Not a BOOTREQUEST. Value: " + message.op.value);
}
if (!message.options) {
// not a DHCP request
throw new Error(LOG_PREFIX + "- Not a DHCP request");
}
var hwaddr = message.options.clientIdentifier || message.options.chaddr;
if (!hwaddr || !hwaddr.type || hwaddr.type.value !== 1) {
throw new Error(LOG_PREFIX + "- Not an ethernet device");
}
return hwaddr.address.toUpperCase();
}
var dhcpDiscoveryFactory = function (dhcpjs, random, discoveryUtils, engineRequest) {
"use strict";
var sentFingerprints = {}; // Filter for known devices
var unknownDevices = {}; // Filter for unknown devices
function signalToEngine(mac, dhcpData) {
if (unknownDevices[mac]) { // Put filter as early as possible
console.verbose(LOG_PREFIX + " not sending unknown devices with mac %s", mac);
return;
}
var hash = random.md5(JSON.stringify(dhcpData));
if (sentFingerprints[mac] === hash) {
console.debug(LOG_PREFIX + " not sending to engine matching md5 (%s) for mac %s", hash, mac);
return;
}
var deviceId = discoveryUtils.getDeviceIdFromMac(mac);
if (!deviceId) {
unknownDevices[mac] = true;
console.warn(LOG_PREFIX + " Unknown device for mac %s, retrying " +
"in %s ms. Fingerprint: %s", mac, RETRY_INTERVAL, JSON.stringify(dhcpData));
setTimeout(function () {
unknownDevices[mac] = false;
signalToEngine(mac, dhcpData);
}, RETRY_INTERVAL);
return;
}
console.info(LOG_PREFIX + "Sending DHCP fingerprint for device %s (mac: %s) ", deviceId, mac);
engineRequest.sendWithPromise('/device/' + deviceId + '/inspection-data/dhcp', 'PUT', dhcpData)
.then(function () {
sentFingerprints[mac] = hash;
}
);
}
function handleMessage(message) {
try {
var mac = isWellFormed(message);
console.verbose(LOG_PREFIX + "DHCP Packet arrived: mac=%s", mac);
signalToEngine(mac, message.options);
} catch (err) {
console.warn('%s %s. Ignoring unexpected packet', LOG_PREFIX, err.message);
console.debug(LOG_PREFIX + JSON.stringify(message));
return err.message; // For testing purpose
}
}
function onListening(address) {
console.info(LOG_PREFIX + '- listening for DHCP packets on ' + address);
}
function listenForLeasePackets() {
var server = dhcpjs.createServer();
server.on('listening', onListening);
server.on('message', handleMessage);
server.bind();
}
return {
// ----- TEST -----
_handleMessage: handleMessage,
// ----- TEST -----
listenForLeasePackets: listenForLeasePackets
};
};
module.exports.factory = dhcpDiscoveryFactory;