From 61e38cbe36c6f9866388fd0d45ffd44b954ad7b8 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Sun, 5 Dec 2021 20:56:57 -0800 Subject: [PATCH] Started work on IP-KVM relay code. --- meshipkvm.js | 24 ++++++++++++------------ webserver.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/meshipkvm.js b/meshipkvm.js index 13006ece..488093df 100644 --- a/meshipkvm.js +++ b/meshipkvm.js @@ -8,7 +8,7 @@ function CreateIPKVMManager(parent) { const obj = {}; - const managedGroups = {} // meshid --> Manager + obj.managedGroups = {} // meshid --> Manager obj.managedPorts = {} // nodeid --> PortInfo // Subscribe for mesh creation events @@ -31,14 +31,14 @@ function CreateIPKVMManager(parent) { // Start managing a IP KVM device function startManagement(mesh) { - if ((mesh == null) || (mesh.mtype != 4) || (mesh.kvm == null) || (mesh.deleted != null) || (managedGroups[mesh._id] != null)) return; + if ((mesh == null) || (mesh.mtype != 4) || (mesh.kvm == null) || (mesh.deleted != null) || (obj.managedGroups[mesh._id] != null)) return; var port = 443, hostSplit = mesh.kvm.host.split(':'), host = hostSplit[0]; if (hostSplit.length == 2) { port = parseInt(hostSplit[1]); } if (mesh.kvm.model == 1) { // Raritan KX III const manager = CreateRaritanKX3Manager(host, port, mesh.kvm.user, mesh.kvm.pass); manager.meshid = mesh._id; manager.domainid = mesh._id.split('/')[1]; - managedGroups[mesh._id] = manager; + obj.managedGroups[mesh._id] = manager; manager.onStateChanged = onStateChanged; manager.onPortsChanged = onPortsChanged; manager.start(); @@ -47,7 +47,7 @@ function CreateIPKVMManager(parent) { // Stop managing a IP KVM device function stopManagement(meshid) { - const manager = managedGroups[meshid]; + const manager = obj.managedGroups[meshid]; if (manager != null) { // Remove all managed ports for (var i = 0; i < manager.ports.length; i++) { @@ -57,7 +57,7 @@ function CreateIPKVMManager(parent) { } // Remove the manager - delete managedGroups[meshid]; + delete obj.managedGroups[meshid]; manager.stop(); } } @@ -107,7 +107,7 @@ function CreateIPKVMManager(parent) { // Set the connectivity state if needed if (obj.managedPorts[nodeid] == null) { parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, 1, null, null); - obj.managedPorts[nodeid] = { name: port.Name }; + obj.managedPorts[nodeid] = { name: port.Name, meshid: sender.meshid, portid: port.PortId }; } // Update busy state @@ -265,7 +265,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) { } function fetchInitialInformation() { - fetch('/webs_cron.asp?_portsstatushash=&_devicesstatushash=&webs_job=sidebarupdates', null, null, function (server, tag, data) { + obj.fetch('/webs_cron.asp?_portsstatushash=&_devicesstatushash=&webs_job=sidebarupdates', null, null, function (server, tag, data) { const parsed = parseJsScript(data); for (var i in parsed['updateSidebarPanel']) { if (parsed['updateSidebarPanel'][i][0] == "cron_device") { @@ -273,7 +273,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) { obj.deviceModel = getSubString(parsed['updateSidebarPanel'][i][1], "
", "<"); } } - fetch('/sidebar.asp', null, null, function (server, tag, data) { + obj.fetch('/sidebar.asp', null, null, function (server, tag, data) { var dataBlock = getSubString(data, "updateKVMLinkHintOnContainer();", "devices.resetDevicesNew(1);"); if (dataBlock == null) { setState(0); return; } const parsed = parseJsScript(dataBlock); @@ -294,7 +294,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) { } obj.update = function () { - fetch('/webs_cron.asp?_portsstatushash=' + obj.portHash + '&_devicesstatushash=' + obj.deviceHash, null, null, function (server, tag, data) { + obj.fetch('/webs_cron.asp?_portsstatushash=' + obj.portHash + '&_devicesstatushash=' + obj.deviceHash, null, null, function (server, tag, data) { const parsed = parseJsScript(data); if (parsed['updatePortStatus']) { obj.portCount = parseInt(parsed['updatePortStatus'][0][0]) - 2; @@ -396,7 +396,7 @@ function CreateRaritanKX3Manager(hostname, port, username, password) { return ((char >= 'A') && (char <= 'Z')) || ((char >= 'a') && (char <= 'z')) || ((char >= '0') && (char <= '9')); } - function fetch(url, postdata, tag, func) { + obj.fetch = function(url, postdata, tag, func) { if (obj.state == 0) return; var data = ''; const options = { @@ -416,9 +416,9 @@ function CreateRaritanKX3Manager(hostname, port, username, password) { if (res.statusCode != 200) { setState(0); return; } 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]; } } } res.on('data', function (d) { data += d; }); - res.on('end', function () { func(obj, tag, data); }); + res.on('end', function () { func(obj, tag, data, res); }); }); - req.on('error', function (error) { setState(0); }) + req.on('error', function (error) { console.log(error); setState(0); }) req.end(); } diff --git a/webserver.js b/webserver.js index 0a2201bf..b32508e0 100644 --- a/webserver.js +++ b/webserver.js @@ -5848,6 +5848,59 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF obj.app.get(url + 'pluginHandler.js', obj.handlePluginJS); } + // Setup IP-KVM relay if supported + if (domain.ipkvm) { + obj.app.ws(url + 'ipkvm.ashx/*', function (ws, req) { + const domain = getDomain(req); + if (domain == null) { parent.debug('web', 'ipkvm: failed domain checks.'); try { ws.close(); } catch (ex) { } return; } + const q = require('url').parse(req.url, true); + const i = q.path.indexOf('/ipkvm.ashx/'); + if (i == -1) { parent.debug('web', 'ipkvm: failed url checks.'); try { ws.close(); } catch (ex) { } return; } + const urlargs = q.path.substring(i + 12).split('/'); + if (urlargs[0].length != 64) { parent.debug('web', 'ipkvm: failed nodeid length checks.'); try { ws.close(); } catch (ex) { } return; } + const nodeid = 'node/' + domain.id + '/' + urlargs[0]; + const nid = urlargs[0]; + const kvmport = parent.ipKvmManager.managedPorts[nodeid]; + if (kvmport == null) { parent.debug('web', 'ipkvm: failed port checks.'); try { ws.close(); } catch (ex) { } return; } + const kvmmanager = parent.ipKvmManager.managedGroups[kvmport.meshid]; + if (kvmmanager == null) { parent.debug('web', 'ipkvm: failed manager checks.'); try { ws.close(); } catch (ex) { } return; } + urlargs.shift(); + var relurl = '/' + urlargs.join('/') + if (relurl.endsWith('/.websocket')) { relurl = relurl.substring(0, relurl.length - 11); } + console.log('ws', relurl); // TODO + }); + obj.app.get(url + 'ipkvm.ashx/*', function (req, res, next) { + const domain = getDomain(req); + if (domain == null) { return; } + const q = require('url').parse(req.url, true); + const i = q.path.indexOf('/ipkvm.ashx/'); + if (i == -1) { next(); return; } + const urlargs = q.path.substring(i + 12).split('/'); + if (urlargs[0].length != 64) { next(); return; } + const nodeid = 'node/' + domain.id + '/' + urlargs[0]; + const nid = urlargs[0]; + const kvmport = parent.ipKvmManager.managedPorts[nodeid]; + if (kvmport == null) { next(); return; } + const kvmmanager = parent.ipKvmManager.managedGroups[kvmport.meshid]; + if (kvmmanager == null) { next(); return; } + urlargs.shift(); + const relurl = '/' + urlargs.join('/') + '#portId=' + kvmport.portid; + // Example: /jsclient/Client.asp#portId=P_000d5d20f64c_1 + kvmmanager.fetch(relurl, null, [res, nid], function (server, args, data, rres) { + const resx = args[0], nidx = args[1]; + if (rres.headers['content-type']) { resx.set('content-type', rres.headers['content-type']); } + + // We need to replace the WebSocket code in one of the files to make it work with our server. + // Replace "b=new WebSocket(e+"//"+c+"/"+g);" in file "/js/js_kvm_client.1604062083669.min.js" + if (relurl.startsWith('/js/js_kvm_client.')) { + data = data.replace('b=new WebSocket(e+"//"+c+"/"+g);', 'b=new WebSocket(e+"//"+c+"/ipkvm.ashx/' + nidx + '/"+g);'); + } + + resx.end(data); + }); + }); + } + // Setup MSTSC.js if needed if (domain.mstsc === true) { obj.app.get(url + 'mstsc.html', function (req, res) { handleMSTSCRequest(req, res, 'mstsc'); });