Skip to content

Commit a2f100a

Browse files
basic (noncompliant) exec impl
1 parent dae5b5d commit a2f100a

1 file changed

Lines changed: 57 additions & 1 deletion

File tree

src/programs/services/sshd.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ interface ChannelWindowChangeRequestMessage extends ChannelRequestMessage {
143143
height_px: number;
144144
}
145145

146+
interface ChannelExecRequestMessage extends ChannelRequestMessage {
147+
request_type: "exec";
148+
command: string;
149+
}
150+
146151
interface ChannelSuccessMessage extends SSHMessageBase {
147152
type: "CHANNEL_SUCCESS";
148153
recipient_channel: number;
@@ -162,7 +167,7 @@ type SSHMessage =
162167
| UserAuthRequestMessage | UserAuthFailureMessage | UserAuthSuccessMessage | UserAuthBannerMessage
163168
| ChannelOpenMessage | ChannelOpenConfirmationMessage | ChannelOpenFailureMessage |
164169
ChannelRequestMessage | ChannelSuccessMessage | ChannelFailureMessage |
165-
ChannelWindowChangeRequestMessage |
170+
ChannelWindowChangeRequestMessage | ChannelExecRequestMessage |
166171
ChannelDataMessage;
167172

168173
type SSHMessageType = SSHMessage["type"];
@@ -462,6 +467,9 @@ const message_deserialisers: Record<number, (reader: MessageReader) => SSHMessag
462467
const height_px = reader.read_uint32();
463468

464469
return { type: "CHANNEL_REQUEST", recipient_channel, request_type, want_reply, cols, rows, width_px, height_px };
470+
} else if (request_type === "exec") {
471+
const command = reader.read_string();
472+
return { type: "CHANNEL_REQUEST", recipient_channel, request_type, want_reply, command };
465473
}
466474

467475
return { type: "CHANNEL_REQUEST", recipient_channel, request_type, want_reply };
@@ -1323,6 +1331,54 @@ export default {
13231331

13241332
await send_encrypted_message_atomic(success_message);
13251333
}
1334+
} else if (message.request_type === "exec") {
1335+
if (!channel.terminal) {
1336+
// create a terminal if it doesn't exist, since exec can be used without pty-req
1337+
channel.terminal = new SSHTerminal(message.recipient_channel);
1338+
}
1339+
1340+
const command = (message as ChannelExecRequestMessage).command;
1341+
1342+
// spawn the exec process
1343+
// TODO: when piping exists then pipe channel data to process stdin
1344+
let spawn_result: SpawnResult;
1345+
try {
1346+
spawn_result = kernel.spawn(
1347+
command,
1348+
[],
1349+
undefined,
1350+
false,
1351+
channel.terminal
1352+
);
1353+
} catch (e) {
1354+
console.error("Failed to spawn process for exec request:", e);
1355+
1356+
if (message.want_reply) {
1357+
const failure_message: ChannelFailureMessage = {
1358+
type: "CHANNEL_FAILURE",
1359+
recipient_channel: message.recipient_channel
1360+
};
1361+
1362+
await send_encrypted_message_atomic(failure_message);
1363+
}
1364+
1365+
return;
1366+
}
1367+
1368+
// acknowledge the spawn
1369+
if (message.want_reply) {
1370+
const success_message: ChannelSuccessMessage = {
1371+
type: "CHANNEL_SUCCESS",
1372+
recipient_channel: message.recipient_channel
1373+
}
1374+
1375+
await send_encrypted_message_atomic(success_message);
1376+
}
1377+
1378+
// handle process termination
1379+
spawn_result.completion.then((exit_code) => {
1380+
spawn_result.process.kill(exit_code);
1381+
});
13261382
} else {
13271383
// unsupported request type, ignore
13281384
if (message.want_reply) {

0 commit comments

Comments
 (0)