mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-08-04 15:18:17 +00:00
refactor: clarify project architecture (#123)
> Make the root of the workspace a virtual manifest. It might > be tempting to put the main crate into the root, but that > pollutes the root with src/, requires passing --workspace to > every Cargo command, and adds an exception to an otherwise > consistent structure. > Don’t succumb to the temptation to strip common prefix > from folder names. If each crate is named exactly as the > folder it lives in, navigation and renames become easier. > Cargo.tomls of reverse dependencies mention both the folder > and the crate name, it’s useful when they are exactly the > same. Source: https://matklad.github.io/2021/08/22/large-rust-workspaces.html#Smaller-Tips
This commit is contained in:
parent
a2c6a5c124
commit
55d11a5000
296 changed files with 577 additions and 450 deletions
20
crates/ironrdp-async/Cargo.toml
Normal file
20
crates/ironrdp-async/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "ironrdp-async"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
description = "Provides `Future`s wrapping the IronRDP state machines conveniently"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bytes = "1"
|
||||
ironrdp-connector.workspace = true
|
||||
ironrdp-pdu.workspace = true
|
||||
# ironrdp-session.workspace = true
|
||||
tap = "1"
|
||||
tracing.workspace = true
|
3
crates/ironrdp-async/README.md
Normal file
3
crates/ironrdp-async/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# IronRDP Async
|
||||
|
||||
`Future`s built on top of `ironrdp-connector` and `ironrdp-session` crates.
|
114
crates/ironrdp-async/src/connector.rs
Normal file
114
crates/ironrdp-async/src/connector.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use ironrdp_connector::{ClientConnector, ClientConnectorState, ConnectionResult, Sequence as _, State as _};
|
||||
|
||||
use crate::framed::{Framed, FramedRead, FramedWrite};
|
||||
|
||||
pub struct ShouldUpgrade {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn connect_begin<S>(
|
||||
framed: &mut Framed<S>,
|
||||
connector: &mut ClientConnector,
|
||||
) -> ironrdp_connector::Result<ShouldUpgrade>
|
||||
where
|
||||
S: Sync + FramedRead + FramedWrite,
|
||||
{
|
||||
let mut buf = Vec::new();
|
||||
|
||||
info!("Begin connection procedure");
|
||||
|
||||
while !connector.should_perform_security_upgrade() {
|
||||
single_connect_step(framed, connector, &mut buf).await?;
|
||||
}
|
||||
|
||||
Ok(ShouldUpgrade { _priv: () })
|
||||
}
|
||||
|
||||
pub fn skip_connect_begin(connector: &mut ClientConnector) -> ShouldUpgrade {
|
||||
assert!(connector.should_perform_security_upgrade());
|
||||
ShouldUpgrade { _priv: () }
|
||||
}
|
||||
|
||||
pub struct Upgraded {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector, server_public_key: Vec<u8>) -> Upgraded {
|
||||
trace!("marked as upgraded");
|
||||
connector.attach_server_public_key(server_public_key);
|
||||
connector.mark_security_upgrade_as_done();
|
||||
Upgraded { _priv: () }
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn connect_finalize<S>(
|
||||
_: Upgraded,
|
||||
framed: &mut Framed<S>,
|
||||
mut connector: ClientConnector,
|
||||
) -> ironrdp_connector::Result<ConnectionResult>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
{
|
||||
let mut buf = Vec::new();
|
||||
|
||||
debug!("CredSSP procedure");
|
||||
|
||||
while connector.is_credssp_step() {
|
||||
single_connect_step(framed, &mut connector, &mut buf).await?;
|
||||
}
|
||||
|
||||
debug!("Remaining of connection sequence");
|
||||
|
||||
let result = loop {
|
||||
single_connect_step(framed, &mut connector, &mut buf).await?;
|
||||
|
||||
if let ClientConnectorState::Connected { result } = connector.state {
|
||||
break result;
|
||||
}
|
||||
};
|
||||
|
||||
info!("Connected with success");
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn single_connect_step<S>(
|
||||
framed: &mut Framed<S>,
|
||||
connector: &mut ClientConnector,
|
||||
buf: &mut Vec<u8>,
|
||||
) -> ironrdp_connector::Result<ironrdp_connector::Written>
|
||||
where
|
||||
S: FramedWrite + FramedRead,
|
||||
{
|
||||
let written = if let Some(next_pdu_hint) = connector.next_pdu_hint() {
|
||||
debug!(
|
||||
connector.state = connector.state.name(),
|
||||
hint = ?next_pdu_hint,
|
||||
"Wait for PDU"
|
||||
);
|
||||
|
||||
let pdu = framed
|
||||
.read_by_hint(next_pdu_hint)
|
||||
.await
|
||||
.map_err(|e| ironrdp_connector::Error::new("read frame by hint").with_custom(e))?;
|
||||
|
||||
trace!(length = pdu.len(), "PDU received");
|
||||
|
||||
connector.step(&pdu, buf)?
|
||||
} else {
|
||||
connector.step_no_input(buf)?
|
||||
};
|
||||
|
||||
if let Some(response_len) = written.size() {
|
||||
let response = &buf[..response_len];
|
||||
trace!(response_len, "Send response");
|
||||
framed
|
||||
.write_all(response)
|
||||
.await
|
||||
.map_err(|e| ironrdp_connector::Error::new("write all").with_custom(e))?;
|
||||
}
|
||||
|
||||
Ok(written)
|
||||
}
|
157
crates/ironrdp-async/src/framed.rs
Normal file
157
crates/ironrdp-async/src/framed.rs
Normal file
|
@ -0,0 +1,157 @@
|
|||
use std::io;
|
||||
use std::pin::Pin;
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use ironrdp_pdu::PduHint;
|
||||
|
||||
// TODO: use static async fn / return position impl trait in traits when stabiziled (https://github.com/rust-lang/rust/issues/91611)
|
||||
|
||||
pub trait FramedRead {
|
||||
/// Reads from stream and fills internal buffer
|
||||
fn read<'a>(
|
||||
&'a mut self,
|
||||
buf: &'a mut BytesMut,
|
||||
) -> Pin<Box<dyn std::future::Future<Output = io::Result<usize>> + 'a>>
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
pub trait FramedWrite {
|
||||
/// Writes an entire buffer into this stream.
|
||||
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> Pin<Box<dyn std::future::Future<Output = io::Result<()>> + 'a>>
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
pub trait StreamWrapper: Sized {
|
||||
type InnerStream;
|
||||
|
||||
fn from_inner(stream: Self::InnerStream) -> Self;
|
||||
|
||||
fn into_inner(self) -> Self::InnerStream;
|
||||
|
||||
fn get_inner(&self) -> &Self::InnerStream;
|
||||
|
||||
fn get_inner_mut(&mut self) -> &mut Self::InnerStream;
|
||||
}
|
||||
|
||||
pub struct Framed<S> {
|
||||
stream: S,
|
||||
buf: BytesMut,
|
||||
}
|
||||
|
||||
impl<S> Framed<S> {
|
||||
pub fn peek(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Framed<S>
|
||||
where
|
||||
S: StreamWrapper,
|
||||
{
|
||||
pub fn new(stream: S::InnerStream) -> Self {
|
||||
Self {
|
||||
stream: S::from_inner(stream),
|
||||
buf: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> (S::InnerStream, BytesMut) {
|
||||
(self.stream.into_inner(), self.buf)
|
||||
}
|
||||
|
||||
pub fn into_inner_no_leftover(self) -> S::InnerStream {
|
||||
let (stream, leftover) = self.into_inner();
|
||||
debug_assert_eq!(leftover.len(), 0, "unexpected leftover");
|
||||
stream
|
||||
}
|
||||
|
||||
pub fn get_inner(&self) -> (&S::InnerStream, &BytesMut) {
|
||||
(self.stream.get_inner(), &self.buf)
|
||||
}
|
||||
|
||||
pub fn get_inner_mut(&mut self) -> (&mut S::InnerStream, &mut BytesMut) {
|
||||
(self.stream.get_inner_mut(), &mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Framed<S>
|
||||
where
|
||||
S: FramedRead,
|
||||
{
|
||||
/// Reads from stream and fills internal buffer
|
||||
pub async fn read(&mut self) -> io::Result<usize> {
|
||||
self.stream.read(&mut self.buf).await
|
||||
}
|
||||
|
||||
pub async fn read_exact(&mut self, length: usize) -> io::Result<Bytes> {
|
||||
loop {
|
||||
if self.buf.len() >= length {
|
||||
return Ok(self.buf.split_to(length).freeze());
|
||||
} else {
|
||||
self.buf.reserve(length - self.buf.len());
|
||||
}
|
||||
|
||||
let len = self.read().await?;
|
||||
|
||||
// Handle EOF
|
||||
if len == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "not enough bytes"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_pdu(&mut self) -> io::Result<(ironrdp_pdu::Action, Bytes)> {
|
||||
loop {
|
||||
// Try decoding and see if a frame has been received already
|
||||
match ironrdp_pdu::find_size(self.peek()) {
|
||||
Ok(Some(pdu_info)) => {
|
||||
let frame = self.read_exact(pdu_info.length).await?;
|
||||
|
||||
return Ok((pdu_info.action, frame));
|
||||
}
|
||||
Ok(None) => {
|
||||
let len = self.read().await?;
|
||||
|
||||
// Handle EOF
|
||||
if len == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "not enough bytes"));
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_by_hint(&mut self, hint: &dyn PduHint) -> io::Result<Bytes> {
|
||||
loop {
|
||||
match hint
|
||||
.find_size(self.peek())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?
|
||||
{
|
||||
Some(length) => {
|
||||
return self.read_exact(length).await;
|
||||
}
|
||||
None => {
|
||||
let len = self.read().await?;
|
||||
|
||||
// Handle EOF
|
||||
if len == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "not enough bytes"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Framed<S>
|
||||
where
|
||||
S: FramedWrite,
|
||||
{
|
||||
/// Reads from stream and fills internal buffer
|
||||
pub async fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.stream.write_all(buf).await
|
||||
}
|
||||
}
|
10
crates/ironrdp-async/src/lib.rs
Normal file
10
crates/ironrdp-async/src/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
mod connector;
|
||||
mod framed;
|
||||
mod session;
|
||||
|
||||
pub use connector::*;
|
||||
pub use framed::*;
|
||||
pub use session::*;
|
1
crates/ironrdp-async/src/session.rs
Normal file
1
crates/ironrdp-async/src/session.rs
Normal file
|
@ -0,0 +1 @@
|
|||
// TODO: active session async helpers
|
Loading…
Add table
Add a link
Reference in a new issue