86 lines
2.2 KiB
JavaScript
86 lines
2.2 KiB
JavaScript
import repl from 'node:repl';
|
|
import net from 'node:net';
|
|
import { Buffer } from 'node:buffer';
|
|
import { timingSafeEqual } from 'node:crypto';
|
|
import { waitEvent } from 'compass-utils';
|
|
import { RemoteTTY } from './remote-tty.mjs';
|
|
|
|
import debug from 'debug';
|
|
const logger = debug('compass-repl');
|
|
|
|
export function createRepl(istream, ostream, prompt, contextLoader) {
|
|
let r = repl.start({
|
|
prompt: `${prompt} > `,
|
|
input: istream,
|
|
output: ostream,
|
|
color: true,
|
|
terminal: true,
|
|
ignoreUndefined: true,
|
|
});
|
|
r.on('SIGCONT', () => { });
|
|
r.on('SIGTSTP', () => {});
|
|
contextLoader(r.context);
|
|
return r;
|
|
}
|
|
|
|
export function createLocalRepl(contextLoader) {
|
|
return createRepl(process.stdin, process.stdout, 'local', contextLoader);
|
|
}
|
|
|
|
export function createTcpReplServer(port, passcode, contextLoader) {
|
|
const connect_repl_sockets = new Set();
|
|
let server = net.createServer(socket => {
|
|
logger('New TCP connection');
|
|
function verifyPasscode(data) {
|
|
let pdata = Buffer.from(data);
|
|
logger(`Got passcode: ${pdata.toString('hex')}`);
|
|
let flag = true;
|
|
if (pdata.length != passcode.length) {
|
|
flag = false;
|
|
pdata = passcode;
|
|
}
|
|
if (timingSafeEqual(passcode, pdata) && flag) {
|
|
logger('Passcode correct, 0x21!');
|
|
socket.write(Buffer.from([0x21]));
|
|
let tty = new RemoteTTY({}, socket, socket);
|
|
tty.ttyReady().then(() => {
|
|
logger('Remote TTY is ready, starting REPL');
|
|
let r = createRepl(tty, tty, 'tcp', contextLoader);
|
|
r.on('exit', () => {
|
|
connect_repl_sockets.delete(socket);
|
|
socket.destroy();
|
|
});
|
|
connect_repl_sockets.add(socket);
|
|
}).catch(err => {
|
|
logger(`TTY Ready Error: ${err}`);
|
|
socket.destroy();
|
|
});
|
|
} else {
|
|
logger('Passcode incorrect, 0x20!');
|
|
socket.write(Buffer.from([0x20]));
|
|
socket.destroy();
|
|
}
|
|
}
|
|
|
|
try {
|
|
socket.once('data', (data) => verifyPasscode(data));
|
|
|
|
socket.on('close', () => {
|
|
logger('Disconnected');
|
|
connect_repl_sockets.delete(socket);
|
|
});
|
|
} catch(err) {
|
|
logger(`Remote Connect Error: ${err}`);
|
|
connect_repl_sockets.delete(socket);
|
|
socket.destroy();
|
|
}
|
|
});
|
|
server.listen(port);
|
|
process.on('exit', () => {
|
|
for (const socket of connect_repl_sockets) {
|
|
socket.destroy();
|
|
}
|
|
});
|
|
return server;
|
|
}
|