Skip to content

Commit

Permalink
feat(initial): initial now-proto-pdu code from IronRDP (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
pacmancoder authored Dec 13, 2024
1 parent f3e20be commit b24add8
Show file tree
Hide file tree
Showing 34 changed files with 5,358 additions and 11 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/now-proto-pdu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ test = false

[lints]
workspace = true

[dependencies]
bitflags = "2"
ironrdp-core = { version = "0.1.1", features = ["alloc"], git = "https://github.com/Devolutions/IronRDP.git" }

[features]
default = []
174 changes: 174 additions & 0 deletions crates/now-proto-pdu/src/core/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//! Buffer types for NOW protocol.
use alloc::vec::Vec;

use ironrdp_core::{
cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult,
ReadCursor, WriteCursor,
};

use crate::VarU32;

/// String value up to 2^32 bytes long.
///
/// NOW-PROTO: NOW_LRGBUF
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NowLrgBuf(Vec<u8>);

impl NowLrgBuf {
const NAME: &'static str = "NOW_LRGBUF";
const FIXED_PART_SIZE: usize = 4;

/// Create a new `NowLrgBuf` instance. Returns an error if the provided value is too large.
pub fn new(value: impl Into<Vec<u8>>) -> DecodeResult<Self> {
let value: Vec<u8> = value.into();

if value.len() > VarU32::MAX as usize {
return Err(invalid_field_err!("data", "data is too large for NOW_LRGBUF"));
}

Self::ensure_message_size(value.len())?;

Ok(NowLrgBuf(value))
}

/// Get the buffer value.
pub fn value(&self) -> &[u8] {
self.0.as_slice()
}

fn ensure_message_size(buffer_size: usize) -> DecodeResult<()> {
if buffer_size > usize::try_from(VarU32::MAX).expect("BUG: too small usize") {
return Err(invalid_field_err!("data", "data is too large for NOW_LRGBUF"));
}

if buffer_size > usize::MAX - Self::FIXED_PART_SIZE {
return Err(invalid_field_err!(
"data",
"data size is too large to fit in 32-bit usize"
));
}

Ok(())
}
}

impl Encode for NowLrgBuf {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
let encoded_size = self.size();
ensure_size!(in: dst, size: encoded_size);

let len: u32 = self.0.len().try_into().expect("BUG: validated in constructor");

dst.write_u32(len);
dst.write_slice(self.0.as_slice());

Ok(())
}

fn name(&self) -> &'static str {
Self::NAME
}

fn size(&self) -> usize {
// <u32 size> + <data bytes>
Self::FIXED_PART_SIZE
.checked_add(self.0.len())
.expect("BUG: size overflow")
}
}

impl Decode<'_> for NowLrgBuf {
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);

let len: usize = cast_length!("len", src.read_u32())?;

Self::ensure_message_size(len)?;

ensure_size!(in: src, size: len);
let bytes = src.read_slice(len);

Ok(NowLrgBuf(bytes.to_vec()))
}
}

impl From<NowLrgBuf> for Vec<u8> {
fn from(buf: NowLrgBuf) -> Self {
buf.0
}
}

/// Buffer up to 2^31 bytes long (Length has compact variable length encoding).
///
/// NOW-PROTO: NOW_VARBUF
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NowVarBuf(Vec<u8>);

impl NowVarBuf {
const NAME: &'static str = "NOW_VARBUF";

/// Create a new `NowVarBuf` instance. Returns an error if the provided value is too large.
pub fn new(value: impl Into<Vec<u8>>) -> DecodeResult<Self> {
let value = value.into();

let _: u32 = value
.len()
.try_into()
.ok()
.and_then(|val| if val <= VarU32::MAX { Some(val) } else { None })
.ok_or_else(|| invalid_field_err!("data", "too large buffer"))?;

Ok(NowVarBuf(value))
}

/// Get the buffer value.
pub fn value(&self) -> &[u8] {
self.0.as_slice()
}
}

impl Encode for NowVarBuf {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
let encoded_size = self.size();
ensure_size!(in: dst, size: encoded_size);

let len: u32 = self.0.len().try_into().expect("BUG: validated in constructor");

VarU32::new(len)?.encode(dst)?;
dst.write_slice(self.0.as_slice());

Ok(())
}

fn name(&self) -> &'static str {
Self::NAME
}

fn size(&self) -> usize {
// <variable-length size> + <data bytes>
// NOTE: Wrapping add will not overflow because the size is limited by VarU32::MAX
VarU32::new(self.0.len().try_into().expect("buffer size always fits into u32"))
.expect("buffer size is validated in constructor and should not overflow")
.size()
.wrapping_add(self.0.len())
}
}

impl Decode<'_> for NowVarBuf {
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let len_u32 = VarU32::decode(src)?.value();
let len: usize = cast_length!("len", len_u32)?;

ensure_size!(in: src, size: len);
let bytes = src.read_slice(len);

Ok(NowVarBuf(bytes.to_vec()))
}
}

impl From<NowVarBuf> for Vec<u8> {
fn from(buf: NowVarBuf) -> Self {
buf.0
}
}
70 changes: 70 additions & 0 deletions crates/now-proto-pdu/src/core/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use ironrdp_core::{ensure_fixed_part_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NowMessageClass(pub u8);

impl NowMessageClass {
/// NOW-PROTO: NOW_SYSTEM_MSG_CLASS_ID
pub const SYSTEM: Self = Self(0x11);

/// NOW-PROTO: NOW_SESSION_MSG_CLASS_ID
pub const SESSION: Self = Self(0x12);

/// NOW-PROTO: NOW_EXEC_MSG_CLASS_ID
pub const EXEC: Self = Self(0x13);
}

/// The NOW_HEADER structure is the header common to all NOW protocol messages.
///
/// NOW-PROTO: NOW_HEADER
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NowHeader {
pub size: u32,
pub class: NowMessageClass,
pub kind: u8,
pub flags: u16,
}

impl NowHeader {
const NAME: &'static str = "NOW_HEADER";
pub const FIXED_PART_SIZE: usize = 8;
}

impl Encode for NowHeader {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_fixed_part_size!(in: dst);

dst.write_u32(self.size);
dst.write_u8(self.class.0);
dst.write_u8(self.kind);
dst.write_u16(self.flags);

Ok(())
}

fn name(&self) -> &'static str {
Self::NAME
}

fn size(&self) -> usize {
Self::FIXED_PART_SIZE
}
}

impl Decode<'_> for NowHeader {
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);

let size = src.read_u32();
let class = NowMessageClass(src.read_u8());
let kind = src.read_u8();
let flags = src.read_u16();

Ok(NowHeader {
size,
class,
kind,
flags,
})
}
}
13 changes: 13 additions & 0 deletions crates/now-proto-pdu/src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! This module contains `NOW-PROTO` core types definitions.
mod buffer;
mod header;
mod number;
mod status;
mod string;

pub use buffer::{NowLrgBuf, NowVarBuf};
pub use header::{NowHeader, NowMessageClass};
pub use number::{VarI16, VarI32, VarI64, VarU16, VarU32, VarU64};
pub use status::{NowSeverity, NowStatus, NowStatusCode};
pub use string::{NowLrgStr, NowString128, NowString16, NowString256, NowString32, NowString64, NowVarStr};
Loading

0 comments on commit b24add8

Please sign in to comment.