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:
Benoît Cortier 2023-05-09 17:00:07 -04:00 committed by GitHub
parent a2c6a5c124
commit 55d11a5000
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
296 changed files with 577 additions and 450 deletions

View 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

View file

@ -0,0 +1,3 @@
# IronRDP Async
`Future`s built on top of `ironrdp-connector` and `ironrdp-session` crates.

View 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)
}

View 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
}
}

View 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::*;

View file

@ -0,0 +1 @@
// TODO: active session async helpers