Started work on IP-KVM/Power relay thru agent.
Ylian Saint-Hilaire committed
Apr 18, 2022 at 15:48 UTC
aa514534c40b51f409bcd7e166d3674d5fd29126
1 file changed
+113
-3
meshipkvm.js
+113
-3
@@ -67,6 +67,7 @@ function CreateIPKVMManager(parent) {
67
if (mesh.kvm.model == 1) { // Raritan KX III
68
const manager = CreateRaritanKX3Manager(obj, host, port, mesh.kvm.user, mesh.kvm.pass);
69
manager.meshid = mesh._id;
70
+ manager.relayid = mesh.relayid;
71
manager.domainid = mesh._id.split('/')[1];
72
obj.managedGroups[mesh._id] = manager;
73
manager.onStateChanged = onStateChanged;
@@ -76,6 +77,7 @@ function CreateIPKVMManager(parent) {
77
else if (mesh.kvm.model == 2) { // WebPowerSwitch 7
78
const manager = CreateWebPowerSwitch(obj, host, port, mesh.kvm.user, mesh.kvm.pass);
79
manager.meshid = mesh._id;
80
+ manager.relayid = mesh.relayid;
81
manager.domainid = mesh._id.split('/')[1];
82
obj.managedGroups[mesh._id] = manager;
83
manager.onStateChanged = onStateChanged;
@@ -774,7 +776,14 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) {
776
obj.start = function () {
777
if (obj.started) return;
778
obj.started = true;
777
- if (obj.state == 0) connect();
779
+ if (obj.state == 0) {
780
+ if (obj.relayid) {
781
+ obj.router = CreateMiniRouter(parent, obj.relayid, hostname, port);
782
+ obj.router.start(function () { connect(); });
783
+ } else {
784
+ connect();
785
+ }
786
+ }
787
}
788
789
obj.stop = function () {
@@ -783,6 +792,7 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) {
792
if (retryTimer != null) { clearTimeout(retryTimer); retryTimer = null; }
793
setState(0);
794
obj.ports = [];
795
+ if (obj.router) { obj.router.stop(); delete obj.router; }
796
}
797
798
function setState(newState) {
@@ -856,8 +866,8 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) {
866
867
var rdata = [];
868
const options = {
859
- hostname: hostname,
860
- port: port,
869
+ hostname: obj.router ? 'localhost' : hostname,
870
+ port: obj.router ? obj.router.tcpServerPort : port,
871
rejectUnauthorized: false,
872
checkServerIdentity: onCheckServerIdentity,
873
path: url,
@@ -945,4 +955,104 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) {
955
}
956
957
958
+// Mini TCP port router
959
+function CreateMiniRouter(parent, nodeid, targetHost, targetPort) {
960
+ const Net = require('net');
961
+ const WebSocket = require('ws');
962
+ const obj = {};
963
+ const tcpSockets = {}
964
+ obj.tcpServerPort = 0;
965
+ obj.nodeid = nodeid;
966
+ obj.targetHost = targetHost;
967
+ obj.targetPort = targetPort;
968
+
969
+ parent.parent.debug('relay', 'MiniRouter: Request relay for ' + obj.targetHost + ':' + obj.targetPort + ' thru ' + nodeid + '.');
970
+
971
+ // Close a TCP socket and the coresponding web socket.
972
+ function closeTcpSocket(tcpSocket) {
973
+ if (tcpSockets[tcpSocket]) {
974
+ 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;
979
+ }
980
+ }
981
+
982
+ // Close a web socket and the coresponding TCP socket.
983
+ function closeWebSocket(webSocket) {
984
+ const tcpSocket = webSocket.tcpSocket;
985
+ if (tcpSocket) { closeTcpSocket(tcpSocket); }
986
+ }
987
+
988
+ // Start the looppback server
989
+ obj.start = function(onReadyFunc) {
990
+ obj.tcpServer = new Net.Server();
991
+ obj.tcpServer.listen(0, 'localhost', function () {
992
+ obj.tcpServerPort = obj.tcpServer.address().port;
993
+ parent.parent.debug('relay', 'MiniRouter: Request for relay ' + obj.targetHost + ':' + obj.targetPort + ' started on port ' + obj.tcpServerPort);
994
+ onReadyFunc(obj.tcpServerPort);
995
+ });
996
+ obj.tcpServer.on('connection', function (socket) {
997
+ tcpSockets[socket] = 1;
998
+ socket.pause();
999
+ socket.on('data', function (chunk) { // Make sure to handle flow control.
1000
+ console.log('<-- ' + chunk);
1001
+ const f = function sendDone() { sendDone.tcpSocket.resume(); }
1002
+ f.tcpSocket = this;
1003
+ if (this.relaySocket && this.relaySocket.active) { this.pause(); this.relaySocket.send(chunk, f); }
1004
+ });
1005
+ socket.on('end', function () { close(this); });
1006
+ socket.on('error', function (err) { close(this); });
1007
+
1008
+ // Encode the device relay cookie. Note that there si no userid in this cookie.
1009
+ const domainid = obj.nodeid.split('/')[1];
1010
+ const cookie = parent.parent.encodeCookie({ domainid: domainid, nodeid: obj.nodeid, tcpaddr: obj.targetHost, tcpport: obj.targetPort }, parent.parent.loginCookieEncryptionKey);
1011
+ const domain = parent.parent.config.domains[domainid];
1012
+
1013
+ // Setup the correct URL with domain and use TLS only if needed.
1014
+ const options = { rejectUnauthorized: false };
1015
+ const protocol = (parent.parent.args.tlsoffload) ? 'ws' : 'wss';
1016
+ var domainadd = '';
1017
+ 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?
1019
+ parent.parent.debug('relay', 'MiniRouter: Connection websocket to ' + url);
1020
+ socket.relaySocket = new WebSocket(url, options);
1021
+ socket.relaySocket.tcpSocket = socket;
1022
+ socket.relaySocket.on('open', function () { parent.parent.debug('relay', 'MiniRouter: Relay websocket open'); });
1023
+ socket.relaySocket.on('message', function (data) { // Make sure to handle flow control.
1024
+ console.log('--> ' + data);
1025
+ if (!this.active) {
1026
+ if (data == 'c') {
1027
+ // Relay Web socket is connected, start data relay
1028
+ this.active = true;
1029
+ this.tcpSocket.resume();
1030
+ } else {
1031
+ // Could not connect web socket, close it
1032
+ console.log('ERR', data);
1033
+ closeWebSocket(this);
1034
+ }
1035
+ } else {
1036
+ // Relay more data
1037
+ this._socket.pause();
1038
+ const f = function sendDone() { sendDone.webSocket._socket.resume(); }
1039
+ f.webSocket = this;
1040
+ this.tcpSocket.write(data, f);
1041
+ }
1042
+ });
1043
+ socket.relaySocket.on('close', function () { parent.parent.debug('relay', 'MiniRouter: Relay websocket closed'); closeWebSocket(this); });
1044
+ socket.relaySocket.on('error', function (err) { parent.parent.debug('relay', 'MiniRouter: Relay websocket error: ' + err); closeWebSocket(this); });
1045
+ });
1046
+ }
1047
+
1048
+ // Stop the looppback server and all relay sockets
1049
+ obj.stop = function () {
1050
+ for (var tcpSocket in tcpSockets) { closeTcpSocket(tcpSocket); }
1051
+ obj.tcpServer.close();
1052
+ obj.tcpServer = null;
1053
+ }
1054
+
1055
+ return obj;
1056
+}
1057
+
1058
module.exports.CreateIPKVMManager = CreateIPKVMManager;
\ No newline at end of file