From 27f5504508bcaedb0e0891399a22f896109397e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Cortier?= <3809077+CBenoit@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:01:49 -0400 Subject: [PATCH] refactor(mstsgu): follow up to PR 913 (#920) - Update tokio-tungstenite to latest - Fix the dependencies - Move the top-level documentation into a README.md that we re-include in the source code - Re-order the imports using the nightly formater - Audit the unwraps and remove them - Fix the UTF-16 string length computation --- ARCHITECTURE.md | 4 + Cargo.lock | 141 ++++++++-------------------- crates/ironrdp-client/src/config.rs | 2 +- crates/ironrdp-mstsgu/Cargo.toml | 33 +++---- crates/ironrdp-mstsgu/README.md | 11 +++ crates/ironrdp-mstsgu/src/lib.rs | 52 +++++----- crates/ironrdp-mstsgu/src/macros.rs | 7 ++ crates/ironrdp-mstsgu/src/proto.rs | 45 +++++---- 8 files changed, 132 insertions(+), 163 deletions(-) create mode 100644 crates/ironrdp-mstsgu/README.md create mode 100644 crates/ironrdp-mstsgu/src/macros.rs diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 4bfac02d..e28decaf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -256,6 +256,10 @@ State machines to drive an RDP connection acceptance sequence Extendable skeleton for implementing custom RDP servers. +#### [`crates/ironrdp-mstsgu`](./crates/ironrdp-mstsgu) (@steffengy) + +Terminal Services Gateway Server Protocol implementation. + #### [`crates/ironrdp-glutin-renderer`](./crates/ironrdp-glutin-renderer) (no maintainer) `glutin` primitives for OpenGL rendering. diff --git a/Cargo.lock b/Cargo.lock index 1acee881..c4face8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1510,7 +1510,7 @@ dependencies = [ "embed-resource", "ironrdp", "ironrdp-cliprdr-native", - "ironrdp-core 0.1.5", + "ironrdp-core", "sspi", "thiserror 2.0.14", "tracing", @@ -2332,7 +2332,7 @@ dependencies = [ "ironrdp-cliprdr", "ironrdp-cliprdr-native", "ironrdp-connector", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-dvc", "ironrdp-graphics", @@ -2359,7 +2359,7 @@ version = "0.6.0" dependencies = [ "ironrdp-async", "ironrdp-connector", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", "tracing", @@ -2370,7 +2370,7 @@ name = "ironrdp-ainput" version = "0.3.0" dependencies = [ "bitflags 2.9.1", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-dvc", "num-derive", "num-traits", @@ -2382,7 +2382,7 @@ version = "0.6.0" dependencies = [ "bytes", "ironrdp-connector", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "tracing", ] @@ -2403,7 +2403,7 @@ version = "0.6.0" dependencies = [ "bytes", "ironrdp-connector", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "tracing", ] @@ -2426,14 +2426,14 @@ dependencies = [ "ironrdp", "ironrdp-cfg", "ironrdp-cliprdr-native", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-dvc-pipe-proxy", "ironrdp-mstsgu", "ironrdp-propertyset", "ironrdp-rdcleanpath", "ironrdp-rdpfile", "ironrdp-rdpsnd-native", - "ironrdp-tls 0.1.3", + "ironrdp-tls", "ironrdp-tokio", "proc-exit", "raw-window-handle", @@ -2442,7 +2442,7 @@ dependencies = [ "softbuffer", "tap", "tokio", - "tokio-tungstenite 0.27.0", + "tokio-tungstenite", "tokio-util", "tracing", "tracing-subscriber", @@ -2460,7 +2460,7 @@ name = "ironrdp-cliprdr" version = "0.3.0" dependencies = [ "bitflags 2.9.1", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", "tracing", @@ -2470,7 +2470,7 @@ dependencies = [ name = "ironrdp-cliprdr-format" version = "0.1.3" dependencies = [ - "ironrdp-core 0.1.5", + "ironrdp-core", "png", ] @@ -2479,7 +2479,7 @@ name = "ironrdp-cliprdr-native" version = "0.3.0" dependencies = [ "ironrdp-cliprdr", - "ironrdp-core 0.1.5", + "ironrdp-core", "tracing", "windows 0.61.3", ] @@ -2489,8 +2489,8 @@ name = "ironrdp-connector" version = "0.6.0" dependencies = [ "arbitrary", - "ironrdp-core 0.1.5", - "ironrdp-error 0.1.3", + "ironrdp-core", + "ironrdp-error", "ironrdp-pdu", "ironrdp-svc", "picky", @@ -2506,23 +2506,14 @@ dependencies = [ name = "ironrdp-core" version = "0.1.5" dependencies = [ - "ironrdp-error 0.1.3", -] - -[[package]] -name = "ironrdp-core" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2db60a59716a84d09040d29c9e75e81545842510fccb0934c09b28e78b46680" -dependencies = [ - "ironrdp-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ironrdp-error", ] [[package]] name = "ironrdp-displaycontrol" version = "0.3.0" dependencies = [ - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-dvc", "ironrdp-pdu", "ironrdp-svc", @@ -2533,7 +2524,7 @@ dependencies = [ name = "ironrdp-dvc" version = "0.3.1" dependencies = [ - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", "slab", @@ -2545,7 +2536,7 @@ name = "ironrdp-dvc-pipe-proxy" version = "0.1.0" dependencies = [ "async-trait", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-dvc", "ironrdp-pdu", "ironrdp-svc", @@ -2557,12 +2548,6 @@ dependencies = [ name = "ironrdp-error" version = "0.1.3" -[[package]] -name = "ironrdp-error" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9d7794e854eef2f13fdf79c8502bcc567a75a15fd0522885f37739386a4cef" - [[package]] name = "ironrdp-futures" version = "0.4.0" @@ -2579,7 +2564,7 @@ dependencies = [ "arbitrary", "ironrdp-cliprdr", "ironrdp-cliprdr-format", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-graphics", "ironrdp-pdu", @@ -2599,7 +2584,7 @@ dependencies = [ "bytemuck", "byteorder", "expect-test", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "lazy_static", "num-derive", @@ -2626,12 +2611,12 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "ironrdp-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ironrdp-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ironrdp-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ironrdp-core", + "ironrdp-error", + "ironrdp-tls", "log", "tokio", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite", "tokio-util", "uuid", ] @@ -2645,8 +2630,8 @@ dependencies = [ "byteorder", "der-parser", "expect-test", - "ironrdp-core 0.1.5", - "ironrdp-error 0.1.3", + "ironrdp-core", + "ironrdp-error", "lazy_static", "md-5", "num-bigint", @@ -2683,8 +2668,8 @@ name = "ironrdp-rdpdr" version = "0.3.0" dependencies = [ "bitflags 2.9.1", - "ironrdp-core 0.1.5", - "ironrdp-error 0.1.3", + "ironrdp-core", + "ironrdp-error", "ironrdp-pdu", "ironrdp-svc", "tracing", @@ -2694,7 +2679,7 @@ dependencies = [ name = "ironrdp-rdpdr-native" version = "0.3.0" dependencies = [ - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "ironrdp-rdpdr", "ironrdp-svc", @@ -2714,7 +2699,7 @@ name = "ironrdp-rdpsnd" version = "0.5.0" dependencies = [ "bitflags 2.9.1", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", "tracing", @@ -2744,7 +2729,7 @@ dependencies = [ "ironrdp-ainput", "ironrdp-async", "ironrdp-cliprdr", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-dvc", "ironrdp-graphics", @@ -2768,10 +2753,10 @@ name = "ironrdp-session" version = "0.5.0" dependencies = [ "ironrdp-connector", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-dvc", - "ironrdp-error 0.1.3", + "ironrdp-error", "ironrdp-graphics", "ironrdp-pdu", "ironrdp-svc", @@ -2789,7 +2774,7 @@ name = "ironrdp-svc" version = "0.4.1" dependencies = [ "bitflags 2.9.1", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-pdu", ] @@ -2804,7 +2789,7 @@ dependencies = [ "ironrdp-cliprdr", "ironrdp-cliprdr-format", "ironrdp-connector", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-dvc", "ironrdp-fuzzing", @@ -2833,7 +2818,7 @@ dependencies = [ "async-trait", "ironrdp", "ironrdp-async", - "ironrdp-tls 0.1.3", + "ironrdp-tls", "ironrdp-tokio", "semver", "tokio", @@ -2851,18 +2836,6 @@ dependencies = [ "x509-cert", ] -[[package]] -name = "ironrdp-tls" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc807c143533f41e19bf323e8c7f78995953ce8427d37220dde53906cbd48a3" -dependencies = [ - "tokio", - "tokio-native-tls", - "tokio-rustls", - "x509-cert", -] - [[package]] name = "ironrdp-tokio" version = "0.6.0" @@ -2892,7 +2865,7 @@ dependencies = [ "iron-remote-desktop", "ironrdp", "ironrdp-cliprdr-format", - "ironrdp-core 0.1.5", + "ironrdp-core", "ironrdp-futures", "ironrdp-propertyset", "ironrdp-rdcleanpath", @@ -5523,24 +5496,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" -dependencies = [ - "futures-util", - "log", - "native-tls", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tungstenite 0.26.2", -] - [[package]] name = "tokio-tungstenite" version = "0.27.0" @@ -5556,7 +5511,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls", - "tungstenite 0.27.0", + "tungstenite", ] [[package]] @@ -5776,26 +5731,6 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -[[package]] -name = "tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "native-tls", - "rand 0.9.2", - "rustls", - "rustls-pki-types", - "sha1", - "thiserror 2.0.14", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.27.0" diff --git a/crates/ironrdp-client/src/config.rs b/crates/ironrdp-client/src/config.rs index 1fbf8323..24808df8 100644 --- a/crates/ironrdp-client/src/config.rs +++ b/crates/ironrdp-client/src/config.rs @@ -325,7 +325,7 @@ impl Config { gw_endpoint: gw_addr, gw_user: String::new(), gw_pass: String::new(), - server: String::new(), // TODO non-standard port? also dont use here? + server: String::new(), // TODO: non-standard port? also dont use here? }); } diff --git a/crates/ironrdp-mstsgu/Cargo.toml b/crates/ironrdp-mstsgu/Cargo.toml index 32f00afd..147cba93 100644 --- a/crates/ironrdp-mstsgu/Cargo.toml +++ b/crates/ironrdp-mstsgu/Cargo.toml @@ -3,6 +3,7 @@ name = "ironrdp-mstsgu" version = "0.0.1" readme = "README.md" description = "Terminal Services Gateway Server Protocol" +publish = false # TODO: publish edition.workspace = true license.workspace = true homepage.workspace = true @@ -11,9 +12,9 @@ authors.workspace = true keywords.workspace = true categories.workspace = true -#[lib] -#doctest = false -#test = false +[lib] +doctest = false +test = false [features] default = [] @@ -21,20 +22,20 @@ rustls = ["ironrdp-tls/rustls", "tokio-tungstenite/rustls-tls-native-roots"] native-tls = ["ironrdp-tls/native-tls", "tokio-tungstenite/native-tls"] [dependencies] -bitflags = "2.9" -ironrdp-core = { version = "0.1", features = ["std"] } -ironrdp-error = { version = "0.1" } -tokio = { version = "1.43", features = ["macros", "rt"] } -tokio-util = { version = "0.7" } -tokio-tungstenite = { version = "0.26" } -ironrdp-tls = { "version" = "0.1.3" } -hyper = { version = "1.6", features = ["client", "http1"] } -hyper-util = { version = "0.1", features = ["tokio"] } -http-body-util = { version = "0.1" } -futures-util = "0.3" -log = "0.4" base64 = "0.22" -uuid = { version = "1.16.0", features = ["v4"] } +bitflags = "2.9" +futures-util = "0.3" +http-body-util = { version = "0.1" } +hyper-util = { version = "0.1", features = ["tokio"] } +hyper = { version = "1.6", features = ["client", "http1"] } +ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["std"] } +ironrdp-error = { path = "../ironrdp-error", version = "0.1" } +ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" } +log = "0.4" +tokio-tungstenite = { version = "0.27" } +tokio-util = { version = "0.7" } +tokio = { version = "1.43", features = ["macros", "rt"] } +uuid = { version = "1.16", features = ["v4"] } [lints] workspace = true diff --git a/crates/ironrdp-mstsgu/README.md b/crates/ironrdp-mstsgu/README.md new file mode 100644 index 00000000..8b1d171c --- /dev/null +++ b/crates/ironrdp-mstsgu/README.md @@ -0,0 +1,11 @@ +# IronRDP MS-TSGU + +[Terminal Services Gateway Server Protocol][MS-TSGU] implementation for IronRDP. + +This crate +- implements an MVP state needed to connect through Microsoft RD Gateway, +- only supports the HTTPS protocol with WebSocket (and not the legacy HTTP, HTTP-RPC or UDP protocols), +- does not implement reconnection/reauthentication, and +- only supports basic auth. + +[MS-TSGU]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/0007d661-a86d-4e8f-89f7-7f77f8824188 diff --git a/crates/ironrdp-mstsgu/src/lib.rs b/crates/ironrdp-mstsgu/src/lib.rs index f0e26207..a3c80c07 100644 --- a/crates/ironrdp-mstsgu/src/lib.rs +++ b/crates/ironrdp-mstsgu/src/lib.rs @@ -1,8 +1,11 @@ -//! [MS-TSGU] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/0007d661-a86d-4e8f-89f7-7f77f8824188 -//! * This implements a MVP (in terms of recentness) state needed to connect through microsoft rdp gateway. -//! * This only supports the HTTPS protocol with Websocket (and not the legacy HTTP, HTTP-RPC or UDP protocols). -//! * This does not implement reconnection/reauthentication. -//! * This only supports basic auth. +#![cfg_attr(doc, doc = include_str!("../README.md"))] +#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] + +#[macro_use] +mod macros; + +mod proto; + use core::fmt; use core::fmt::Display; use core::pin::Pin; @@ -22,17 +25,16 @@ use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpStream; use tokio::sync::oneshot; use tokio_tungstenite::tungstenite::handshake::client::generate_key; -use tokio_tungstenite::tungstenite::http::{self}; +use tokio_tungstenite::tungstenite::http; use tokio_tungstenite::tungstenite::protocol::Role; use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::WebSocketStream; +use tokio_util::sync::PollSender; -mod proto; -use proto::{ +use self::proto::{ ChannelPkt, ChannelResp, DataPkt, HandshakeReqPkt, HandshakeRespPkt, HttpCapsTy, KeepalivePkt, PktHdr, PktTy, TunnelAuthPkt, TunnelAuthRespPkt, TunnelReqPkt, TunnelRespPkt, }; -use tokio_util::sync::PollSender; #[derive(Clone, Debug)] pub struct GwConnectTarget { @@ -50,9 +52,10 @@ type Error = ironrdp_error::Error; pub enum GwErrorKind { InvalidGwTarget, Connect, - PacketEOF, + PacketEof, UnsupportedFeature, Custom, + Encode, Decode, } @@ -74,12 +77,13 @@ impl GwErrorExt for ironrdp_error::Error { impl Display for GwErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let x = match self { - GwErrorKind::InvalidGwTarget => "Invalid GW Target", - GwErrorKind::Connect => "Connection error", - GwErrorKind::PacketEOF => "PacketEOF", - GwErrorKind::UnsupportedFeature => "Unsupported feature", - GwErrorKind::Custom => "Custom", - GwErrorKind::Decode => "Decode", + GwErrorKind::InvalidGwTarget => "invalid GW Target", + GwErrorKind::Connect => "connection error", + GwErrorKind::PacketEof => "PacketEOF", + GwErrorKind::UnsupportedFeature => "unsupported feature", + GwErrorKind::Custom => "custom", + GwErrorKind::Encode => "encode", + GwErrorKind::Decode => "decode", }; f.write_str(x) } @@ -87,14 +91,6 @@ impl Display for GwErrorKind { impl core::error::Error for GwErrorKind {} -/// Creates a `ConnectorError` with `Custom` kind and a source error attached to it -#[macro_export] -macro_rules! custom_err { - ( $context:expr, $source:expr $(,)? ) => {{ - <$crate::Error as $crate::GwErrorExt>::custom($context, $source) - }}; -} - struct GwConn { client_name: String, target: GwConnectTarget, @@ -270,13 +266,15 @@ impl GwConn { let mut buf = [0u8; 4096]; let pos = { let mut cur = WriteCursor::new(&mut buf); - payload.encode(&mut cur).unwrap(); + payload + .encode(&mut cur) + .map_err(|e| Error::new("packet encode", GwErrorKind::Encode).with_source(e))?; cur.pos() }; self.ws_sink .send(Message::Binary(Bytes::copy_from_slice(&buf[..pos]))) .await - .map_err(|e| custom_err!("WS Send error", e))?; + .map_err(|e| custom_err!("WebSocket send error", e))?; Ok(()) } @@ -292,7 +290,7 @@ impl GwConn { let hdr = PktHdr::decode(&mut cur).map_err(|_| Error::new("PktHdr", GwErrorKind::Decode))?; if cur.len() != hdr.length as usize - hdr.size() { - return Err(Error::new("read_packet", GwErrorKind::PacketEOF)); + return Err(Error::new("read_packet", GwErrorKind::PacketEof)); } Ok((hdr, msg.split_off(cur.pos()))) diff --git a/crates/ironrdp-mstsgu/src/macros.rs b/crates/ironrdp-mstsgu/src/macros.rs new file mode 100644 index 00000000..bb2299a6 --- /dev/null +++ b/crates/ironrdp-mstsgu/src/macros.rs @@ -0,0 +1,7 @@ +/// Creates a [`crate::Error`] with `Custom` kind and a source error attached to it +#[macro_export] +macro_rules! custom_err { + ( $context:expr, $source:expr $(,)? ) => {{ + <$crate::Error as $crate::GwErrorExt>::custom($context, $source) + }}; +} diff --git a/crates/ironrdp-mstsgu/src/proto.rs b/crates/ironrdp-mstsgu/src/proto.rs index 69a0950a..494c7eff 100644 --- a/crates/ironrdp-mstsgu/src/proto.rs +++ b/crates/ironrdp-mstsgu/src/proto.rs @@ -1,6 +1,7 @@ use bitflags::bitflags; use ironrdp_core::{ - ensure_fixed_part_size, ensure_size, unsupported_value_err, Decode, Encode, ReadCursor, WriteCursor, + cast_int, cast_length, ensure_fixed_part_size, ensure_size, unsupported_value_err, Decode, Encode, ReadCursor, + WriteCursor, }; bitflags! { @@ -70,7 +71,7 @@ pub(crate) struct PktHdr { } impl PktHdr { - const FIXED_PART_SIZE: usize = 4 /* ty */ + 2/* _reserved */ + 2 /* length */; + const FIXED_PART_SIZE: usize = 4 /* ty */ + 2 /* _reserved */ + 2 /* length */; } impl Encode for PktHdr { @@ -89,7 +90,7 @@ impl Encode for PktHdr { } fn size(&self) -> usize { - 8 + Self::FIXED_PART_SIZE } } @@ -123,7 +124,7 @@ impl Encode for HandshakeReqPkt { let hdr = PktHdr { ty: PktTy::HandshakeReq, - length: u32::try_from(self.size()).unwrap(), + length: u32::try_from(self.size()).expect("handshake packet size fits in u32"), ..PktHdr::default() }; hdr.encode(dst)?; @@ -141,7 +142,7 @@ impl Encode for HandshakeReqPkt { } fn size(&self) -> usize { - PktHdr::default().size() + 6 + PktHdr::FIXED_PART_SIZE + 6 } } @@ -191,7 +192,7 @@ impl Encode for TunnelReqPkt { let hdr = PktHdr { ty: PktTy::TunnelCreate, - length: u32::try_from(self.size()).unwrap(), + length: u32::try_from(self.size()).expect("tunnel request packet size fits in u32"), ..PktHdr::default() }; hdr.encode(dst)?; @@ -303,14 +304,16 @@ impl Encode for ExtendedAuthPkt { let hdr = PktHdr { ty: PktTy::ExtendedAuth, - length: u32::try_from(self.size()).unwrap(), + length: cast_int!("packet length", self.size())?, ..PktHdr::default() }; hdr.encode(dst)?; dst.write_u32(self.error_code); - dst.write_u16(u16::try_from(self.blob.len()).unwrap()); + let blob_len: u16 = cast_int!("blob length", self.blob.len())?; + dst.write_u16(blob_len); dst.write_slice(&self.blob); + Ok(()) } @@ -349,17 +352,23 @@ impl Encode for TunnelAuthPkt { let hdr = PktHdr { ty: PktTy::TunnelAuth, - length: u32::try_from(self.size()).unwrap(), + length: cast_int!("packet length", self.size())?, ..PktHdr::default() }; hdr.encode(dst)?; dst.write_u16(self.fields_present); - dst.write_u16(u16::try_from(2 * (self.client_name.len() + 1)).unwrap()); + + let client_name_len = self.client_name.encode_utf16().count() * 2 + 2; // Add 2 to account for a null terminator (0x0000). + let client_name_len: u16 = cast_int!("client name length", client_name_len)?; + dst.write_u16(client_name_len); + for c in self.client_name.encode_utf16() { dst.write_u16(c); } + dst.write_u16(0); + Ok(()) } @@ -409,19 +418,22 @@ impl Encode for ChannelPkt { let hdr = PktHdr { ty: PktTy::ChannelCreate, - length: u32::try_from(self.size()).unwrap(), + length: cast_int!("packet length", self.size())?, ..PktHdr::default() }; hdr.encode(dst)?; - dst.write_u8(u8::try_from(self.resources.len()).unwrap()); + let resources_count: u8 = cast_length!("resources count", self.resources.len())?; + dst.write_u8(resources_count); dst.write_u8(0); // alt_names dst.write_u16(self.port); dst.write_u16(self.protocol); // 2.2.10.3 HTTP_CHANNEL_PACKET_VARIABLE for res in &self.resources { - dst.write_u16(u16::try_from(2 * (res.len() + 1)).unwrap()); + let res_utf16_len = res.encode_utf16().count() * 2 + 2; // Add 2 to account for a null terminator (0x0000). + let res_len: u16 = cast_int!("resource name UTF-16 length", res_utf16_len)?; + dst.write_u16(res_len); for b in res.encode_utf16() { dst.write_u16(b); } @@ -496,11 +508,12 @@ impl Encode for DataPkt<'_> { let hdr = PktHdr { ty: PktTy::Data, - length: u32::try_from(self.size()).unwrap(), + length: cast_int!("packet length", self.size())?, ..PktHdr::default() }; hdr.encode(dst)?; - dst.write_u16(u16::try_from(self.data.len()).unwrap()); + let data_len: u16 = cast_int!("data payload length", self.data.len())?; + dst.write_u16(data_len); dst.write_slice(self.data); Ok(()) } @@ -531,7 +544,7 @@ impl Encode for KeepalivePkt { fn encode(&self, dst: &mut WriteCursor<'_>) -> ironrdp_core::EncodeResult<()> { let hdr = PktHdr { ty: PktTy::Keepalive, - length: u32::try_from(self.size()).unwrap(), + length: u32::try_from(self.size()).expect("keepalive packet size fits in u32"), ..PktHdr::default() }; hdr.encode(dst)