Added full agent ping/pong support on SSH/SFTP sessions.

Ylian Saint-Hilaire committed May 5, 2022 at 16:16 UTC 5869782bdf227343aa99502577cac28eb2ff046e
3 files changed +61 -28
apprelays.js
+55 -27
@@ -212,9 +212,11 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
212
213 // When data is received from the web socket
214 // RDP default port is 3389
215 - ws.on('message', function (msg) {
215 + ws.on('message', function (data) {
216 try {
217 - msg = JSON.parse(msg);
217 + var msg = null;
218 + try { msg = JSON.parse(data); } catch (ex) { }
219 + if ((msg == null) || (typeof msg != 'object')) return;
220 switch (msg[0]) {
221 case 'infos': {
222 obj.infos = msg[1];
@@ -493,7 +495,13 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
495 ws._socket.resume();
496 }
497 } else {
496 - if (typeof data == 'string') return; // Only process binary data, this will include ping/ping
498 + if (typeof data == 'string') {
499 + // Forward any ping/pong commands to the browser
500 + var cmd = null;
501 + try { cmd = JSON.parse(data); } catch (ex) { }
502 + if ((cmd != null) && (cmd.ctrlChannel == '102938') && ((cmd.type == 'ping') || (cmd.type == 'pong'))) { obj.ws.send(data); }
503 + return;
504 + }
505
506 // Relay WS --> SSH
507 if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
@@ -508,12 +516,15 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
516
517 // When data is received from the web socket
518 // SSH default port is 22
511 - ws.on('message', function (msg) {
519 + ws.on('message', function (data) {
520 try {
513 - if (typeof msg != 'string') return;
514 - if (msg[0] == '{') {
521 + if (typeof data != 'string') return;
522 + if (data[0] == '{') {
523 // Control data
516 - msg = JSON.parse(msg);
524 + var msg = null;
525 + try { msg = JSON.parse(data); } catch (ex) { }
526 + if ((msg == null) || (typeof msg != 'object')) return;
527 + if ((msg.ctrlChannel == '102938') && ((msg.type == 'ping') || (msg.type == 'pong'))) { try { obj.wsClient.send(data); } catch (ex) { } return; }
528 if (typeof msg.action != 'string') return;
529 switch (msg.action) {
530 case 'connect': {
@@ -563,9 +574,9 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
574 break;
575 }
576 }
566 - } else if (msg[0] == '~') {
577 + } else if (data[0] == '~') {
578 // Terminal data
568 - if (obj.sshShell != null) { obj.sshShell.write(msg.substring(1)); }
579 + if (obj.sshShell != null) { obj.sshShell.write(data.substring(1)); }
580 }
581 } catch (ex) { obj.close(); }
582 });
@@ -790,7 +801,13 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
801 ws._socket.resume();
802 }
803 } else {
793 - if (typeof data == 'string') return; // Only process binary data, this will include ping/ping
804 + if (typeof data == 'string') {
805 + // Forward any ping/pong commands to the browser
806 + var cmd = null;
807 + try { cmd = JSON.parse(data); } catch (ex) { }
808 + if ((cmd != null) && (cmd.ctrlChannel == '102938') && ((cmd.type == 'ping') || (cmd.type == 'pong'))) { try { obj.ws.send(data); } catch (ex) { console.log(ex); } }
809 + return;
810 + }
811
812 // Relay WS --> SSH
813 if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
@@ -808,13 +825,15 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
825
826 // When data is received from the web socket
827 // SSH default port is 22
811 - ws.on('message', function (msg) {
828 + ws.on('message', function (data) {
829 try {
813 - if (typeof msg != 'string') return;
814 - if (msg[0] == '{') {
830 + if (typeof data != 'string') return;
831 + if (data[0] == '{') {
832 // Control data
816 - msg = JSON.parse(msg);
817 - if (typeof msg.action != 'string') return;
833 + var msg = null;
834 + try { msg = JSON.parse(data); } catch (ex) { }
835 + if ((msg == null) || (typeof msg != 'object')) return;
836 + if ((msg.ctrlChannel == '102938') && ((msg.type == 'ping') || (msg.type == 'pong'))) { try { obj.wsClient.send(data); } catch (ex) { } return; }
837 switch (msg.action) {
838 case 'sshauth': {
839 // Verify inputs
@@ -866,9 +885,9 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
885 break;
886 }
887 }
869 - } else if (msg[0] == '~') {
888 + } else if (data[0] == '~') {
889 // Terminal data
871 - if (obj.sshShell != null) { obj.sshShell.write(msg.substring(1)); }
890 + if (obj.sshShell != null) { obj.sshShell.write(data.substring(1)); }
891 }
892 } catch (ex) { obj.close(); }
893 });
@@ -1095,7 +1114,13 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1114 ws._socket.resume();
1115 }
1116 } else {
1098 - if (typeof data == 'string') return; // Only process binary data, this will include ping/ping
1117 + if (typeof data == 'string') {
1118 + // Forward any ping/pong commands to the browser
1119 + var cmd = null;
1120 + try { cmd = JSON.parse(data); } catch (ex) { }
1121 + if ((cmd != null) && (cmd.ctrlChannel == '102938') && ((cmd.type == 'ping') || (cmd.type == 'pong'))) { obj.ws.send(data); }
1122 + return;
1123 + }
1124
1125 // Relay WS --> SSH
1126 if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
@@ -1113,15 +1138,15 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1138
1139 // When data is received from the web socket
1140 // SSH default port is 22
1116 - ws.on('message', function (msg) {
1141 + ws.on('message', function (data) {
1142 //if ((obj.firstMessage === true) && (msg != 5)) { obj.close(); return; } else { delete obj.firstMessage; }
1143 try {
1119 - if (typeof msg != 'string') {
1120 - if (msg[0] == 123) {
1121 - msg = msg.toString();
1144 + if (typeof data != 'string') {
1145 + if (data[0] == 123) {
1146 + data = data.toString();
1147 } else if ((obj.sftp != null) && (obj.uploadHandle != null)) {
1123 - const off = (msg[0] == 0) ? 1 : 0;
1124 - obj.sftp.write(obj.uploadHandle, msg, off, msg.length - off, obj.uploadPosition, function (err) {
1148 + const off = (data[0] == 0) ? 1 : 0;
1149 + obj.sftp.write(obj.uploadHandle, data, off, data.length - off, obj.uploadPosition, function (err) {
1150 if (err != null) {
1151 obj.sftp.close(obj.uploadHandle, function () { });
1152 try { obj.ws.send(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: obj.uploadReqid }))) } catch (ex) { }
@@ -1134,13 +1159,16 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
1159 try { obj.ws.send(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: obj.uploadReqid }))) } catch (ex) { }
1160 }
1161 });
1137 - obj.uploadPosition += (msg.length - off);
1162 + obj.uploadPosition += (data.length - off);
1163 return;
1164 }
1165 }
1141 - if (msg[0] == '{') {
1166 + if (data[0] == '{') {
1167 // Control data
1143 - msg = JSON.parse(msg);
1168 + var msg = null;
1169 + try { msg = JSON.parse(data); } catch (ex) { }
1170 + if ((msg == null) || (typeof msg != 'object')) return;
1171 + if ((msg.ctrlChannel == '102938') && ((msg.type == 'ping') || (msg.type == 'pong'))) { try { obj.wsClient.send(data); } catch (ex) { } return; }
1172 if (typeof msg.action != 'string') return;
1173 switch (msg.action) {
1174 case 'ls': {
public/scripts/agent-redir-ws-0.1.1.js
+2
@@ -109,6 +109,8 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
109 } else if (controlMsg.type == 'webrtc2') {
110 // TODO: Resume/Start sending data over WebRTC
111 }
112 + } else if (controlMsg.type == 'ping') { // if we get a ping, respond with a pong.
113 + obj.sendCtrlMsg('{"ctrlChannel":"102938","type":"pong"}');
114 }
115 }
116
views/ssh.handlebars
+4 -1
@@ -186,7 +186,10 @@
186 socket.onmessage = function (data) {
187 if (typeof data.data != 'string') return;
188 if (data.data[0] == '{') {
189 - var json = JSON.parse(data.data);
189 + var json = null;
190 + try { json = JSON.parse(data.data); } catch (ex) { }
191 + if ((json == null) || (typeof json != 'object')) return;
192 + if ((json.ctrlChannel == 102938) && (json.type == 'ping')) { socket.send('{"ctrlChannel":"102938","type":"pong"}'); return; }
193 switch (json.action) {
194 case 'connected': { state = 3; updateState(); term.focus(); break; }
195 case 'sshauth': {