Added support for dynamic relayid change for WebPowerSwitch and Raritan.

Ylian Saint-Hilaire committed Apr 19, 2022 at 10:48 UTC 3a1d3433cfc17467b064f0576dc95e8462f6a279
3 files changed +47 -21
meshipkvm.js
+45 -19
@@ -37,7 +37,7 @@ function CreateIPKVMManager(parent) {
37 const MESHRIGHT_ADMIN = 0xFFFFFFFF;
38
39 // Subscribe for mesh creation events
40 - parent.AddEventDispatch(['server-createmesh', 'server-deletemesh', 'devport-operation'], obj);
40 + parent.AddEventDispatch(['server-createmesh', 'server-deletemesh', 'server-editmesh', 'devport-operation'], obj);
41 obj.HandleEvent = function (source, event, ids, id) {
42 if ((event == null) || (event.mtype != 4)) return;
43 if (event.action == 'createmesh') {
@@ -46,6 +46,9 @@ function CreateIPKVMManager(parent) {
46 } else if (event.action == 'deletemesh') {
47 // Stop managing this device group
48 stopManagement(event.meshid);
49 + } else if ((event.action == 'meshchange') && (event.relayid != null)) {
50 + // See if the relayid changed
51 + changeManagementRelayId(event.meshid, event.relayid);
52 } else if ((event.action == 'turnon') || (event.action == 'turnoff')) {
53 // Perform power operation
54 const manager = obj.managedGroups[event.meshid];
@@ -73,8 +76,7 @@ function CreateIPKVMManager(parent) {
76 manager.onStateChanged = onStateChanged;
77 manager.onPortsChanged = onPortsChanged;
78 manager.start();
76 - }
77 - else if (mesh.kvm.model == 2) { // WebPowerSwitch 7
79 + } else if (mesh.kvm.model == 2) { // WebPowerSwitch 7
80 const manager = CreateWebPowerSwitch(obj, host, port, mesh.kvm.user, mesh.kvm.pass);
81 manager.meshid = mesh._id;
82 manager.relayid = mesh.relayid;
@@ -103,6 +105,12 @@ function CreateIPKVMManager(parent) {
105 }
106 }
107
108 + // Change the relayid of a managed device if needed
109 + function changeManagementRelayId(meshid, relayid) {
110 + const manager = obj.managedGroups[meshid];
111 + if ((manager != null) && (manager.relayid != null) && (manager.relayid != relayid)) { manager.updateRelayId(relayid); }
112 + }
113 +
114 // Called when a KVM device changes state
115 function onStateChanged(sender, state) {
116 /*
@@ -342,7 +350,12 @@ function CreateRaritanKX3Manager(parent, hostname, port, username, password) {
350 obj.start = function () {
351 if (obj.started) return;
352 obj.started = true;
345 - if (obj.state == 0) connect();
353 + if (obj.relayid) {
354 + obj.router = CreateMiniRouter(parent, obj.relayid, hostname, port);
355 + obj.router.start(function () { connect(); });
356 + } else {
357 + connect();
358 + }
359 }
360
361 obj.stop = function () {
@@ -350,6 +363,13 @@ function CreateRaritanKX3Manager(parent, hostname, port, username, password) {
363 obj.started = false;
364 if (retryTimer != null) { clearTimeout(retryTimer); retryTimer = null; }
365 setState(0);
366 + if (obj.router) { obj.router.stop(); delete obj.router; }
367 + }
368 +
369 + // If the relay device has changed, update our router
370 + obj.updateRelayId = function (relayid) {
371 + obj.relayid = relayid;
372 + if (obj.router != null) { obj.router.nodeid = relayid; }
373 }
374
375 function setState(newState) {
@@ -421,6 +441,7 @@ function CreateRaritanKX3Manager(parent, hostname, port, username, password) {
441
442 function fetchInitialInformation() {
443 obj.fetch('/webs_cron.asp?_portsstatushash=&_devicesstatushash=&webs_job=sidebarupdates', null, null, function (server, tag, data) {
444 + data = data.toString();
445 const parsed = parseJsScript(data);
446 for (var i in parsed['updateSidebarPanel']) {
447 if (parsed['updateSidebarPanel'][i][0] == "cron_device") {
@@ -558,8 +579,8 @@ function CreateRaritanKX3Manager(parent, hostname, port, username, password) {
579
580 var data = [];
581 const options = {
561 - hostname: hostname,
562 - port: port,
582 + hostname: obj.router ? 'localhost' : hostname,
583 + port: obj.router ? obj.router.tcpServerPort : port,
584 rejectUnauthorized: false,
585 checkServerIdentity: onCheckServerIdentity,
586 path: url,
@@ -795,6 +816,12 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) {
816 if (obj.router) { obj.router.stop(); delete obj.router; }
817 }
818
819 + // If the relay device has changed, update our router
820 + obj.updateRelayId = function (relayid) {
821 + obj.relayid = relayid;
822 + if (obj.router != null) { obj.router.nodeid = relayid; }
823 + }
824 +
825 function setState(newState) {
826 if (obj.state == newState) return;
827 obj.state = newState;
@@ -860,7 +887,6 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) {
887 }
888
889 obj.fetch = function (url, method, data, tag, func) {
863 - //console.log('fetch', url, method, data, tag);
890 if (obj.state == 0) return;
891 if (typeof data == 'string') { data = Buffer.from(data); }
892
@@ -972,10 +998,12 @@ function CreateMiniRouter(parent, nodeid, targetHost, targetPort) {
998 function closeTcpSocket(tcpSocket) {
999 if (tcpSockets[tcpSocket]) {
1000 delete tcpSockets[tcpSocket];
975 - try { tcpSocket.close(); } catch (ex) { }
976 - if (tcpSocket.relaySocket) { try { tcpSocket.relaySocket.close(); } catch (ex) { } }
977 - delete tcpSocket.relaySocket.tcpSocket;
978 - delete tcpSocket.relaySocket;
1001 + try { tcpSocket.end(); } catch (ex) { console.log(ex); }
1002 + if (tcpSocket.relaySocket) { try { tcpSocket.relaySocket.close(); } catch (ex) { console.log(ex); } }
1003 + if (tcpSocket) {
1004 + delete tcpSocket.relaySocket.tcpSocket;
1005 + delete tcpSocket.relaySocket;
1006 + }
1007 }
1008 }
1009
@@ -991,19 +1019,19 @@ function CreateMiniRouter(parent, nodeid, targetHost, targetPort) {
1019 obj.tcpServer.listen(0, 'localhost', function () {
1020 obj.tcpServerPort = obj.tcpServer.address().port;
1021 parent.parent.debug('relay', 'MiniRouter: Request for relay ' + obj.targetHost + ':' + obj.targetPort + ' started on port ' + obj.tcpServerPort);
994 - onReadyFunc(obj.tcpServerPort);
1022 + onReadyFunc(obj.tcpServerPort, obj);
1023 });
1024 obj.tcpServer.on('connection', function (socket) {
1025 tcpSockets[socket] = 1;
1026 socket.pause();
1027 socket.on('data', function (chunk) { // Make sure to handle flow control.
1000 - console.log('<-- ' + chunk);
1028 const f = function sendDone() { sendDone.tcpSocket.resume(); }
1029 f.tcpSocket = this;
1030 if (this.relaySocket && this.relaySocket.active) { this.pause(); this.relaySocket.send(chunk, f); }
1031 });
1005 - socket.on('end', function () { close(this); });
1006 - socket.on('error', function (err) { close(this); });
1032 + socket.on('end', function () { closeTcpSocket(this); });
1033 + socket.on('close', function () { closeTcpSocket(this); });
1034 + socket.on('error', function (err) { closeTcpSocket(this); });
1035
1036 // Encode the device relay cookie. Note that there is no userid in this cookie.
1037 const domainid = obj.nodeid.split('/')[1];
@@ -1015,13 +1043,12 @@ function CreateMiniRouter(parent, nodeid, targetHost, targetPort) {
1043 const protocol = (parent.parent.args.tlsoffload) ? 'ws' : 'wss';
1044 var domainadd = '';
1045 if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
1018 - const url = protocol + '://localhost:' + parent.parent.args.port + '/' + domainadd + 'meshrelay.ashx?noping=1&auth=' + cookie; // TODO: &p=10, Protocol 10 is Web-RDP, Specify TCP routing protocol?
1046 + const url = protocol + '://localhost:' + parent.parent.args.port + '/' + domainadd + 'meshrelay.ashx?noping=1&hd=1&auth=' + cookie; // TODO: &p=10, Protocol 10 is Web-RDP, Specify TCP routing protocol?
1047 parent.parent.debug('relay', 'MiniRouter: Connection websocket to ' + url);
1048 socket.relaySocket = new WebSocket(url, options);
1049 socket.relaySocket.tcpSocket = socket;
1050 socket.relaySocket.on('open', function () { parent.parent.debug('relay', 'MiniRouter: Relay websocket open'); });
1051 socket.relaySocket.on('message', function (data) { // Make sure to handle flow control.
1024 - console.log('--> ' + data);
1052 if (!this.active) {
1053 if (data == 'c') {
1054 // Relay Web socket is connected, start data relay
@@ -1029,7 +1056,6 @@ function CreateMiniRouter(parent, nodeid, targetHost, targetPort) {
1056 this.tcpSocket.resume();
1057 } else {
1058 // Could not connect web socket, close it
1032 - console.log('ERR', data);
1059 closeWebSocket(this);
1060 }
1061 } else {
@@ -1040,7 +1066,7 @@ function CreateMiniRouter(parent, nodeid, targetHost, targetPort) {
1066 this.tcpSocket.write(data, f);
1067 }
1068 });
1043 - socket.relaySocket.on('close', function () { parent.parent.debug('relay', 'MiniRouter: Relay websocket closed'); closeWebSocket(this); });
1069 + socket.relaySocket.on('close', function (reasonCode, description) { parent.parent.debug('relay', 'MiniRouter: Relay websocket closed'); closeWebSocket(this); });
1070 socket.relaySocket.on('error', function (err) { parent.parent.debug('relay', 'MiniRouter: Relay websocket error: ' + err); closeWebSocket(this); });
1071 });
1072 }
meshrelay.js
+1 -1
@@ -141,7 +141,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
141 // Disconnect this agent
142 obj.close = function (arg) {
143 if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Soft disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
144 - if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
144 + if ((arg == 2) || (req.query.hd == 1)) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect (' + obj.req.clientIp + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket
145
146 // Aggressive cleanup
147 delete obj.id;
meshuser.js
+1 -1
@@ -2199,7 +2199,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2199 db.Set(mesh);
2200 var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msgid: 142, msgArgs: [mesh.name, changesids], msg: change, domain: domain.id, invite: mesh.invite, expireDevs: command.expireDevs, relayid: mesh.relayid };
2201 if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come.
2202 - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(mesh, [user._id]), obj, event);
2202 + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(mesh, [user._id, 'server-editmesh']), obj, event);
2203 }
2204
2205 // Notify the devices that they have changed relay roles