domotz-remote-pawn
Version:
Domotz Agent
184 lines (171 loc) • 7.28 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 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 IP_OCTET = "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])";
const IP_VALIDATOR = new RegExp("^(" + IP_OCTET + "\.){3}" + IP_OCTET + "$");
const LINK_LOCAL = new RegExp("^169\.254\." + IP_OCTET + "\." + IP_OCTET + "$");
const MAC_VALIDATOR = /^(?!(?:ff:ff:ff:ff:ff:ff|00:00:00:00:00:00))(?:[\da-f]{2}:){5}[\da-f]{2}$/i;
function logInterface(name, mac, ip, netmask) {
console.info('%s - (%s): MAC address: [%s] - ' +
'IP address: [%s] - Netmask: [%s].', MODULE_NAME, name, mac,
ip, netmask);
}
function checkIp(addr) {
if (!IP_VALIDATOR.test(addr) || LINK_LOCAL.test(addr)) {
throw new Error(MODULE_NAME + " - Invalid IP address. [" + addr + "] " +
"Maybe the interface is not connected.");
}
}
function checkMac(mac) {
if (!mac || !MAC_VALIDATOR.test(mac.toLowerCase())) {
throw new Error(MODULE_NAME + " - Invalid MAC address [" + mac + "]. " +
"Maybe not a valid interface!");
}
}
var factory = function (proc, ipUtils, networkInterfaces, customInterfaces) {
/**
* [**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 interfacesList = {},
defaultGateway = null,
defaultInterface = null;
var recoverHostInfo = networkInterfaces ? recoverHostInfoV1 : recoverHostInfoV2;
function recoverHostInfoV1() {
return proc.spawn('ip', ['route'])
.then(function (data) {
var lines = data.toString().split(/\r?\n/);
lines.every(function (item) {
var fields = item.split(/ +/);
console.debug(MODULE_NAME + ': route line = > %s', item, {});
if (fields[0] === "default" && fields[1] === "via") {
console.debug(MODULE_NAME + ': Fields = %j', fields, {});
defaultGateway = fields[2];
console.info(MODULE_NAME + ': 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 = customInterfaces.get();
list.forEach(function (nic) {
var name = nic.name;
logInterface(name, nic.mac, nic.ip, nic.netmask, defaultInterface);
delete nic.name;
tmp[name] = nic;
});
interfacesList = tmp;
});
}
function recoverHostInfoV2() {
return proc.spawn('domotz_network_utils')
.then(function(output) {
var rows = output.split('\n');
var tmp = customInterfaces.get();
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
defaultGateway = rows[++i].slice(17);
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) {
defaultInterface = name;
console.info('%s - (%s): Default Gateway on %s',
MODULE_NAME, name, defaultGateway);
}
logInterface(name, mac, ip, netmask);
tmp[name] = {
ip: ip,
mac: mac,
netmask: netmask
};
} catch (err) {
console.warn(err.message);
}
}
}
interfacesList = tmp;
});
}
recoverHostInfo();
return {
recoverHostInfo: recoverHostInfo,
getIP: function getIP(nic) {
return interfacesList[nic || defaultInterface].ip;
},
getMAC: function (nic) {
return interfacesList[nic || defaultInterface].mac;
},
getNetmask: function (nic) {
return interfacesList[nic || defaultInterface].netmask;
},
getDefaultGateway: function () {
return defaultGateway;
},
getInterfacesList: function () {
return interfacesList;
},
getInterface: function () {
return defaultInterface;
},
getInterfaceType: function(ip) {
for (var iface in interfacesList) {
if (interfacesList.hasOwnProperty(iface)) {
var nic = interfacesList[iface];
if (ipUtils.cidrSubnet(nic.ip + '/' + nic.netmask).contains(ip)) {
return nic.custom || false;
}
}
}
return false;
}
};
};
module.exports.factory = factory;