feat: added cd

added cd and fixed all the warnings in the source code
This commit is contained in:
Andrew Rioux 2023-09-08 23:26:10 -04:00
parent ae24c2e0ad
commit 726e6dff13
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
10 changed files with 139 additions and 42 deletions

View File

@ -5,10 +5,10 @@ use std::{
sync::Arc, sync::Arc,
}; };
use anyhow::{bail, Context}; use anyhow::bail;
use ed25519_dalek::{Keypair, Signature, Signer, Verifier}; use ed25519_dalek::{Signature, Signer, Verifier};
use sparse_05_common::messages::{ use sparse_05_common::messages::{
Capabilities, Command, OperatingSystem, Response, CONNECTED_MESSAGE, CONNECT_MESSAGE, Capabilities, Command, Response, CONNECTED_MESSAGE, CONNECT_MESSAGE,
}; };
use tokio::{fs, net::UdpSocket}; use tokio::{fs, net::UdpSocket};

View File

@ -1,6 +1,5 @@
use std::{ use std::{
io::{self, Read, Write}, io::{self, Read, Write},
os::fd::AsRawFd,
path::PathBuf, path::PathBuf,
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
@ -11,7 +10,6 @@ use std::{
use sparse_05_common::messages::{Capabilities, Command, Response}; use sparse_05_common::messages::{Capabilities, Command, Response};
use structopt::StructOpt; use structopt::StructOpt;
use tempfile::NamedTempFile;
use tokio::{ use tokio::{
io::{stderr, stdout, AsyncWriteExt}, io::{stderr, stdout, AsyncWriteExt},
runtime::Handle, runtime::Handle,
@ -46,7 +44,7 @@ pub enum SparseCommands {
}, },
} }
macro_rules! libc_try { /*macro_rules! libc_try {
($expr:expr) => { ($expr:expr) => {
if unsafe { $expr } == -1 { if unsafe { $expr } == -1 {
return Err(std::io::Error::last_os_error())?; return Err(std::io::Error::last_os_error())?;
@ -71,7 +69,7 @@ fn set_term_attrs<F: AsRawFd>(fd: &mut F, attrs: &libc::termios) -> anyhow::Resu
fn convert_termios_raw(attrs: &mut libc::termios) -> anyhow::Result<()> { fn convert_termios_raw(attrs: &mut libc::termios) -> anyhow::Result<()> {
unsafe { libc::cfmakeraw(attrs) }; unsafe { libc::cfmakeraw(attrs) };
Ok(()) Ok(())
} }*/
async fn run_command( async fn run_command(
stdin: &mut Receiver<Vec<u8>>, stdin: &mut Receiver<Vec<u8>>,
@ -159,11 +157,10 @@ pub(super) async fn shell(
println!("Source code available at https://github.com/r-a303931/sparse (feel free to give it a star!)"); println!("Source code available at https://github.com/r-a303931/sparse (feel free to give it a star!)");
println!("Type #help to view a list of sparse commands\n"); println!("Type #help to view a list of sparse commands\n");
let mut stdin = tokio::io::stdin();
let mut stdout = io::stdout(); let mut stdout = io::stdout();
let backup_term_attrs = get_term_attrs(&stdin)?; /* let backup_term_attrs = get_term_attrs(&stdin)?;
let mut raw_term_attrs = get_term_attrs(&stdin)?; let mut raw_term_attrs = get_term_attrs(&stdin)?;
convert_termios_raw(&mut raw_term_attrs)?; convert_termios_raw(&mut raw_term_attrs)?;*/
let mut cwd = "/".to_string(); let mut cwd = "/".to_string();
@ -195,7 +192,7 @@ pub(super) async fn shell(
} }
}); });
loop { 'outer: loop {
print!( print!(
"{}@{}:{} {} ", "{}@{}:{} {} ",
capabilities.userent.as_deref().unwrap_or("unknown user!"), capabilities.userent.as_deref().unwrap_or("unknown user!"),
@ -280,6 +277,23 @@ pub(super) async fn shell(
} }
pause.store(false, Ordering::Relaxed); pause.store(false, Ordering::Relaxed);
} }
(Ok(SparseCommands::Cd { folder }), _) => {
let Ok(_) = connection.send_command(Command::Cd(folder.clone())).await else {
continue;
};
let resp = loop {
let Ok(resp) = connection.get_response().await else {
continue 'outer;
};
if let Response::CdDone(res) = resp {
break res;
}
};
match resp {
Ok(rcwd) => cwd = rcwd.to_string_lossy().to_string(),
Err(e) => eprintln!("{e}"),
}
}
_ => { _ => {
if !input.to_string().trim().is_empty() && !pause.load(Ordering::Relaxed) { if !input.to_string().trim().is_empty() && !pause.load(Ordering::Relaxed) {
if let Err(e) = run_command( if let Err(e) = run_command(

View File

@ -1,5 +1,5 @@
use std::{ use std::{
net::{Ipv4Addr, SocketAddr, ToSocketAddrs}, net::{SocketAddr, ToSocketAddrs},
path::PathBuf, path::PathBuf,
}; };

View File

@ -69,7 +69,7 @@ pub mod messages {
SendStdout(Vec<u8>, u64), SendStdout(Vec<u8>, u64),
CommandDone(u64, i32), CommandDone(u64, i32),
CdDone, CdDone(Result<PathBuf, String>),
LsResults(Vec<DirEntry>), LsResults(Vec<DirEntry>),
OpenedTTY(u64), OpenedTTY(u64),
@ -100,6 +100,7 @@ pub mod messages {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct Capabilities { pub struct Capabilities {
pub cwd: PathBuf,
pub operating_system: OperatingSystem, pub operating_system: OperatingSystem,
pub docker_container: bool, pub docker_container: bool,
pub docker_breakout: bool, pub docker_breakout: bool,

View File

@ -1,7 +1,36 @@
#[cfg(target_os = "linux")]
use std::ffi::c_int; use std::ffi::c_int;
use std::path::PathBuf;
use sparse_05_common::messages::{Capabilities, OperatingSystem, TransportType}; use sparse_05_common::messages::{Capabilities, OperatingSystem, TransportType};
pub struct SrvCapabilities {
pub operating_system: OperatingSystem,
pub docker_container: bool,
pub docker_breakout: bool,
pub setuid: bool,
pub root: bool,
pub userent: Option<String>,
pub transport: TransportType,
pub hostname: Option<String>,
}
impl SrvCapabilities {
pub fn to_capabilities(&self) -> Capabilities {
Capabilities {
cwd: PathBuf::from("/"),
docker_breakout: self.docker_breakout,
docker_container: self.docker_container,
hostname: self.hostname.clone(),
operating_system: self.operating_system.clone(),
root: self.root,
setuid: self.setuid,
transport: self.transport,
userent: self.userent.clone(),
}
}
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
const CAP_SETUID: u32 = 1 << 7; const CAP_SETUID: u32 = 1 << 7;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -48,12 +77,12 @@ fn get_username(uid: u32) -> anyhow::Result<Option<String>> {
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn get_username(uid: u32) -> anyhow::Result<Option<String>> { fn get_username(_uid: u32) -> anyhow::Result<Option<String>> {
Ok(std::env::var("USERPROFILE").ok()) Ok(std::env::var("USERPROFILE").ok())
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn get_current_capabilities() -> anyhow::Result<Capabilities> { fn get_current_capabilities() -> anyhow::Result<SrvCapabilities> {
let mut header = cap_user_header_t { let mut header = cap_user_header_t {
version: 0x20080522, version: 0x20080522,
pid: 0, pid: 0,
@ -85,7 +114,7 @@ fn get_current_capabilities() -> anyhow::Result<Capabilities> {
.map(|s| s.trim().to_string()) .map(|s| s.trim().to_string())
.ok(); .ok();
Ok(Capabilities { Ok(SrvCapabilities {
operating_system: if cfg!(target_os = "linux") { operating_system: if cfg!(target_os = "linux") {
OperatingSystem::Linux OperatingSystem::Linux
} else { } else {
@ -102,10 +131,10 @@ fn get_current_capabilities() -> anyhow::Result<Capabilities> {
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn get_current_capabilities() -> anyhow::Result<Capabilities> { fn get_current_capabilities() -> anyhow::Result<SrvCapabilities> {
let userent = get_username(0)?; let userent = get_username(0)?;
Ok(Capabilities { Ok(SrvCapabilities {
operating_system: OperatingSystem::Windows, operating_system: OperatingSystem::Windows,
docker_container: false, docker_container: false,
docker_breakout: false, docker_breakout: false,
@ -117,6 +146,6 @@ fn get_current_capabilities() -> anyhow::Result<Capabilities> {
}) })
} }
pub fn get_capabilities() -> anyhow::Result<Capabilities> { pub fn get_capabilities() -> anyhow::Result<SrvCapabilities> {
get_current_capabilities() get_current_capabilities()
} }

View File

@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
net::{Ipv4Addr, UdpSocket}, net::Ipv4Addr,
path::PathBuf,
sync::{ sync::{
mpsc::{channel, Receiver, Sender}, mpsc::{channel, Receiver, Sender},
Arc, Mutex, Arc, Mutex,
@ -8,11 +9,13 @@ use std::{
thread, thread,
}; };
use anyhow::{anyhow, bail, Context}; use anyhow::{bail, Context};
use ed25519_dalek::{Keypair, Signature, Signer, Verifier}; use ed25519_dalek::{Keypair, Signature, Signer, Verifier};
use packets::EthernetPacket; use packets::EthernetPacket;
use sparse_05_common::messages::{Capabilities, Command, Response, CONNECTED_MESSAGE}; use sparse_05_common::messages::{Capabilities, Command, Response, CONNECTED_MESSAGE};
use crate::capabilities::SrvCapabilities;
#[derive(Clone)] #[derive(Clone)]
pub struct ConnectionHandle { pub struct ConnectionHandle {
packet_handler_sender: Sender<EthernetPacket>, packet_handler_sender: Sender<EthernetPacket>,
@ -90,7 +93,7 @@ impl ConnectionInformation {
} }
pub fn spawn_connection_handler( pub fn spawn_connection_handler(
capabilities: Arc<Capabilities>, capabilities: Arc<SrvCapabilities>,
sign_pubkey: Arc<ed25519_dalek::PublicKey>, sign_pubkey: Arc<ed25519_dalek::PublicKey>,
enc_pubkey: Arc<ecies_ed25519::PublicKey>, enc_pubkey: Arc<ecies_ed25519::PublicKey>,
packet_sender: Sender<EthernetPacket>, packet_sender: Sender<EthernetPacket>,
@ -153,6 +156,8 @@ pub fn spawn_connection_handler(
let (packet_handler_sender, packet_handler) = channel(); let (packet_handler_sender, packet_handler) = channel();
let capabilities = Arc::new(Mutex::new(capabilities.to_capabilities()));
thread::spawn(move || { thread::spawn(move || {
if let Err(e) = authenticate(capabilities, packet_handler, conninfo, close) { if let Err(e) = authenticate(capabilities, packet_handler, conninfo, close) {
eprintln!("connection thread died: {e:?}"); eprintln!("connection thread died: {e:?}");
@ -165,7 +170,7 @@ pub fn spawn_connection_handler(
} }
fn authenticate<F: Fn()>( fn authenticate<F: Fn()>(
capabilities: Arc<Capabilities>, capabilities: Arc<Mutex<Capabilities>>,
packet_handler: Receiver<EthernetPacket>, packet_handler: Receiver<EthernetPacket>,
conninfo: ConnectionInformation, conninfo: ConnectionInformation,
close: F, close: F,
@ -221,7 +226,7 @@ mod download_file;
mod upload_file; mod upload_file;
fn handle_full_connection<F>( fn handle_full_connection<F>(
capabilities: Arc<Capabilities>, capabilities: Arc<Mutex<Capabilities>>,
packet_handler: Receiver<EthernetPacket>, packet_handler: Receiver<EthernetPacket>,
conninfo: ConnectionInformation, conninfo: ConnectionInformation,
close: F, close: F,
@ -253,19 +258,29 @@ where
match data { match data {
Command::RunCommand(comm) => { Command::RunCommand(comm) => {
let Ok(cap_lock) = capabilities.lock() else {
continue;
};
let cwd = cap_lock.cwd.clone();
drop(cap_lock);
let commands_clone = commands.clone(); let commands_clone = commands.clone();
let Ok(mut lock) = commands.lock() else { let Ok(mut lock) = commands.lock() else {
continue; continue;
}; };
let handler = let handler = match command::spawn_command(
match command::spawn_command(&s, comm, conninfo.clone(), commands_clone) { &s,
Ok(handler) => handler, comm,
Err(e) => { conninfo.clone(),
eprintln!("error spawning command: {e:?}"); commands_clone,
continue; cwd,
} ) {
}; Ok(handler) => handler,
Err(e) => {
eprintln!("error spawning command: {e:?}");
continue;
}
};
lock.insert(handler.id, handler); lock.insert(handler.id, handler);
} }
@ -279,7 +294,43 @@ where
} }
} }
Command::Cd(_) => {} Command::Cd(path) => {
let Ok(mut lock) = capabilities.lock() else {
let Ok(pkt) = conninfo.encrypt_and_sign_resp(Response::CdDone(Err(
"could not get cwd lock".to_string(),
))) else {
continue;
};
let _ = conninfo.send(pkt);
continue;
};
let mut path_base = lock.cwd.clone();
let path = PathBuf::from(path);
path_base.extend(&path);
let path_to_use = match std::fs::canonicalize(&path) {
Ok(p) => p,
Err(_) => path_base,
};
if let Err(e) = std::fs::metadata(&path_to_use) {
let Ok(pkt) = conninfo.encrypt_and_sign_resp(Response::CdDone(Err(
format!("could not cd into directory ({e:?})"),
))) else {
continue;
};
let _ = conninfo.send(pkt);
continue;
};
lock.cwd = path_to_use.clone();
let Ok(pkt) = conninfo.encrypt_and_sign_resp(Response::CdDone(Ok(path_to_use)))
else {
continue;
};
let _ = conninfo.send(pkt);
}
Command::Ls(_) => {} Command::Ls(_) => {}
Command::OpenTTY => {} Command::OpenTTY => {}

View File

@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{Read, Write}, io::{Read, Write},
path::PathBuf,
process::{Command, Stdio}, process::{Command, Stdio},
sync::{ sync::{
atomic::{AtomicBool, AtomicU64, Ordering}, atomic::{AtomicBool, AtomicU64, Ordering},
@ -28,6 +29,7 @@ pub(super) fn spawn_command<'a, 'b: 'a>(
command: String, command: String,
conninfo: ConnectionInformation, conninfo: ConnectionInformation,
command_map: Arc<Mutex<HashMap<u64, CommandHandler>>>, command_map: Arc<Mutex<HashMap<u64, CommandHandler>>>,
cwd: PathBuf,
) -> anyhow::Result<CommandHandler> { ) -> anyhow::Result<CommandHandler> {
let (data_sender, data_receiver) = channel(); let (data_sender, data_receiver) = channel();
@ -57,6 +59,7 @@ pub(super) fn spawn_command<'a, 'b: 'a>(
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.current_dir(cwd)
.spawn()?; .spawn()?;
let resp = Response::AckRunCommand(id); let resp = Response::AckRunCommand(id);

View File

@ -1,6 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fs::{self, OpenOptions}, fs::OpenOptions,
io::Read, io::Read,
path::PathBuf, path::PathBuf,
sync::{ sync::{
@ -9,7 +9,6 @@ use std::{
Arc, Mutex, Arc, Mutex,
}, },
thread::Scope, thread::Scope,
time::Duration,
}; };
use sparse_05_common::messages::{Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE}; use sparse_05_common::messages::{Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE};

View File

@ -1,6 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fs::{self, OpenOptions}, fs::OpenOptions,
io::Write, io::Write,
path::PathBuf, path::PathBuf,
sync::{ sync::{

View File

@ -1,9 +1,8 @@
use std::{ use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
net::{Ipv4Addr, SocketAddrV4, UdpSocket}, #[cfg(target_os = "linux")]
sync::Arc, use std::{sync::Arc, thread};
thread,
};
#[cfg(target_os = "linux")]
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
use packets::{self, EthernetPkt}; use packets::{self, EthernetPkt};
@ -80,7 +79,7 @@ impl Interface {
pub fn split(self) -> anyhow::Result<(InterfaceSender, InterfaceReceiver)> { pub fn split(self) -> anyhow::Result<(InterfaceSender, InterfaceReceiver)> {
match self { match self {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
Self::RawUdp(interface, port) => { Self::RawUdp(interface, _) => {
let arc = Arc::new(interface); let arc = Arc::new(interface);
Ok(( Ok((
InterfaceSender::RawUdp(Arc::clone(&arc)), InterfaceSender::RawUdp(Arc::clone(&arc)),
@ -173,6 +172,7 @@ impl InterfaceReceiver {
}, },
}; };
#[cfg(target_os = "linux")]
Ok(()) Ok(())
} }
} }