diff --git a/apprelays.js b/apprelays.js index 8309bb90..e27a6756 100644 --- a/apprelays.js +++ b/apprelays.js @@ -21,26 +21,30 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { const WebSocket = require('ws'); var obj = {}; - obj.domain = domain; obj.ws = ws; - obj.wsClient = null; - obj.tcpServer = null; obj.tcpServerPort = 0; - obj.relaySocket = null; obj.relayActive = false; - obj.infos = null; var rdpClient = null; parent.parent.debug('relay', 'RDP: Request for RDP relay (' + req.clientIp + ')'); // Disconnect obj.close = function (arg) { + if (obj.ws == null) return; + + // Collect how many raw bytes where received and sent. + // We sum both the websocket and TCP client in this case. + //var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten; + //if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; } + //console.log('WinRDP - in', inTraffc, 'out', outTraffc); + + if (obj.wsClient) { obj.wsClient.close(); delete obj.wsClient; } + if (obj.tcpServer) { obj.tcpServer.close(); obj.tcpServer.destroy(); delete obj.tcpServer; } + if (rdpClient) { rdpClient.close(); rdpClient = null; } if ((arg == 1) || (arg == null)) { try { ws.close(); } catch (e) { console.log(e); } } // Soft close, close the websocket if (arg == 2) { try { ws._socket._parent.end(); } catch (e) { console.log(e); } } // Hard close, close the TCP socket - if (obj.wsClient) { obj.wsClient.close(); obj.wsClient = null; } - if (obj.tcpServer) { obj.tcpServer.close(); obj.tcpServer = null; } - if (rdpClient) { rdpClient.close(); rdpClient = null; } - delete obj.domain; + obj.ws.removeAllListeners(); + obj.relayActive = false; delete obj.ws; }; @@ -67,11 +71,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { // Setup the correct URL with domain and use TLS only if needed. var options = { rejectUnauthorized: false }; if (domain.dns != null) { options.servername = domain.dns; } - var protocol = 'wss'; - if (args.tlsoffload) { protocol = 'ws'; } + var protocol = (args.tlsoffload) ? 'ws' : 'wss'; var domainadd = ''; if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' } - var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + ((cookie.lc == 1)?'local':'mesh') + 'relay.ashx?noping=1&auth=' + obj.infos.ip; + var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + ((cookie.lc == 1) ? 'local' : 'mesh') + 'relay.ashx?noping=1&p=10&auth=' + obj.infos.ip; // Protocol 10 is Web-RDP parent.parent.debug('relay', 'RDP: Connection websocket to ' + url); obj.wsClient = new WebSocket(url, options); obj.wsClient.on('open', function () { parent.parent.debug('relay', 'RDP: Relay websocket open'); }); @@ -170,48 +173,56 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { var obj = new require('stream').Duplex(options); obj.forwardwrite = null; obj.updateBuffer = function (chunk) { this.push(chunk); }; - obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err("Failed to fwd _write."); } if (callback) callback(); }; // Pass data written to forward + obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } if (callback) callback(); }; // Pass data written to forward obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer() + obj.destroy = function () { delete obj.forwardwrite; } return obj; } const obj = {}; - obj.domain = domain; obj.ws = ws; obj.relayActive = false; - obj.sshClient = null; - obj.sshShell = null; - obj.termSize = null; - obj.relayActive = false; - obj.wsClient = null; parent.parent.debug('relay', 'SSH: Request for SSH relay (' + req.clientIp + ')'); // Disconnect obj.close = function (arg) { - if ((arg == 1) || (arg == null)) { try { ws.close(); } catch (e) { console.log(e); } } // Soft close, close the websocket - if (arg == 2) { try { ws._socket._parent.end(); } catch (e) { console.log(e); } } // Hard close, close the TCP socket - //if (obj.wsClient) { obj.wsClient.close(); obj.wsClient = null; } - //if (obj.tcpServer) { obj.tcpServer.close(); obj.tcpServer = null; } - //if (sshClient) { sshClient.close(); sshClient = null; } + if (obj.ws == null) return; - if (obj.wsClient != null) { - try { obj.wsClient.close(); } catch (ex) { console.log(ex); } - delete obj.wsClient; - } - if (obj.sshClient != null) { - try { obj.sshClient.end(); } catch (ex) { console.log(ex); } - delete obj.sshClient; - } - if (obj.sshShell != null) { + // Collect how many raw bytes where received and sent. + // We sum both the websocket and TCP client in this case. + //var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten; + //if (obj.wsClient != null) { inTraffc += obj.wsClient._socket.bytesRead; outTraffc += obj.wsClient._socket.bytesWritten; } + //console.log('WinSSH - in', inTraffc, 'out', outTraffc); + + if (obj.sshShell) { + obj.sshShell.destroy(); + obj.sshShell.removeAllListeners('data'); + obj.sshShell.removeAllListeners('close'); try { obj.sshShell.end(); } catch (ex) { console.log(ex); } delete obj.sshShell; } + if (obj.sshClient) { + obj.sshClient.destroy(); + obj.sshClient.removeAllListeners('ready'); + try { obj.sshClient.end(); } catch (ex) { console.log(ex); } + delete obj.sshClient; + } + if (obj.wsClient) { + obj.wsClient.removeAllListeners('open'); + obj.wsClient.removeAllListeners('message'); + obj.wsClient.removeAllListeners('close'); + try { obj.wsClient.close(); } catch (ex) { console.log(ex); } + delete obj.wsClient; + } + + if ((arg == 1) || (arg == null)) { try { ws.close(); } catch (e) { console.log(e); } } // Soft close, close the websocket + if (arg == 2) { try { ws._socket._parent.end(); } catch (e) { console.log(e); } } // Hard close, close the TCP socket + obj.ws.removeAllListeners(); obj.relayActive = false; delete obj.termSize; delete obj.cookie; - delete obj.domain; delete obj.ws; }; @@ -229,7 +240,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { if (args.tlsoffload) { protocol = 'ws'; } var domainadd = ''; if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' } - var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + ((obj.cookie.lc == 1) ? 'local' : 'mesh') + 'relay.ashx?noping=1&auth=' + req.query.auth; + var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + ((obj.cookie.lc == 1) ? 'local' : 'mesh') + 'relay.ashx?noping=1&p=11&auth=' + req.query.auth; // Protocol 11 is Web-SSH parent.parent.debug('relay', 'SSH: Connection websocket to ' + url); obj.wsClient = new WebSocket(url, options); obj.wsClient.on('open', function () { parent.parent.debug('relay', 'SSH: Relay websocket open'); }); @@ -306,10 +317,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { // Terminal data if (obj.sshShell != null) { obj.sshShell.write(msg.substring(1)); } } - } catch (ex) { - console.log('SSHMessageException', msg, ex); - obj.close(); - } + } catch (ex) { obj.close(); } }); // If error, do nothing @@ -317,9 +325,6 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { // If the web socket is closed ws.on('close', function (req) { parent.parent.debug('relay', 'SSH: Browser websocket closed'); obj.close(); }); - - // Send data on the web socket - //function send(obj) { try { ws.send(JSON.stringify(obj), function () { }); } catch (ex) { } } return obj; }; \ No newline at end of file diff --git a/meshrelay.js b/meshrelay.js index d555bf19..4a1d6320 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -36,6 +36,17 @@ const MESHRIGHT_RESETOFF = 0x00040000; const MESHRIGHT_GUESTSHARING = 0x00080000; const MESHRIGHT_ADMIN = 0xFFFFFFFF; +// Protocol: +// 1 = Terminal +// 2 = Desktop +// 5 = Files +// 10 = Web-RDP +// 11 = Web-SSH +// 12 = Web-VNC +// 100 = Intel AMT WSMAN +// 101 = Intel AMT Redirection +// 200 = Messenger + function checkDeviceSharePublicIdentifier(parent, domain, nodeid, pid, func) { // Check the public id parent.db.GetAllTypeNodeFiltered([nodeid], domain.id, 'deviceshare', null, function (err, docs) { @@ -1011,15 +1022,24 @@ function CreateLocalRelayEx(parent, ws, req, domain, user, cookie) { // Disconnect obj.close = function (arg) { - if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Soft disconnect'); } catch (e) { console.log(e); } } // Soft close, close the websocket - if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket + // If the web socket is already closed, stop here. + if (obj.ws == null) return; + + // Collect how many raw bytes where received and sent. + // We sum both the websocket and TCP client in this case. + var inTraffc = obj.ws._socket.bytesRead, outTraffc = obj.ws._socket.bytesWritten; + if (obj.client != null) { inTraffc += obj.client.bytesRead; outTraffc += obj.client.bytesWritten; } + + // Close the web socket + if ((arg == 1) || (arg == null)) { try { obj.ws.close(); parent.parent.debug('relay', 'Relay: Soft disconnect'); } catch (e) { console.log(e); } } // Soft close, close the websocket + if (arg == 2) { try { obj.ws._socket._parent.end(); parent.parent.debug('relay', 'Relay: Hard disconnect'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket // Update the relay session count if (obj.relaySessionCounted) { parent.relaySessionCount--; delete obj.relaySessionCounted; } - // Log the disconnection + // Log the disconnection, traffic will be credited to the authenticated user if (obj.time) { - var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: obj.user._id, username: obj.user.name, msgid: 9, msgArgs: [obj.id, obj.req.clientIp, obj.host, Math.floor((Date.now() - obj.time) / 1000)], msg: 'Ended relay session \"' + obj.id + '\" from ' + obj.req.clientIp + ' to ' + obj.host + ', ' + Math.floor((Date.now() - obj.time) / 1000) + ' second(s)', nodeid: obj.req.query.nodeid }; + var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: obj.user._id, username: obj.user.name, msgid: 9, msgArgs: [obj.id, obj.req.clientIp, obj.host, Math.floor((Date.now() - obj.time) / 1000)], msg: 'Ended relay session \"' + obj.id + '\" from ' + obj.req.clientIp + ' to ' + obj.host + ', ' + Math.floor((Date.now() - obj.time) / 1000) + ' second(s)', nodeid: obj.req.query.nodeid, protocol: req.query.p, in: inTraffc, out: outTraffc }; parent.parent.DispatchEvent(['*', user._id], obj, event); } @@ -1031,7 +1051,7 @@ function CreateLocalRelayEx(parent, ws, req, domain, user, cookie) { delete obj.meshid; delete obj.tcpport; delete obj.expireTimer; - if (obj.client != null) { obj.client.destroy(); delete obj.client; } + if (obj.client != null) { obj.client.destroy(); delete obj.client; } // Close the client socket if (obj.pingtimer != null) { clearInterval(obj.pingtimer); delete obj.pingtimer; } if (obj.pongtimer != null) { clearInterval(obj.pongtimer); delete obj.pongtimer; } @@ -1066,7 +1086,7 @@ function CreateLocalRelayEx(parent, ws, req, domain, user, cookie) { obj.client.connect(obj.tcpport, node.host, function () { // Log the start of the connection obj.time = Date.now(); - var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: obj.user._id, username: obj.user.name, msgid: 13, msgArgs: [obj.id, obj.req.clientIp, obj.host], msg: 'Started relay session \"' + obj.id + '\" from ' + obj.req.clientIp + ' to ' + obj.host, nodeid: req.query.nodeid }; + var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: obj.user._id, username: obj.user.name, msgid: 13, msgArgs: [obj.id, obj.req.clientIp, obj.host], msg: 'Started relay session \"' + obj.id + '\" from ' + obj.req.clientIp + ' to ' + obj.host, nodeid: req.query.nodeid, protocol: req.query.p }; parent.parent.DispatchEvent(['*', obj.user._id, obj.meshid, obj.nodeid], obj, event); // Start the session diff --git a/public/novnc/core/rfb.js b/public/novnc/core/rfb.js index 89b8130d..6b8c0985 100644 --- a/public/novnc/core/rfb.js +++ b/public/novnc/core/rfb.js @@ -397,7 +397,7 @@ export default class RFB extends EventTargetMixin { try { // WebSocket.onopen transitions to the RFB init states - this._sock.open(this._url, ['binary']); + this._sock.open(this._url + '&p=12', ['binary']); } catch (e) { if (e.name === 'SyntaxError') { this._fail("Invalid host or port (" + e + ")");