1. Added Windows Terminal Fix (ConPTY Leak/Workaround) back in 2. Added python/script XOPTION 3. Changed resize to use tcsetsize if available 4. Added tunnel idleTimeout Ping
1. Added Windows Terminal Fix (ConPTY Leak/Workaround) back in 2. Added python/script XOPTION 3. Changed resize to use tcsetsize if available 4. Added tunnel idleTimeout Ping
Bryan Roe committed
Jan 28, 2020 at 15:08 UTC
e7cd6d1db4044a717415c65cf72f6f39b3d65ae7
1 file changed
+92
-49
agents/meshcore.js
+92
-49
@@ -1140,117 +1140,160 @@ function createMeshCore(agent) {
1140
};
1141
1142
// Remote terminal using native pipes
1143
- if (process.platform == 'win32') {
1144
- try {
1143
+ if (process.platform == 'win32')
1144
+ {
1145
+ try
1146
+ {
1147
var cols = 80, rows = 25;
1146
- if (this.httprequest.xoptions) {
1148
+ if (this.httprequest.xoptions)
1149
+ {
1150
if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1151
if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1152
}
1153
1154
if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9)) { throw ('PowerShell is not supported on this version of windows'); }
1152
- if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6)) {
1155
+ if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1156
+ {
1157
// Admin Terminal
1154
- if (require('win-virtual-terminal').supported) {
1158
+ if (require('win-virtual-terminal').supported)
1159
+ {
1160
// ConPTY PseudoTerminal
1156
- this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows);
1161
+ // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1162
+
1163
+ // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1164
+ this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1165
+ this.httprequest._dispatcher.ws = this;
1166
+ this.httprequest._dispatcher.on('connection', function (c)
1167
+ {
1168
+ console.log('client connected');
1169
+ this.ws._term = c;
1170
+ c.pipe(this.ws, { dataTypeSkip: 1 });
1171
+ this.ws.pipe(c, { dataTypeSkip: 1 });
1172
+ });
1173
}
1158
- else {
1174
+ else
1175
+ {
1176
// Legacy Terminal
1177
this.httprequest._term = require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows);
1178
}
1162
- } else {
1179
+ }
1180
+ else
1181
+ {
1182
// Logged in user
1183
var userPromise = require('user-sessions').enumerateUsers();
1184
userPromise.that = this;
1166
- userPromise.then(function (u) {
1185
+ userPromise.then(function (u)
1186
+ {
1187
var that = this.that;
1168
- if (u.Active.length > 0) {
1188
+ if (u.Active.length > 0)
1189
+ {
1190
var username = u.Active[0].Username;
1170
- if (require('win-virtual-terminal').supported) {
1191
+ if (require('win-virtual-terminal').supported)
1192
+ {
1193
// ConPTY PseudoTerminal
1194
that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1173
- } else {
1195
+ }
1196
+ else
1197
+ {
1198
// Legacy Terminal
1199
that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1200
}
1201
that.httprequest._dispatcher.ws = that;
1178
- that.httprequest._dispatcher.on('connection', function (c) {
1202
+ that.httprequest._dispatcher.on('connection', function (c)
1203
+ {
1204
console.log('client connected');
1205
this.ws._term = c;
1206
c.pipe(this.ws, { dataTypeSkip: 1 });
1182
- this.ws.pipe(c, { dataTypeSkip: 1, end: false });
1183
- this.ws.prependListener('end', function () {
1184
- if (this.httprequest._term) { this.httprequest._term.end(function () { console.log("Terminal was closed"); }); }
1185
- });
1207
+ this.ws.pipe(c, { dataTypeSkip: 1 });
1208
});
1209
}
1188
- });
1210
+ });
1211
}
1190
- } catch (e) {
1212
+ } catch (e)
1213
+ {
1214
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
1215
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
1216
this.end();
1217
return;
1218
}
1196
- if (!this.httprequest._dispatcher) {
1219
+ if (!this.httprequest._dispatcher)
1220
+ {
1221
this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
1222
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
1223
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log("Terminal was closed"); }); });
1224
}
1201
- } else {
1202
- try {
1225
+ }
1226
+ else
1227
+ {
1228
+ try
1229
+ {
1230
var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1231
var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1232
var script = false;
1206
- try {
1207
- if (require('linux-gnome-helpers').scriptVersion) {
1208
- if (require('linux-gnome-helpers').scriptVersion.major > 2 ||
1209
- (require('linux-gnome-helpers').scriptVersion.major == 2 && require('linux-gnome-helpers').scriptVersion.minor >= 25)) {
1210
- script = '/usr/bin/script';
1233
+ if (this.httprequest.xoptions.script)
1234
+ {
1235
+ try
1236
+ {
1237
+ if (require('linux-gnome-helpers').scriptVersion)
1238
+ {
1239
+ if (require('linux-gnome-helpers').scriptVersion.major > 2 ||
1240
+ (require('linux-gnome-helpers').scriptVersion.major == 2 && require('linux-gnome-helpers').scriptVersion.minor >= 25))
1241
+ {
1242
+ script = '/usr/bin/script';
1243
+ }
1244
}
1212
- }
1213
- } catch (ex) { }
1214
- var python = fs.existsSync('/usr/bin/python') ? '/usr/bin/python' : false;
1245
+ } catch (ex) { }
1246
+ }
1247
+ var python = (this.httprequest.xoptions.python && fs.existsSync('/usr/bin/python')) ? '/usr/bin/python' : false;
1248
var shell = bash || sh;
1216
- var pty = python || script;
1249
1250
var env = { HISTCONTROL: 'ignoreboth', TERM: 'xterm' };
1219
- if (this.httprequest.xoptions) {
1251
+ if (this.httprequest.xoptions)
1252
+ {
1253
if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1254
if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1255
}
1256
var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1257
var setupcommands = ' alias ls=\'ls --color=auto\'\n';
1258
if (shell == sh) setupcommands += ' stty erase ^H\n';
1226
- // Dynamic resizing is only supported in PTYs
1227
- if (pty) setupcommands += ' mcresize() { old=$(stty -g);stty raw -echo min 0 time 5;printf \'\\0337\\033[r\\033[999;999H\\033[6n\\0338\' > /dev/tty;IFS=\'[;R\' read -r _ rows cols _ < /dev/tty;stty "$old";stty cols "$cols" rows "$rows"; };trap mcresize SIGWINCH;\n'
1259
1229
- if (script && shell && process.platform == 'linux') {
1260
+ if (script && shell && process.platform == 'linux')
1261
+ {
1262
this.httprequest.process = childProcess.execFile(script, ['script', '--return', '--quiet', '-c', '"' + shell + '"', '/dev/null'], options); // Start as active user
1263
this.httprequest.process.stdin.write(setupcommands);
1232
- } else if (python && shell) {
1264
+ }
1265
+ else if (python && shell)
1266
+ {
1267
this.httprequest.process = childProcess.execFile(python, ['python', '-c', 'import pty; pty.spawn(["' + shell + '"])'], options); // Start as active user
1268
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
1235
- } else if (bash) {
1269
+ }
1270
+ else if (bash)
1271
+ {
1272
options.type = childProcess.SpawnTypes.TERM;
1273
this.httprequest.process = childProcess.execFile(bash, ['bash', '-i'], options); // Start as active user
1274
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
1239
- } else if (sh) {
1275
+ }
1276
+ else if (sh)
1277
+ {
1278
options.type = childProcess.SpawnTypes.TERM;
1279
this.httprequest.process = childProcess.execFile(sh, ['sh'], options); // Start as active user
1280
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands + "PS1='$ '\n"); }
1243
- } else {
1281
+ }
1282
+ else
1283
+ {
1284
MeshServerLog("Failed to start remote terminal session, no shell found");
1285
return;
1286
}
1247
- } catch (e) {
1287
+ } catch (e)
1288
+ {
1289
MeshServerLog("Failed to start remote terminal session, " + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
1290
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
1291
this.end();
1292
return;
1293
}
1253
- this.httprequest.process.pty = pty;
1294
+
1295
+ if (this.httprequest.process.tcsetsize && this.httprequest.xoptions) { this.httprequest.process.tcsetsize(this.httprequest.xoptions.rows, this.httprequest.xoptions.cols); }
1296
+
1297
this.httprequest.process.tunnel = this;
1298
this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
1299
this.httprequest.process.stdin.write(" clear\n");
@@ -1786,17 +1829,17 @@ function createMeshCore(agent) {
1829
}
1830
case 'termsize': {
1831
// Indicates a change in terminal size
1789
- if (process.platform == 'win32') {
1832
+ if (process.platform == 'win32')
1833
+ {
1834
if (ws.httprequest._term == null) return;
1835
//sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
1836
// TODO
1793
- } else {
1794
- if (ws.httprequest.process == null || !ws.httprequest.process.pty) return;
1795
- // ILibDuktape_ChildProcess kill doesn't support sending signals
1796
- if (fs.existsSync('/bin/kill')) {
1797
- // We need to send signal to the child of the process, since the child is the shell
1798
- childProcess.execFile('/bin/bash', ['bash', '-c', 'kill -SIGWINCH $(pgrep -P ' + ws.httprequest.process.pid + ')']);
1799
- }
1837
+ } else
1838
+ {
1839
+ if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
1840
+ //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
1841
+
1842
+ if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
1843
}
1844
break;
1845
}