Started work on IP-KVM relay code.

Ylian Saint-Hilaire committed Dec 5, 2021 at 20:56 UTC 61e38cbe36c6f9866388fd0d45ffd44b954ad7b8
2 files changed +65 -12
meshipkvm.js
+12 -12
@@ -8,7 +8,7 @@
8
9 function CreateIPKVMManager(parent) {
10 const obj = {};
11 - const managedGroups = {} // meshid --> Manager
11 + obj.managedGroups = {} // meshid --> Manager
12 obj.managedPorts = {} // nodeid --> PortInfo
13
14 // Subscribe for mesh creation events
@@ -31,14 +31,14 @@ function CreateIPKVMManager(parent) {
31
32 // Start managing a IP KVM device
33 function startManagement(mesh) {
34 - if ((mesh == null) || (mesh.mtype != 4) || (mesh.kvm == null) || (mesh.deleted != null) || (managedGroups[mesh._id] != null)) return;
34 + if ((mesh == null) || (mesh.mtype != 4) || (mesh.kvm == null) || (mesh.deleted != null) || (obj.managedGroups[mesh._id] != null)) return;
35 var port = 443, hostSplit = mesh.kvm.host.split(':'), host = hostSplit[0];
36 if (hostSplit.length == 2) { port = parseInt(hostSplit[1]); }
37 if (mesh.kvm.model == 1) { // Raritan KX III
38 const manager = CreateRaritanKX3Manager(host, port, mesh.kvm.user, mesh.kvm.pass);
39 manager.meshid = mesh._id;
40 manager.domainid = mesh._id.split('/')[1];
41 - managedGroups[mesh._id] = manager;
41 + obj.managedGroups[mesh._id] = manager;
42 manager.onStateChanged = onStateChanged;
43 manager.onPortsChanged = onPortsChanged;
44 manager.start();
@@ -47,7 +47,7 @@ function CreateIPKVMManager(parent) {
47
48 // Stop managing a IP KVM device
49 function stopManagement(meshid) {
50 - const manager = managedGroups[meshid];
50 + const manager = obj.managedGroups[meshid];
51 if (manager != null) {
52 // Remove all managed ports
53 for (var i = 0; i < manager.ports.length; i++) {
@@ -57,7 +57,7 @@ function CreateIPKVMManager(parent) {
57 }
58
59 // Remove the manager
60 - delete managedGroups[meshid];
60 + delete obj.managedGroups[meshid];
61 manager.stop();
62 }
63 }
@@ -107,7 +107,7 @@ function CreateIPKVMManager(parent) {
107 // Set the connectivity state if needed
108 if (obj.managedPorts[nodeid] == null) {
109 parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, 1, null, null);
110 - obj.managedPorts[nodeid] = { name: port.Name };
110 + obj.managedPorts[nodeid] = { name: port.Name, meshid: sender.meshid, portid: port.PortId };
111 }
112
113 // Update busy state
@@ -265,7 +265,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) {
265 }
266
267 function fetchInitialInformation() {
268 - fetch('/webs_cron.asp?_portsstatushash=&_devicesstatushash=&webs_job=sidebarupdates', null, null, function (server, tag, data) {
268 + obj.fetch('/webs_cron.asp?_portsstatushash=&_devicesstatushash=&webs_job=sidebarupdates', null, null, function (server, tag, data) {
269 const parsed = parseJsScript(data);
270 for (var i in parsed['updateSidebarPanel']) {
271 if (parsed['updateSidebarPanel'][i][0] == "cron_device") {
@@ -273,7 +273,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) {
273 obj.deviceModel = getSubString(parsed['updateSidebarPanel'][i][1], "<div class=\"device-model\">", "<");
274 }
275 }
276 - fetch('/sidebar.asp', null, null, function (server, tag, data) {
276 + obj.fetch('/sidebar.asp', null, null, function (server, tag, data) {
277 var dataBlock = getSubString(data, "updateKVMLinkHintOnContainer();", "devices.resetDevicesNew(1);");
278 if (dataBlock == null) { setState(0); return; }
279 const parsed = parseJsScript(dataBlock);
@@ -294,7 +294,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) {
294 }
295
296 obj.update = function () {
297 - fetch('/webs_cron.asp?_portsstatushash=' + obj.portHash + '&_devicesstatushash=' + obj.deviceHash, null, null, function (server, tag, data) {
297 + obj.fetch('/webs_cron.asp?_portsstatushash=' + obj.portHash + '&_devicesstatushash=' + obj.deviceHash, null, null, function (server, tag, data) {
298 const parsed = parseJsScript(data);
299 if (parsed['updatePortStatus']) {
300 obj.portCount = parseInt(parsed['updatePortStatus'][0][0]) - 2;
@@ -396,7 +396,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) {
396 return ((char >= 'A') && (char <= 'Z')) || ((char >= 'a') && (char <= 'z')) || ((char >= '0') && (char <= '9'));
397 }
398
399 - function fetch(url, postdata, tag, func) {
399 + obj.fetch = function(url, postdata, tag, func) {
400 if (obj.state == 0) return;
401 var data = '';
402 const options = {
@@ -416,9 +416,9 @@ function CreateRaritanKX3Manager(hostname, port, username, password) {
416 if (res.statusCode != 200) { setState(0); return; }
417 if (res.headers['set-cookie'] != null) { for (var i in res.headers['set-cookie']) { if (res.headers['set-cookie'][i].startsWith('pp_session_id=')) { obj.authCookie = res.headers['set-cookie'][i].substring(14).split(';')[0]; } } }
418 res.on('data', function (d) { data += d; });
419 - res.on('end', function () { func(obj, tag, data); });
419 + res.on('end', function () { func(obj, tag, data, res); });
420 });
421 - req.on('error', function (error) { setState(0); })
421 + req.on('error', function (error) { console.log(error); setState(0); })
422 req.end();
423 }
424
webserver.js
+53
@@ -5848,6 +5848,59 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5848 obj.app.get(url + 'pluginHandler.js', obj.handlePluginJS);
5849 }
5850
5851 + // Setup IP-KVM relay if supported
5852 + if (domain.ipkvm) {
5853 + obj.app.ws(url + 'ipkvm.ashx/*', function (ws, req) {
5854 + const domain = getDomain(req);
5855 + if (domain == null) { parent.debug('web', 'ipkvm: failed domain checks.'); try { ws.close(); } catch (ex) { } return; }
5856 + const q = require('url').parse(req.url, true);
5857 + const i = q.path.indexOf('/ipkvm.ashx/');
5858 + if (i == -1) { parent.debug('web', 'ipkvm: failed url checks.'); try { ws.close(); } catch (ex) { } return; }
5859 + const urlargs = q.path.substring(i + 12).split('/');
5860 + if (urlargs[0].length != 64) { parent.debug('web', 'ipkvm: failed nodeid length checks.'); try { ws.close(); } catch (ex) { } return; }
5861 + const nodeid = 'node/' + domain.id + '/' + urlargs[0];
5862 + const nid = urlargs[0];
5863 + const kvmport = parent.ipKvmManager.managedPorts[nodeid];
5864 + if (kvmport == null) { parent.debug('web', 'ipkvm: failed port checks.'); try { ws.close(); } catch (ex) { } return; }
5865 + const kvmmanager = parent.ipKvmManager.managedGroups[kvmport.meshid];
5866 + if (kvmmanager == null) { parent.debug('web', 'ipkvm: failed manager checks.'); try { ws.close(); } catch (ex) { } return; }
5867 + urlargs.shift();
5868 + var relurl = '/' + urlargs.join('/')
5869 + if (relurl.endsWith('/.websocket')) { relurl = relurl.substring(0, relurl.length - 11); }
5870 + console.log('ws', relurl); // TODO
5871 + });
5872 + obj.app.get(url + 'ipkvm.ashx/*', function (req, res, next) {
5873 + const domain = getDomain(req);
5874 + if (domain == null) { return; }
5875 + const q = require('url').parse(req.url, true);
5876 + const i = q.path.indexOf('/ipkvm.ashx/');
5877 + if (i == -1) { next(); return; }
5878 + const urlargs = q.path.substring(i + 12).split('/');
5879 + if (urlargs[0].length != 64) { next(); return; }
5880 + const nodeid = 'node/' + domain.id + '/' + urlargs[0];
5881 + const nid = urlargs[0];
5882 + const kvmport = parent.ipKvmManager.managedPorts[nodeid];
5883 + if (kvmport == null) { next(); return; }
5884 + const kvmmanager = parent.ipKvmManager.managedGroups[kvmport.meshid];
5885 + if (kvmmanager == null) { next(); return; }
5886 + urlargs.shift();
5887 + const relurl = '/' + urlargs.join('/') + '#portId=' + kvmport.portid;
5888 + // Example: /jsclient/Client.asp#portId=P_000d5d20f64c_1
5889 + kvmmanager.fetch(relurl, null, [res, nid], function (server, args, data, rres) {
5890 + const resx = args[0], nidx = args[1];
5891 + if (rres.headers['content-type']) { resx.set('content-type', rres.headers['content-type']); }
5892 +
5893 + // We need to replace the WebSocket code in one of the files to make it work with our server.
5894 + // Replace "b=new WebSocket(e+"//"+c+"/"+g);" in file "/js/js_kvm_client.1604062083669.min.js"
5895 + if (relurl.startsWith('/js/js_kvm_client.')) {
5896 + data = data.replace('b=new WebSocket(e+"//"+c+"/"+g);', 'b=new WebSocket(e+"//"+c+"/ipkvm.ashx/' + nidx + '/"+g);');
5897 + }
5898 +
5899 + resx.end(data);
5900 + });
5901 + });
5902 + }
5903 +
5904 // Setup MSTSC.js if needed
5905 if (domain.mstsc === true) {
5906 obj.app.get(url + 'mstsc.html', function (req, res) { handleMSTSCRequest(req, res, 'mstsc'); });