2026-04-26 11:25:46 -06:00
|
|
|
//! Remote shell controller example.
|
|
|
|
|
//!
|
|
|
|
|
//! This binary listens for the endpoint example, opens one remote shell session, sends a few
|
|
|
|
|
//! commands, and prints returned hook data until the shell closes.
|
|
|
|
|
|
2026-04-25 14:41:00 -06:00
|
|
|
use std::error::Error;
|
|
|
|
|
use std::net::TcpListener;
|
|
|
|
|
|
2026-04-26 12:57:56 -06:00
|
|
|
use unshell::leaves::remote_shell;
|
|
|
|
|
use unshell::leaves::remote_shell::OpenRequest;
|
|
|
|
|
use unshell::protocol::tree::encode_call_reply;
|
2026-04-25 20:47:37 -06:00
|
|
|
use unshell::protocol::tree::{Endpoint, EndpointOutcome, Ingress, LocalEvent};
|
2026-04-25 14:41:00 -06:00
|
|
|
|
|
|
|
|
fn main() -> Result<(), Box<dyn Error>> {
|
2026-04-26 12:57:56 -06:00
|
|
|
let listener = TcpListener::bind(remote_shell::endpoint::LISTEN_ADDR)?;
|
|
|
|
|
println!("listening on {}", remote_shell::endpoint::LISTEN_ADDR);
|
2026-04-25 14:41:00 -06:00
|
|
|
|
|
|
|
|
let (mut stream, peer_addr) = listener.accept()?;
|
|
|
|
|
println!("accepted endpoint connection from {peer_addr}");
|
|
|
|
|
|
2026-04-26 12:57:56 -06:00
|
|
|
let frame_rx = remote_shell::endpoint::spawn_frame_reader(stream.try_clone()?);
|
|
|
|
|
let mut endpoint = remote_shell::endpoint::build_controller_endpoint();
|
2026-04-25 14:41:00 -06:00
|
|
|
let hook_id = endpoint.allocate_hook_id();
|
2026-04-26 12:57:56 -06:00
|
|
|
let shell_leaf_name = remote_shell::endpoint::RemoteShellEndpoint::protocol_leaf_name();
|
|
|
|
|
let open_procedure = remote_shell::endpoint::ProcedureOpen::protocol_procedure_id();
|
2026-04-25 14:41:00 -06:00
|
|
|
|
2026-04-26 12:57:56 -06:00
|
|
|
remote_shell::endpoint::send_forward(
|
2026-04-25 15:35:08 -06:00
|
|
|
&mut stream,
|
2026-04-25 16:27:10 -06:00
|
|
|
endpoint.send_call(
|
2026-04-26 12:57:56 -06:00
|
|
|
agent_path(),
|
2026-04-25 16:27:10 -06:00
|
|
|
Some(shell_leaf_name),
|
|
|
|
|
open_procedure.clone(),
|
|
|
|
|
Some(hook_id),
|
2026-04-26 12:57:56 -06:00
|
|
|
encode_call_reply(&OpenRequest).expect("remote shell open payload should encode"),
|
2026-04-25 16:27:10 -06:00
|
|
|
)?,
|
2026-04-25 14:41:00 -06:00
|
|
|
)?;
|
|
|
|
|
|
2026-04-25 15:35:08 -06:00
|
|
|
for (index, command) in ["pwd\n", "whoami\n", "exit\n"].iter().enumerate() {
|
2026-04-26 12:57:56 -06:00
|
|
|
remote_shell::endpoint::send_forward(
|
2026-04-25 15:35:08 -06:00
|
|
|
&mut stream,
|
2026-04-25 16:27:10 -06:00
|
|
|
endpoint.send_data(
|
2026-04-26 12:57:56 -06:00
|
|
|
agent_path(),
|
2026-04-25 16:27:10 -06:00
|
|
|
hook_id,
|
|
|
|
|
open_procedure.clone(),
|
|
|
|
|
command.as_bytes().to_vec(),
|
|
|
|
|
index == 2,
|
|
|
|
|
)?,
|
2026-04-25 15:35:08 -06:00
|
|
|
)?;
|
|
|
|
|
}
|
2026-04-25 14:41:00 -06:00
|
|
|
|
|
|
|
|
for result in frame_rx {
|
|
|
|
|
let frame = result?;
|
2026-04-26 12:57:56 -06:00
|
|
|
let outcome = endpoint.receive(&Ingress::Child(agent_path()), frame)?;
|
2026-04-25 20:47:37 -06:00
|
|
|
let EndpointOutcome::Local(event) = outcome else {
|
2026-04-25 14:41:00 -06:00
|
|
|
continue;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match event {
|
|
|
|
|
LocalEvent::Data { message, .. } => {
|
|
|
|
|
print!("{}", String::from_utf8_lossy(&message.data));
|
|
|
|
|
|
|
|
|
|
if message.end_hook {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LocalEvent::Fault { message, .. } => {
|
|
|
|
|
eprintln!("received protocol fault: 0x{:02X}", message.fault.0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
LocalEvent::Call { .. } => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-04-26 12:57:56 -06:00
|
|
|
|
|
|
|
|
fn agent_path() -> Vec<String> {
|
|
|
|
|
vec![String::from("agent")]
|
|
|
|
|
}
|