UNPKG

iobroker.js-controller

Version:

Updated by reinstall.js on 2018-06-11T15:19:56.688Z

266 lines (265 loc) • 8.95 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var multihostServer_exports = {}; __export(multihostServer_exports, { MHServer: () => MHServer }); module.exports = __toCommonJS(multihostServer_exports); var import_node_dgram = __toESM(require("node:dgram"), 1); var import_node_crypto = require("node:crypto"); var import_js_controller_common = require("@iobroker/js-controller-common"); const PORT = 50005; const MULTICAST_ADDR = "239.255.255.250"; class MHServer { count = 0; buffer = {}; lastFrame = {}; authList = {}; config; logger; info; secret; hostname; server = null; initTimer = null; stopped = false; constructor(hostname, logger, config, info, secret) { this.hostname = hostname; this.config = config; this.logger = logger; this.info = info; this.secret = secret; this.init(); } send(msg, rinfo) { if (this.server) { setImmediate(() => { const text = JSON.stringify(msg); try { this.server?.send(text, 0, text.length, rinfo.port, rinfo.address); } catch (e) { this.logger.warn(`host.${this.hostname} Multi-host discovery server: cannot send answer to ${rinfo.address}:${rinfo.port}: ${e}`); } }); } } // delete all old connections checkAuthList(ts) { ts = ts || (/* @__PURE__ */ new Date()).getTime(); for (const id of Object.keys(this.authList)) { if (!this.authList[id]) { delete this.authList[id]; } else if (ts - this.authList[id].ts > 31e3) { delete this.authList[id]; } } } sha(secret, salt, callback) { const hash = (0, import_node_crypto.createHash)("sha256"); hash.on("readable", () => { const data = hash.read(); if (data) { callback(data.toString("hex")); } }); hash.write(secret + salt); hash.end(); } // hello => auth => browse async process(msg, rinfo) { if (!msg) { return; } const ts = (/* @__PURE__ */ new Date()).getTime(); this.checkAuthList(ts); const id = `${rinfo.address}:${rinfo.port}`; switch (msg.cmd) { case "browse": if (this.secret && msg.password && this.authList[id]) { this.sha(this.secret, this.authList[id].salt, async (shaText) => { if (shaText !== msg.password) { this.send({ auth: this.config.multihostService.secure, cmd: "browse", id: msg.id, result: "invalid password" }, rinfo); } else { this.authList[id].auth = true; this.send({ auth: this.config.multihostService.secure, cmd: "browse", id: msg.id, result: "ok", objects: this.config.objects, states: this.config.states, info: this.info, hostname: this.hostname, slave: !await (0, import_js_controller_common.isLocalObjectsDbServer)(this.config.objects.type, this.config.objects.host) }, rinfo); } }); return; } if (!this.config.multihostService.secure || this.authList[id] && this.authList[id].auth) { this.send({ auth: this.config.multihostService.secure, cmd: "browse", id: msg.id, result: "ok", objects: this.config.objects, states: this.config.states, info: this.info, hostname: this.hostname, slave: !await (0, import_js_controller_common.isLocalObjectsDbServer)(this.config.objects.type, this.config.objects.host) }, rinfo); } else { this.authList[id] = { ts, salt: (Math.random() * 1e6 + ts).toString().substring(0, 16), auth: false }; if (this.authList[id].salt.length < 16) { this.authList[id].salt += new Array(16 - this.authList[id].salt.length).join("_"); } this.send({ auth: this.config.multihostService.secure, cmd: "browse", id: msg.id, result: "not authenticated", salt: this.authList[id].salt }, rinfo); } break; default: this.send({ cmd: msg.cmd, id: msg.id, result: "unknown command" }, rinfo); break; } } init() { this.stopped = false; if (this.initTimer) { clearTimeout(this.initTimer); this.initTimer = null; } if (this.count > 10) { return this.logger.warn(`host.${this.hostname} Multi-host discovery server: Port ${PORT} is occupied. Service stopped.`); } this.server = import_node_dgram.default.createSocket({ type: "udp4", reuseAddr: true }); this.server.on("error", (err) => { this.logger.error(`host.${this.hostname} Multi-host discovery server: error: ${err.stack}`); this.server?.close(); this.server = null; this.initTimer = this.initTimer || setTimeout(() => { this.initTimer = null; this.init(); }, 5e3); }); this.server.on("close", () => { this.server = null; if (!this.initTimer && !this.stopped) { this.initTimer = setTimeout(() => { this.initTimer = null; this.init(); }, 5e3); } }); this.server.on("message", (msg, rinfo) => { const text = msg.toString(); const now = (/* @__PURE__ */ new Date()).getTime(); const id = `${rinfo.address}:${rinfo.port}`; for (const ids in this.buffer) { if (!this.lastFrame[ids]) { delete this.buffer[ids]; } else if (now - this.lastFrame[ids] > 1e3) { delete this.buffer[ids]; delete this.lastFrame[ids]; } } if (this.lastFrame[id] && now - this.lastFrame[id] > 1e3) { this.buffer[id] = ""; } this.lastFrame[id] = now; if (!this.buffer[id] && text[0] !== "{") { this.logger.debug(`host.${this.hostname} Multi-host discovery server: Message from ${rinfo.address} ignored: ${text}`); } else { this.buffer[id] = (this.buffer[id] || "") + msg.toString(); if (this.buffer[id] && this.buffer[id][this.buffer[id].length - 1] === "}") { try { const data = JSON.parse(this.buffer[id]); this.buffer[id] = ""; if (data) { this.process(data, rinfo); } } catch { } } } }); this.server.on("listening", () => { try { this.server?.addMembership(MULTICAST_ADDR); } catch { this.logger.warn(`host.${this.hostname} Multi-host discovery server: Multicast membership could not be added.`); } const address = this.server?.address(); this.logger.info(`host.${this.hostname} Multi-host discovery server: service started on ${address?.address}:${address?.port}`); }); this.server.bind(PORT); } close(callback) { this.stopped = true; if (this.initTimer) { clearTimeout(this.initTimer); this.initTimer = null; } if (this.server) { try { this.server.close(callback); this.server = null; } catch { this.server = null; if (callback) { callback(); } } } else if (callback) { callback(); } } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { MHServer }); //# sourceMappingURL=multihostServer.js.map