diff --git a/async/src/server.rs b/async/src/server.rs new file mode 100644 index 0000000000000000000000000000000000000000..d9ed0ec75a358fce614910d3a88971b46c673ba0 --- /dev/null +++ b/async/src/server.rs @@ -0,0 +1,61 @@ +#[allow(unused_imports)] +use log::{debug, error, info, log, trace, warn}; + +use snafu::{prelude::*, Whatever}; + +use std::io::{Read, Write}; +use std::os::unix::io::{FromRawFd, RawFd}; +use tokio::io::unix::AsyncFd; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; + +use std::io::Error as IoError; +use std::io::ErrorKind; + +use core::pin::Pin; +use core::task::{Context, Poll}; + +use nix::fcntl::{fcntl, FcntlArg, OFlag}; + +use crate::async_channel::*; +use crate::async_door::*; +use crate::*; + +use door::config::*; +use door::sshnames::SSH_EXTENDED_DATA_STDERR; +use door::{ServBehaviour, Behaviour, Result, Runner}; +use door_sshproto as door; + +pub struct SSHServer<'a> { + door: AsyncDoor<'a>, +} + +impl<'a> SSHServer<'a> { + pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8], + b: &mut (dyn ServBehaviour + Send), + ) -> Result<Self> { + let runner = Runner::new_server(inbuf, outbuf, b)?; + let door = AsyncDoor::new(runner); + Ok(Self { door }) + } + + pub fn socket(&self) -> AsyncDoorSocket<'a> { + self.door.socket() + } + + pub async fn progress( + &mut self, + b: &mut (dyn ServBehaviour + Send), + ) -> Result<()> + { + let mut b = Behaviour::new_server(b); + self.door.progress(&mut b).await + } + + pub async fn channel(&mut self, ch: u32) -> Result<(ChanInOut<'a>, Option<ChanExtOut<'a>>)> { + let ty = self.door.with_runner(|r| r.channel_type(ch)).await?; + let inout = ChanInOut::new(ch, &self.door); + // TODO ext + let ext = None; + Ok((inout, ext)) + } +} diff --git a/sshproto/src/channel.rs b/sshproto/src/channel.rs index 8a37e626b87e15284ce48f544e2bedb9d141e7b6..1414b43b91e816ebd80edf79a259fb83a467ed9d 100644 --- a/sshproto/src/channel.rs +++ b/sshproto/src/channel.rs @@ -1,4 +1,4 @@ - #[allow(unused_imports)] +#[allow(unused_imports)] use { crate::error::{Error, Result, TrapBug}, log::{debug, error, info, log, trace, warn}, @@ -8,13 +8,16 @@ use core::mem; use heapless::{Deque, String, Vec}; -use crate::{*, sshwire::SSHEncodeEnum}; +use crate::{sshwire::SSHEncodeEnum, *}; use config::*; use conn::Dispatched; -use packets::{ChannelReqType, ChannelOpenFailure, ChannelRequest, Packet, ChannelOpen, ChannelOpenType, ChannelData, ChannelDataExt}; -use traffic::TrafSend; -use sshwire::{BinString, TextString}; +use packets::{ + ChannelData, ChannelDataExt, ChannelOpen, ChannelOpenFailure, ChannelOpenType, + ChannelReqType, ChannelRequest, Packet, +}; use sshnames::*; +use sshwire::{BinString, TextString}; +use traffic::TrafSend; /// The result of a channel open request. pub enum ChanOpened { @@ -43,7 +46,7 @@ impl From<Error> for DispatchOpenError { fn from(e: Error) -> Self { match e { Error::NoChannels => Self::Failure(ChanFail::SSH_OPEN_RESOURCE_SHORTAGE), - e => Self::Error(e) + e => Self::Error(e), } } } @@ -56,10 +59,7 @@ impl From<ChanFail> for DispatchOpenError { impl Channels { pub fn new() -> Self { - Channels { - ch: Default::default(), - pending_input: None, - } + Channels { ch: Default::default(), pending_input: None } } pub fn open<'b>( @@ -105,7 +105,8 @@ impl Channels { } pub fn get_mut(&mut self, num: u32) -> Result<&mut Channel> { - let ch = self.ch + let ch = self + .ch .get_mut(num as usize) // out of range .ok_or(Error::BadChannel)? @@ -129,7 +130,9 @@ impl Channels { /// Returns the first available channel fn unused_chan(&self) -> Result<u32> { - self.ch.iter().enumerate() + self.ch + .iter() + .enumerate() .find_map( |(i, ch)| if ch.as_ref().is_none() { Some(i as u32) } else { None }, ) @@ -144,7 +147,7 @@ impl Channels { num: co.num, max_packet: co.max_packet as usize, window: co.initial_window as usize, - }); + }); chan.state = ChanState::InOpen; let ch = &mut self.ch[num as usize]; @@ -154,11 +157,15 @@ impl Channels { /// Returns the channel data packet to send, and the length of data consumed. /// Caller has already checked valid length with send_allowed() - pub(crate) fn send_data<'b>(&mut self, num: u32, ext: Option<u32>, data: &'b [u8]) - -> Result<Packet<'b>> { + pub(crate) fn send_data<'b>( + &mut self, + num: u32, + ext: Option<u32>, + data: &'b [u8], + ) -> Result<Packet<'b>> { let send = self.get_mut(num)?.send.as_mut().trap()?; if data.len() > send.max_packet || data.len() > send.window { - return Err(Error::bug()) + return Err(Error::bug()); } send.window -= data.len(); @@ -196,11 +203,12 @@ impl Channels { self.get(num).map_or(Some(0), |c| c.send_allowed()) } - fn dispatch_open(&mut self, p: &ChannelOpen<'_>, + fn dispatch_open( + &mut self, + p: &ChannelOpen<'_>, s: &mut TrafSend, b: &mut Behaviour<'_>, - ) -> Result<()> { - + ) -> Result<()> { match self.dispatch_open_inner(p, s, b) { Err(DispatchOpenError::Failure(f)) => { s.send(packets::ChannelOpenFailure { @@ -210,18 +218,19 @@ impl Channels { lang: "", })?; Ok(()) - }, + } Err(DispatchOpenError::Error(e)) => Err(e), - Ok(()) => Ok(()) + Ok(()) => Ok(()), } } // the caller will send failure messages if required - fn dispatch_open_inner(&mut self, p: &ChannelOpen<'_>, + fn dispatch_open_inner( + &mut self, + p: &ChannelOpen<'_>, s: &mut TrafSend, b: &mut Behaviour<'_>, - ) -> Result<(), DispatchOpenError> { - + ) -> Result<(), DispatchOpenError> { if b.is_client() && matches!(p.ty, ChannelOpenType::Session) { // only server should receive session opens return Err(Error::SSHProtoError.into()); @@ -233,9 +242,7 @@ impl Channels { debug!("Rejecting unknown channel type '{u}'"); return Err(ChanFail::SSH_OPEN_UNKNOWN_CHANNEL_TYPE.into()); } - _ => { - self.reserve_chan(p)? - } + _ => self.reserve_chan(p)?, }; // beware that a reserved channel must be cleaned up on failure @@ -247,12 +254,8 @@ impl Channels { let mut bserv = b.server().unwrap(); bserv.open_session(ch.num()) } - ChannelOpenType::ForwardedTcpip(t) => { - b.open_tcp_forwarded(ch.num(), t) - } - ChannelOpenType::DirectTcpip(t) => { - b.open_tcp_direct(ch.num(), t) - } + ChannelOpenType::ForwardedTcpip(t) => b.open_tcp_forwarded(ch.num(), t), + ChannelOpenType::DirectTcpip(t) => b.open_tcp_direct(ch.num(), t), ChannelOpenType::Unknown(_) => { unreachable!() } @@ -261,11 +264,11 @@ impl Channels { match r { ChanOpened::Success => { s.send(ch.open_done()?)?; - }, + } ChanOpened::Failure(f) => { let n = ch.num(); self.remove(n)?; - return Err(f.into()) + return Err(f.into()); } ChanOpened::Defer => { // application will reply later @@ -275,11 +278,12 @@ impl Channels { Ok(()) } - pub fn dispatch_request(&mut self, + pub fn dispatch_request( + &mut self, p: &packets::ChannelRequest, s: &mut TrafSend, b: &mut Behaviour<'_>, - ) -> Result<()> { + ) -> Result<()> { if let Ok(ch) = self.get(p.num) { // only servers accept requests let success = if let Ok(b) = b.server() { @@ -346,7 +350,7 @@ impl Channels { let ch = self.get(p.num)?; if ch.send.is_some() { // TODO: or just warn? - return Err(Error::SSHProtoError) + return Err(Error::SSHProtoError); } else { self.remove(p.num)?; // TODO event @@ -360,20 +364,32 @@ impl Channels { self.get(p.num)?; // TODO check we are expecting input if self.pending_input.is_some() { - return Err(Error::bug()) + return Err(Error::bug()); } - self.pending_input = Some(PendInput { chan: p.num, len: p.data.0.len() }); - let di = DataIn { num: p.num, ext: None, offset: p.data_offset(), len: p.data.0.len() }; + self.pending_input = + Some(PendInput { chan: p.num, len: p.data.0.len() }); + let di = DataIn { + num: p.num, + ext: None, + offset: p.data_offset(), + len: p.data.0.len(), + }; disp = Dispatched(Some(di)); } Packet::ChannelDataExt(p) => { self.get(p.num)?; // TODO check we are expecting input and ext is valid. if self.pending_input.is_some() { - return Err(Error::bug()) + return Err(Error::bug()); } - self.pending_input = Some(PendInput { chan: p.num, len: p.data.0.len() }); - let di = DataIn { num: p.num, ext: Some(p.code), offset: p.data_offset(), len: p.data.0.len() }; + self.pending_input = + Some(PendInput { chan: p.num, len: p.data.0.len() }); + let di = DataIn { + num: p.num, + ext: Some(p.code), + offset: p.data_offset(), + len: p.data.0.len(), + }; trace!("{di:?}"); } Packet::ChannelEof(p) => { @@ -392,7 +408,7 @@ impl Channels { Packet::ChannelFailure(_p) => { todo!(); } - _ => Error::bug_msg("unreachable")? + _ => Error::bug_msg("unreachable")?, }; Ok(disp) } @@ -406,7 +422,6 @@ impl Channels { s: &mut TrafSend<'_, '_>, b: &mut Behaviour<'_>, ) -> Result<Dispatched> { - let r = self.dispatch_inner(packet, s, b).await; match r { @@ -457,6 +472,21 @@ pub struct Pty { pub modes: Vec<ModePair, { termmodes::NUM_MODES }>, } +impl TryFrom<&packets::Pty<'_>> for Pty { + type Error = Error; + fn try_from(p: &packets::Pty) -> Result<Self, Self::Error> { + warn!("TODO implement pty modes"); + let term = p.term.as_ascii()?.try_into().map_err(|e| Error::BadString)?; + Ok(Pty { + term, + cols: p.cols, + rows: p.rows, + width: p.width, + height: p.height, + modes: Vec::new(), + }) + } +} pub(crate) type ExecString = heapless::String<MAX_EXEC>; /// Like a `packets::ChannelReqType` but with storage. @@ -494,6 +524,7 @@ impl Req { let ty = match &self.details { ReqDetails::Shell => ChannelReqType::Shell, ReqDetails::Pty(pty) => { + warn!("TODO implement pty modes"); ChannelReqType::Pty(packets::Pty { term: TextString(pty.term.as_bytes()), cols: pty.cols, @@ -548,7 +579,9 @@ enum ChanState { /// is received // TODO: this is wasting half a kB. where else could we store it? could // the Behaviour own it? Or we don't store them here, just callback to the Behaviour. - Opening { init_req: InitReqs }, + Opening { + init_req: InitReqs, + }, Normal, RecvEof, @@ -622,56 +655,52 @@ impl Channel { sender_num: self.send.as_ref().unwrap().num, initial_window: self.recv.window as u32, max_packet: self.recv.max_packet as u32, - }.into(); + } + .into(); Ok(p) } - fn dispatch_server_request(&self, + fn dispatch_server_request( + &self, p: &packets::ChannelRequest, s: &mut TrafSend, b: &mut dyn ServBehaviour, - ) -> Result<bool> { - + ) -> Result<bool> { if !matches!(self.ty, ChanType::Session) { - return Ok(false) + return Ok(false); } match &p.req { - ChannelReqType::Shell => { - Ok(b.sess_shell(self.num())) + ChannelReqType::Shell => Ok(b.sess_shell(self.num())), + ChannelReqType::Exec(ex) => Ok(b.sess_exec(self.num(), ex.command)), + ChannelReqType::Pty(pty) => { + let cpty = pty.try_into()?; + Ok(b.sess_pty(self.num(), &cpty)) } - ChannelReqType::Exec(ex) => { - Ok(b.sess_exec(self.num(), ex.command)) - } - // TODO need to convert packet to channel Pty - // ChannelReqType::Pty(pty) => { - // let cpty = pty.into(); - // Ok(b.sess_pty(self.num(), &cpty)) - // } _ => { if let ChannelReqType::Unknown(u) = &p.req { warn!("Unknown channel req type \"{}\"", u) } else { // OK unwrap: tested for Unknown - warn!("Unhandled channel req \"{}\"", p.req.variant_name().unwrap()) + warn!( + "Unhandled channel req \"{}\"", + p.req.variant_name().unwrap() + ) }; Ok(false) } } } - fn finished_input(&mut self, len: usize ) { + fn finished_input(&mut self, len: usize) { self.pending_adjust = self.pending_adjust.saturating_add(len) } fn have_recv_eof(&self) -> bool { match self.state { - |ChanState::RecvEof - |ChanState::RecvClose - => true, + ChanState::RecvEof | ChanState::RecvClose => true, _ => false, } - } // None on close @@ -723,12 +752,10 @@ pub enum ChanEvent<'a> { // TODO details // OpenRequest { }, - ReqPty { num: u32, want_reply: bool, pty: packets::Pty<'a> }, Req { num: u32, req: ChannelReqType<'a> }, // TODO closein/closeout/eof, etc. Should also return the exit status etc - Eof { num: u32 }, Close { num: u32 }, @@ -743,16 +770,21 @@ pub(crate) enum ChanEventMaker { /// by returning the offset into the payload buffer, used by `traffic`. DataIn(DataIn), - OpenSuccess { num: u32 }, + OpenSuccess { + num: u32, + }, // A ChannelRequest. Will be split into separate ChanEvent variants // for each type. Req, // TODO closein/closeout/eof, etc. Should also return the exit status etc + Eof { + num: u32, + }, - Eof { num: u32 }, - - Close { num: u32 }, + Close { + num: u32, + }, // TODO: responses to a previous ChanMsg? } @@ -767,9 +799,16 @@ impl ChanEventMaker { } Self::OpenSuccess { num } => Some(ChanEvent::OpenSuccess { num: *num }), Self::Req => { - if let Packet::ChannelRequest(ChannelRequest { num, want_reply, req }) = packet { + if let Packet::ChannelRequest(ChannelRequest { + num, + want_reply, + req, + }) = packet + { match req { - ChannelReqType::Pty(pty) => Some(ChanEvent::ReqPty { num, want_reply, pty }), + ChannelReqType::Pty(pty) => { + Some(ChanEvent::ReqPty { num, want_reply, pty }) + } _ => { warn!("Unhandled {:?}", self); None @@ -784,7 +823,6 @@ impl ChanEventMaker { Self::Eof { num } => Some(ChanEvent::Eof { num: *num }), Self::Close { num } => Some(ChanEvent::Close { num: *num }), } - } } diff --git a/sshproto/src/sshwire.rs b/sshproto/src/sshwire.rs index 8f80128a3fc8b1174f6983506c30a89d9ccb1adf..aef4f1cef5d071a7ba4c3d6ee400c556d887af83 100644 --- a/sshproto/src/sshwire.rs +++ b/sshproto/src/sshwire.rs @@ -107,7 +107,7 @@ pub fn packet_from_bytes<'a>(b: &'a [u8], ctx: &ParseContext) -> Result<Packet<' if s.pos() != b.len() && !s.ctx().seen_unknown { // No length check if the packet had an unknown variant - // - we skipped parsing the rest of the packet. + // - it skipped parsing the remainder of the packet. Err(Error::WrongPacketLength) } else { Ok(p)