1
0
Fork 0
mirror of https://gitlab.redox-os.org/CoffeeCode/redox-ssh.git synced 2025-12-28 17:02:19 +01:00

Refactor: Split pty and shell out of channel

This commit is contained in:
Laurenz 2024-09-28 20:48:50 +02:00
parent b30a46e568
commit a1ff624f2a
Signed by: C0ffeeCode
SSH key fingerprint: SHA256:jnEltBNftC3wUZESLSMvM9zVPOkkevGRzqqoW2k2ORI
4 changed files with 153 additions and 111 deletions

View file

@ -1,12 +1,14 @@
use std::fs::{File, OpenOptions}; mod pty;
use std::io::{self, Read, Write}; mod shell;
use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{self, Stdio};
use std::thread::{self, JoinHandle};
use crate::sys; use std::fs::File;
use std::io::{self, Write};
use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::process;
use std::thread::JoinHandle;
pub use pty::PtyConfig;
pub type ChannelId = u32; pub type ChannelId = u32;
@ -25,23 +27,18 @@ pub struct Channel {
#[derive(Debug)] #[derive(Debug)]
pub enum ChannelRequest { pub enum ChannelRequest {
Pty { Pty(PtyConfig),
term: String,
chars: u16,
rows: u16,
pixel_width: u16,
pixel_height: u16,
modes: Vec<u8>,
},
Shell, Shell,
} }
impl Channel { impl Channel {
pub fn new( pub fn new(
id: ChannelId, peer_id: ChannelId, peer_window_size: u32, id: ChannelId,
max_packet_size: u32 peer_id: ChannelId,
) -> Channel { peer_window_size: u32,
Channel { max_packet_size: u32,
) -> Self {
Self {
id, id,
peer_id, peer_id,
process: None, process: None,
@ -66,99 +63,19 @@ impl Channel {
self.max_packet_size self.max_packet_size
} }
pub fn request(&mut self, request: ChannelRequest) { pub fn handle_request(&mut self, request: ChannelRequest) {
match request match request {
{ ChannelRequest::Pty(ref pty) => self.setup_tty(pty),
ChannelRequest::Pty { ChannelRequest::Shell => self.setup_shell(),
chars,
rows,
pixel_width,
pixel_height,
..
} => {
let (master_fd, tty_path) = sys::getpty();
sys::set_winsize(
master_fd,
chars,
rows,
pixel_width,
pixel_height,
);
self.read_thread = Some(thread::spawn(move || {
#[cfg(target_os = "redox")]
let master2 = unsafe { syscall::dup(master_fd as usize, &[]).unwrap_or(!0) };
#[cfg(not(target_os = "redox"))]
let master2 = unsafe { libc::dup(master_fd) };
println!("dup result: {}", master2 as u32);
let mut master = unsafe { File::from_raw_fd(master2 as i32) };
loop {
use std::str::from_utf8_unchecked;
let mut buf = [0; 4096];
let count = master.read(&mut buf).unwrap();
// This is weird.
// An error is thrown&unwrapped here (panic)
// but yet it continues to function properly
if count == 0 {
break;
}
println!("Read: {}", unsafe {
from_utf8_unchecked(&buf[0..count])
});
}
println!("Quitting read thread.");
}));
self.pty = Some((master_fd, tty_path));
self.master = Some(unsafe { File::from_raw_fd(master_fd) });
}
ChannelRequest::Shell => {
if let Some((_, tty_path)) = self.pty.as_ref() {
let stdin = OpenOptions::new()
.read(true)
.write(true)
.open(tty_path)
.unwrap()
.into_raw_fd();
let stdout = OpenOptions::new()
.read(true)
.write(true)
.open(tty_path)
.unwrap()
.into_raw_fd();
let stderr = OpenOptions::new()
.read(true)
.write(true)
.open(tty_path)
.unwrap()
.into_raw_fd();
unsafe {
process::Command::new("login")
.stdin(Stdio::from_raw_fd(stdin))
.stdout(Stdio::from_raw_fd(stdout))
.stderr(Stdio::from_raw_fd(stderr))
.pre_exec(sys::before_exec)
}
.spawn()
.unwrap();
}
}
} }
debug!("Channel Request: {:?}", request); debug!("Channel Request: {:?}", request);
} }
pub fn data(&mut self, data: &[u8]) -> io::Result<()> { pub fn write_data(&mut self, data: &[u8]) -> io::Result<()> {
if let Some(ref mut master) = self.master { if let Some(ref mut master) = self.master {
master.write_all(data)?; master.write_all(data)?;
master.flush() master.flush()
} } else {
else {
Ok(()) Ok(())
} }
} }

76
src/channel/pty.rs Normal file
View file

@ -0,0 +1,76 @@
use std::{fs::File, io::Read, os::fd::FromRawFd, thread};
use crate::sys;
use super::Channel;
#[derive(Debug)]
pub struct PtyConfig {
pub term: String,
pub chars: u16,
pub rows: u16,
pub pixel_width: u16,
pub pixel_height: u16,
pub modes: Vec<u8>,
}
impl Channel {
/// Allocates and sets up new PTY.
/// TODO: consider what to do if there already is a PTY.
///
/// Also does incorrect error handling internally
/// on I/O errors and occurs if a process exits.
pub fn setup_tty(
&mut self,
PtyConfig {
term,
chars,
rows,
pixel_width,
pixel_height,
modes,
}: &PtyConfig,
) {
let (master_fd, tty_path) = sys::getpty();
sys::set_winsize(master_fd, *chars, *rows, *pixel_width, *pixel_height);
self.read_thread = Some(thread::spawn(move || {
#[cfg(target_os = "redox")]
let master2 =
unsafe { syscall::dup(master_fd as usize, &[]).unwrap_or(!0) };
#[cfg(not(target_os = "redox"))]
let master2 = unsafe { libc::dup(master_fd) };
println!("dup result: {}", master2 as u32);
let mut master = unsafe { File::from_raw_fd(master2 as i32) };
loop {
use std::str::from_utf8_unchecked;
let mut buf = [0; 4096];
let count = match master.read(&mut buf) {
Ok(o) => o,
Err(e) => {
warn!("Error occured, ignoring: {}", e);
1 // TODO
},
};
// This is weird.
// An error is thrown&unwrapped here (panic)
// but yet it continues to function properly
if count == 0 {
break;
}
println!("Read: {}", unsafe {
from_utf8_unchecked(&buf[0..count])
});
}
println!("Quitting read thread.");
}));
self.pty = Some((master_fd, tty_path));
self.master = Some(unsafe { File::from_raw_fd(master_fd) });
}
}

49
src/channel/shell.rs Normal file
View file

@ -0,0 +1,49 @@
use std::{
fs::OpenOptions,
os::{
fd::{FromRawFd, IntoRawFd},
unix::process::CommandExt,
},
process::{self, Stdio},
};
use crate::sys;
use super::Channel;
impl Channel {
pub fn setup_shell(&self) {
if let Some((_, tty_path)) = self.pty.as_ref() {
let stdin = OpenOptions::new()
.read(true)
.write(true)
.open(tty_path)
.unwrap()
.into_raw_fd();
let stdout = OpenOptions::new()
.read(true)
.write(true)
.open(tty_path)
.unwrap()
.into_raw_fd();
let stderr = OpenOptions::new()
.read(true)
.write(true)
.open(tty_path)
.unwrap()
.into_raw_fd();
unsafe {
process::Command::new("login")
.stdin(Stdio::from_raw_fd(stdin))
.stdout(Stdio::from_raw_fd(stdout))
.stderr(Stdio::from_raw_fd(stderr))
.pre_exec(sys::before_exec)
}
.spawn()
.unwrap();
}
}
}

View file

@ -4,7 +4,7 @@ use std::sync::Arc;
use rand::distributions::Standard; use rand::distributions::Standard;
use crate::channel::{Channel, ChannelId, ChannelRequest}; use crate::channel::{Channel, ChannelId, ChannelRequest, PtyConfig};
use crate::encryption::{AesCtr, Decryptor, Encryption}; use crate::encryption::{AesCtr, Decryptor, Encryption};
use crate::error::{ConnectionError, ConnectionResult as Result}; use crate::error::{ConnectionError, ConnectionResult as Result};
use crate::key_exchange::{KexResult, KeyExchange}; use crate::key_exchange::{KexResult, KeyExchange};
@ -315,21 +315,21 @@ impl Connection {
let want_reply = reader.read_bool()?; let want_reply = reader.read_bool()?;
let request = match &*name { let request = match &*name {
"pty-req" => Some(ChannelRequest::Pty { "pty-req" => Some(ChannelRequest::Pty(PtyConfig {
term: reader.read_utf8()?, term: reader.read_utf8()?,
chars: reader.read_uint32()? as u16, chars: reader.read_uint32()? as u16,
rows: reader.read_uint32()? as u16, rows: reader.read_uint32()? as u16,
pixel_width: reader.read_uint32()? as u16, pixel_width: reader.read_uint32()? as u16,
pixel_height: reader.read_uint32()? as u16, pixel_height: reader.read_uint32()? as u16,
modes: reader.read_string()?, modes: reader.read_string()?,
}), })),
"shell" => Some(ChannelRequest::Shell), "shell" => Some(ChannelRequest::Shell),
_ => None, _ => None,
}; };
if let Some(request) = request { if let Some(request) = request {
let channel = self.channels.get_mut(&channel_id).unwrap(); let channel = self.channels.get_mut(&channel_id).unwrap();
channel.request(request); channel.handle_request(request);
} else { } else {
warn!("Unkown channel request {}", name); warn!("Unkown channel request {}", name);
} }
@ -349,7 +349,7 @@ impl Connection {
let data = reader.read_string()?; let data = reader.read_string()?;
let channel = self.channels.get_mut(&channel_id).unwrap(); let channel = self.channels.get_mut(&channel_id).unwrap();
channel.data(data.as_slice())?; channel.write_data(data.as_slice())?;
Ok(None) Ok(None)
} }