mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 08:25:05 +01:00
ineteractive "tty"
This commit is contained in:
parent
6486bb4c81
commit
c7965e404a
3 changed files with 113 additions and 13 deletions
|
|
@ -13,4 +13,4 @@ useless_format = "allow"
|
|||
|
||||
[workspace.dependencies]
|
||||
tracing = "0.1.40"
|
||||
eyre = "0.6.12"
|
||||
eyre = "0.6.12"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
mod readline;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use cluelessh_tokio::{server::ServerAuthVerify, Channel};
|
||||
|
|
@ -134,6 +136,8 @@ async fn handle_session_channel(
|
|||
mut channel: Channel,
|
||||
total_sent_data: Arc<Mutex<Vec<u8>>>,
|
||||
) -> Result<()> {
|
||||
let mut readline = None;
|
||||
|
||||
loop {
|
||||
match channel.next_update().await {
|
||||
Ok(update) => match update {
|
||||
|
|
@ -141,6 +145,13 @@ async fn handle_session_channel(
|
|||
let success = ChannelOperationKind::Success;
|
||||
match req {
|
||||
ChannelRequest::PtyReq { want_reply, .. } => {
|
||||
let mut new_readline = readline::InteractiveShell::new();
|
||||
let to_write = new_readline.bytes_to_write();
|
||||
if !to_write.is_empty() {
|
||||
channel.send(ChannelOperationKind::Data(to_write)).await?;
|
||||
}
|
||||
readline = Some(new_readline);
|
||||
|
||||
if want_reply {
|
||||
channel.send(success).await?;
|
||||
}
|
||||
|
|
@ -176,13 +187,7 @@ async fn handle_session_channel(
|
|||
}
|
||||
ChannelUpdateKind::OpenFailed { .. } => todo!(),
|
||||
ChannelUpdateKind::Data { data } => {
|
||||
let is_eof = data.contains(&0x04 /*EOF, Ctrl-D*/);
|
||||
|
||||
// echo :3
|
||||
channel
|
||||
.send(ChannelOperationKind::Data(data.clone()))
|
||||
.await?;
|
||||
|
||||
// Store sent data
|
||||
let mut total_sent_data = total_sent_data.lock().await;
|
||||
// arbitrary limit
|
||||
if total_sent_data.len() < 50_000 {
|
||||
|
|
@ -195,11 +200,34 @@ async fn handle_session_channel(
|
|||
channel.send(ChannelOperationKind::Close).await?;
|
||||
}
|
||||
|
||||
if is_eof {
|
||||
debug!("Received Ctrl-D, closing channel");
|
||||
if let Some(readline) = &mut readline {
|
||||
readline.recv_bytes(&data);
|
||||
let to_write = readline.bytes_to_write();
|
||||
if !to_write.is_empty() {
|
||||
channel.send(ChannelOperationKind::Data(to_write)).await?;
|
||||
}
|
||||
|
||||
channel.send(ChannelOperationKind::Eof).await?;
|
||||
channel.send(ChannelOperationKind::Close).await?;
|
||||
if readline.should_exit() {
|
||||
debug!("Received Ctrl-D, closing channel");
|
||||
|
||||
channel.send(ChannelOperationKind::Eof).await?;
|
||||
channel.send(ChannelOperationKind::Close).await?;
|
||||
}
|
||||
} else {
|
||||
// bad fallback behavior
|
||||
let is_eof = data.contains(&0x04 /*EOF, Ctrl-D*/);
|
||||
|
||||
// echo :3
|
||||
channel
|
||||
.send(ChannelOperationKind::Data(data.clone()))
|
||||
.await?;
|
||||
|
||||
if is_eof {
|
||||
debug!("Received Ctrl-D, closing channel");
|
||||
|
||||
channel.send(ChannelOperationKind::Eof).await?;
|
||||
channel.send(ChannelOperationKind::Close).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ChannelUpdateKind::Open(_)
|
||||
|
|
@ -233,7 +261,7 @@ fn execute_command(command: &[u8]) -> ProcessOutput {
|
|||
stdout: b"what the hell".to_vec(),
|
||||
};
|
||||
};
|
||||
match command {
|
||||
match command.trim() {
|
||||
"uname -s -v -n -r -m" => ProcessOutput {
|
||||
status: 0,
|
||||
stdout: UNAME_SVNRM.to_vec(),
|
||||
|
|
|
|||
72
bin/cluelessh-faked/src/readline.rs
Normal file
72
bin/cluelessh-faked/src/readline.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
pub struct InteractiveShell {
|
||||
line_buf: Vec<u8>,
|
||||
out_buf: Vec<u8>,
|
||||
should_exit: bool,
|
||||
}
|
||||
|
||||
impl InteractiveShell {
|
||||
pub fn new() -> Self {
|
||||
let mut this = Self {
|
||||
line_buf: Vec::new(),
|
||||
out_buf: Vec::new(),
|
||||
should_exit: false,
|
||||
};
|
||||
this.prompt();
|
||||
this
|
||||
}
|
||||
|
||||
pub fn recv_bytes(&mut self, data: &[u8]) {
|
||||
// we're doing a little bit of tty-drivering
|
||||
for &byte in data {
|
||||
match byte {
|
||||
// EOF
|
||||
0x04 => {
|
||||
self.should_exit = true;
|
||||
return;
|
||||
}
|
||||
b'\r' => {
|
||||
let output = super::execute_command(&self.line_buf);
|
||||
self.line_buf.clear();
|
||||
self.write(b"\r\n");
|
||||
self.write(&output.stdout);
|
||||
self.prompt();
|
||||
}
|
||||
// ESC
|
||||
27 => {
|
||||
// We don't handle any of the fancy escape characters, so just drop it to avoid weird behavior.
|
||||
}
|
||||
// DEL
|
||||
127 => {
|
||||
// Backspace, space, backspace.
|
||||
// We literally erase it.
|
||||
if self.line_buf.len() > 0 {
|
||||
self.write(&[8, 32, 8]);
|
||||
self.line_buf.truncate(self.line_buf.len() - 1);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if self.line_buf.len() < 1_000 {
|
||||
self.line_buf.extend_from_slice(&[byte]);
|
||||
}
|
||||
self.write(&[byte]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt(&mut self) {
|
||||
self.write(b"# ");
|
||||
}
|
||||
|
||||
fn write(&mut self, data: &[u8]) {
|
||||
self.out_buf.extend_from_slice(data);
|
||||
}
|
||||
|
||||
pub fn should_exit(&self) -> bool {
|
||||
self.should_exit
|
||||
}
|
||||
|
||||
pub fn bytes_to_write(&mut self) -> Vec<u8> {
|
||||
std::mem::take(&mut self.out_buf)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue