refactor(mstsgu): follow up to PR 913 (#920)
Some checks are pending
CI / Fuzzing (push) Blocked by required conditions
CI / Check formatting (push) Waiting to run
CI / Check typos (push) Waiting to run
CI / Checks [linux] (push) Blocked by required conditions
CI / Checks [macos] (push) Blocked by required conditions
CI / Checks [windows] (push) Blocked by required conditions
CI / Web Client (push) Blocked by required conditions
CI / FFI (push) Blocked by required conditions
CI / Success (push) Blocked by required conditions
Coverage / Coverage Report (push) Waiting to run
Release crates / Open release PR (push) Waiting to run
Release crates / Release crates (push) Waiting to run

- 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
This commit is contained in:
Benoît Cortier 2025-08-19 10:01:49 -04:00 committed by GitHub
parent c84b46be91
commit 27f5504508
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 132 additions and 163 deletions

View file

@ -256,6 +256,10 @@ State machines to drive an RDP connection acceptance sequence
Extendable skeleton for implementing custom RDP servers. 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) #### [`crates/ironrdp-glutin-renderer`](./crates/ironrdp-glutin-renderer) (no maintainer)
`glutin` primitives for OpenGL rendering. `glutin` primitives for OpenGL rendering.

141
Cargo.lock generated
View file

@ -1510,7 +1510,7 @@ dependencies = [
"embed-resource", "embed-resource",
"ironrdp", "ironrdp",
"ironrdp-cliprdr-native", "ironrdp-cliprdr-native",
"ironrdp-core 0.1.5", "ironrdp-core",
"sspi", "sspi",
"thiserror 2.0.14", "thiserror 2.0.14",
"tracing", "tracing",
@ -2332,7 +2332,7 @@ dependencies = [
"ironrdp-cliprdr", "ironrdp-cliprdr",
"ironrdp-cliprdr-native", "ironrdp-cliprdr-native",
"ironrdp-connector", "ironrdp-connector",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-displaycontrol", "ironrdp-displaycontrol",
"ironrdp-dvc", "ironrdp-dvc",
"ironrdp-graphics", "ironrdp-graphics",
@ -2359,7 +2359,7 @@ version = "0.6.0"
dependencies = [ dependencies = [
"ironrdp-async", "ironrdp-async",
"ironrdp-connector", "ironrdp-connector",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
"tracing", "tracing",
@ -2370,7 +2370,7 @@ name = "ironrdp-ainput"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-dvc", "ironrdp-dvc",
"num-derive", "num-derive",
"num-traits", "num-traits",
@ -2382,7 +2382,7 @@ version = "0.6.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"ironrdp-connector", "ironrdp-connector",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"tracing", "tracing",
] ]
@ -2403,7 +2403,7 @@ version = "0.6.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"ironrdp-connector", "ironrdp-connector",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"tracing", "tracing",
] ]
@ -2426,14 +2426,14 @@ dependencies = [
"ironrdp", "ironrdp",
"ironrdp-cfg", "ironrdp-cfg",
"ironrdp-cliprdr-native", "ironrdp-cliprdr-native",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-dvc-pipe-proxy", "ironrdp-dvc-pipe-proxy",
"ironrdp-mstsgu", "ironrdp-mstsgu",
"ironrdp-propertyset", "ironrdp-propertyset",
"ironrdp-rdcleanpath", "ironrdp-rdcleanpath",
"ironrdp-rdpfile", "ironrdp-rdpfile",
"ironrdp-rdpsnd-native", "ironrdp-rdpsnd-native",
"ironrdp-tls 0.1.3", "ironrdp-tls",
"ironrdp-tokio", "ironrdp-tokio",
"proc-exit", "proc-exit",
"raw-window-handle", "raw-window-handle",
@ -2442,7 +2442,7 @@ dependencies = [
"softbuffer", "softbuffer",
"tap", "tap",
"tokio", "tokio",
"tokio-tungstenite 0.27.0", "tokio-tungstenite",
"tokio-util", "tokio-util",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -2460,7 +2460,7 @@ name = "ironrdp-cliprdr"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
"tracing", "tracing",
@ -2470,7 +2470,7 @@ dependencies = [
name = "ironrdp-cliprdr-format" name = "ironrdp-cliprdr-format"
version = "0.1.3" version = "0.1.3"
dependencies = [ dependencies = [
"ironrdp-core 0.1.5", "ironrdp-core",
"png", "png",
] ]
@ -2479,7 +2479,7 @@ name = "ironrdp-cliprdr-native"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"ironrdp-cliprdr", "ironrdp-cliprdr",
"ironrdp-core 0.1.5", "ironrdp-core",
"tracing", "tracing",
"windows 0.61.3", "windows 0.61.3",
] ]
@ -2489,8 +2489,8 @@ name = "ironrdp-connector"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-error 0.1.3", "ironrdp-error",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
"picky", "picky",
@ -2506,23 +2506,14 @@ dependencies = [
name = "ironrdp-core" name = "ironrdp-core"
version = "0.1.5" version = "0.1.5"
dependencies = [ dependencies = [
"ironrdp-error 0.1.3", "ironrdp-error",
]
[[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)",
] ]
[[package]] [[package]]
name = "ironrdp-displaycontrol" name = "ironrdp-displaycontrol"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-dvc", "ironrdp-dvc",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
@ -2533,7 +2524,7 @@ dependencies = [
name = "ironrdp-dvc" name = "ironrdp-dvc"
version = "0.3.1" version = "0.3.1"
dependencies = [ dependencies = [
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
"slab", "slab",
@ -2545,7 +2536,7 @@ name = "ironrdp-dvc-pipe-proxy"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-dvc", "ironrdp-dvc",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
@ -2557,12 +2548,6 @@ dependencies = [
name = "ironrdp-error" name = "ironrdp-error"
version = "0.1.3" 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]] [[package]]
name = "ironrdp-futures" name = "ironrdp-futures"
version = "0.4.0" version = "0.4.0"
@ -2579,7 +2564,7 @@ dependencies = [
"arbitrary", "arbitrary",
"ironrdp-cliprdr", "ironrdp-cliprdr",
"ironrdp-cliprdr-format", "ironrdp-cliprdr-format",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-displaycontrol", "ironrdp-displaycontrol",
"ironrdp-graphics", "ironrdp-graphics",
"ironrdp-pdu", "ironrdp-pdu",
@ -2599,7 +2584,7 @@ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
"expect-test", "expect-test",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"lazy_static", "lazy_static",
"num-derive", "num-derive",
@ -2626,12 +2611,12 @@ dependencies = [
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-util", "hyper-util",
"ironrdp-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "ironrdp-core",
"ironrdp-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ironrdp-error",
"ironrdp-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ironrdp-tls",
"log", "log",
"tokio", "tokio",
"tokio-tungstenite 0.26.2", "tokio-tungstenite",
"tokio-util", "tokio-util",
"uuid", "uuid",
] ]
@ -2645,8 +2630,8 @@ dependencies = [
"byteorder", "byteorder",
"der-parser", "der-parser",
"expect-test", "expect-test",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-error 0.1.3", "ironrdp-error",
"lazy_static", "lazy_static",
"md-5", "md-5",
"num-bigint", "num-bigint",
@ -2683,8 +2668,8 @@ name = "ironrdp-rdpdr"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-error 0.1.3", "ironrdp-error",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
"tracing", "tracing",
@ -2694,7 +2679,7 @@ dependencies = [
name = "ironrdp-rdpdr-native" name = "ironrdp-rdpdr-native"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-rdpdr", "ironrdp-rdpdr",
"ironrdp-svc", "ironrdp-svc",
@ -2714,7 +2699,7 @@ name = "ironrdp-rdpsnd"
version = "0.5.0" version = "0.5.0"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
"tracing", "tracing",
@ -2744,7 +2729,7 @@ dependencies = [
"ironrdp-ainput", "ironrdp-ainput",
"ironrdp-async", "ironrdp-async",
"ironrdp-cliprdr", "ironrdp-cliprdr",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-displaycontrol", "ironrdp-displaycontrol",
"ironrdp-dvc", "ironrdp-dvc",
"ironrdp-graphics", "ironrdp-graphics",
@ -2768,10 +2753,10 @@ name = "ironrdp-session"
version = "0.5.0" version = "0.5.0"
dependencies = [ dependencies = [
"ironrdp-connector", "ironrdp-connector",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-displaycontrol", "ironrdp-displaycontrol",
"ironrdp-dvc", "ironrdp-dvc",
"ironrdp-error 0.1.3", "ironrdp-error",
"ironrdp-graphics", "ironrdp-graphics",
"ironrdp-pdu", "ironrdp-pdu",
"ironrdp-svc", "ironrdp-svc",
@ -2789,7 +2774,7 @@ name = "ironrdp-svc"
version = "0.4.1" version = "0.4.1"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-pdu", "ironrdp-pdu",
] ]
@ -2804,7 +2789,7 @@ dependencies = [
"ironrdp-cliprdr", "ironrdp-cliprdr",
"ironrdp-cliprdr-format", "ironrdp-cliprdr-format",
"ironrdp-connector", "ironrdp-connector",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-displaycontrol", "ironrdp-displaycontrol",
"ironrdp-dvc", "ironrdp-dvc",
"ironrdp-fuzzing", "ironrdp-fuzzing",
@ -2833,7 +2818,7 @@ dependencies = [
"async-trait", "async-trait",
"ironrdp", "ironrdp",
"ironrdp-async", "ironrdp-async",
"ironrdp-tls 0.1.3", "ironrdp-tls",
"ironrdp-tokio", "ironrdp-tokio",
"semver", "semver",
"tokio", "tokio",
@ -2851,18 +2836,6 @@ dependencies = [
"x509-cert", "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]] [[package]]
name = "ironrdp-tokio" name = "ironrdp-tokio"
version = "0.6.0" version = "0.6.0"
@ -2892,7 +2865,7 @@ dependencies = [
"iron-remote-desktop", "iron-remote-desktop",
"ironrdp", "ironrdp",
"ironrdp-cliprdr-format", "ironrdp-cliprdr-format",
"ironrdp-core 0.1.5", "ironrdp-core",
"ironrdp-futures", "ironrdp-futures",
"ironrdp-propertyset", "ironrdp-propertyset",
"ironrdp-rdcleanpath", "ironrdp-rdcleanpath",
@ -5523,24 +5496,6 @@ dependencies = [
"tokio", "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]] [[package]]
name = "tokio-tungstenite" name = "tokio-tungstenite"
version = "0.27.0" version = "0.27.0"
@ -5556,7 +5511,7 @@ dependencies = [
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tokio-rustls", "tokio-rustls",
"tungstenite 0.27.0", "tungstenite",
] ]
[[package]] [[package]]
@ -5776,26 +5731,6 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" 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]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.27.0" version = "0.27.0"

View file

@ -325,7 +325,7 @@ impl Config {
gw_endpoint: gw_addr, gw_endpoint: gw_addr,
gw_user: String::new(), gw_user: String::new(),
gw_pass: 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?
}); });
} }

View file

@ -3,6 +3,7 @@ name = "ironrdp-mstsgu"
version = "0.0.1" version = "0.0.1"
readme = "README.md" readme = "README.md"
description = "Terminal Services Gateway Server Protocol" description = "Terminal Services Gateway Server Protocol"
publish = false # TODO: publish
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
homepage.workspace = true homepage.workspace = true
@ -11,9 +12,9 @@ authors.workspace = true
keywords.workspace = true keywords.workspace = true
categories.workspace = true categories.workspace = true
#[lib] [lib]
#doctest = false doctest = false
#test = false test = false
[features] [features]
default = [] 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"] native-tls = ["ironrdp-tls/native-tls", "tokio-tungstenite/native-tls"]
[dependencies] [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" 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] [lints]
workspace = true workspace = true

View file

@ -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

View file

@ -1,8 +1,11 @@
//! [MS-TSGU] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/0007d661-a86d-4e8f-89f7-7f77f8824188 #![cfg_attr(doc, doc = include_str!("../README.md"))]
//! * This implements a MVP (in terms of recentness) state needed to connect through microsoft rdp gateway. #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
//! * This only supports the HTTPS protocol with Websocket (and not the legacy HTTP, HTTP-RPC or UDP protocols).
//! * This does not implement reconnection/reauthentication. #[macro_use]
//! * This only supports basic auth. mod macros;
mod proto;
use core::fmt; use core::fmt;
use core::fmt::Display; use core::fmt::Display;
use core::pin::Pin; use core::pin::Pin;
@ -22,17 +25,16 @@ use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use tokio_tungstenite::tungstenite::handshake::client::generate_key; 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::protocol::Role;
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::WebSocketStream; use tokio_tungstenite::WebSocketStream;
use tokio_util::sync::PollSender;
mod proto; use self::proto::{
use proto::{
ChannelPkt, ChannelResp, DataPkt, HandshakeReqPkt, HandshakeRespPkt, HttpCapsTy, KeepalivePkt, PktHdr, PktTy, ChannelPkt, ChannelResp, DataPkt, HandshakeReqPkt, HandshakeRespPkt, HttpCapsTy, KeepalivePkt, PktHdr, PktTy,
TunnelAuthPkt, TunnelAuthRespPkt, TunnelReqPkt, TunnelRespPkt, TunnelAuthPkt, TunnelAuthRespPkt, TunnelReqPkt, TunnelRespPkt,
}; };
use tokio_util::sync::PollSender;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct GwConnectTarget { pub struct GwConnectTarget {
@ -50,9 +52,10 @@ type Error = ironrdp_error::Error<GwErrorKind>;
pub enum GwErrorKind { pub enum GwErrorKind {
InvalidGwTarget, InvalidGwTarget,
Connect, Connect,
PacketEOF, PacketEof,
UnsupportedFeature, UnsupportedFeature,
Custom, Custom,
Encode,
Decode, Decode,
} }
@ -74,12 +77,13 @@ impl GwErrorExt for ironrdp_error::Error<GwErrorKind> {
impl Display for GwErrorKind { impl Display for GwErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let x = match self { let x = match self {
GwErrorKind::InvalidGwTarget => "Invalid GW Target", GwErrorKind::InvalidGwTarget => "invalid GW Target",
GwErrorKind::Connect => "Connection error", GwErrorKind::Connect => "connection error",
GwErrorKind::PacketEOF => "PacketEOF", GwErrorKind::PacketEof => "PacketEOF",
GwErrorKind::UnsupportedFeature => "Unsupported feature", GwErrorKind::UnsupportedFeature => "unsupported feature",
GwErrorKind::Custom => "Custom", GwErrorKind::Custom => "custom",
GwErrorKind::Decode => "Decode", GwErrorKind::Encode => "encode",
GwErrorKind::Decode => "decode",
}; };
f.write_str(x) f.write_str(x)
} }
@ -87,14 +91,6 @@ impl Display for GwErrorKind {
impl core::error::Error 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 { struct GwConn {
client_name: String, client_name: String,
target: GwConnectTarget, target: GwConnectTarget,
@ -270,13 +266,15 @@ impl GwConn {
let mut buf = [0u8; 4096]; let mut buf = [0u8; 4096];
let pos = { let pos = {
let mut cur = WriteCursor::new(&mut buf); 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() cur.pos()
}; };
self.ws_sink self.ws_sink
.send(Message::Binary(Bytes::copy_from_slice(&buf[..pos]))) .send(Message::Binary(Bytes::copy_from_slice(&buf[..pos])))
.await .await
.map_err(|e| custom_err!("WS Send error", e))?; .map_err(|e| custom_err!("WebSocket send error", e))?;
Ok(()) Ok(())
} }
@ -292,7 +290,7 @@ impl GwConn {
let hdr = PktHdr::decode(&mut cur).map_err(|_| Error::new("PktHdr", GwErrorKind::Decode))?; let hdr = PktHdr::decode(&mut cur).map_err(|_| Error::new("PktHdr", GwErrorKind::Decode))?;
if cur.len() != hdr.length as usize - hdr.size() { 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()))) Ok((hdr, msg.split_off(cur.pos())))

View file

@ -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)
}};
}

View file

@ -1,6 +1,7 @@
use bitflags::bitflags; use bitflags::bitflags;
use ironrdp_core::{ 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! { bitflags! {
@ -70,7 +71,7 @@ pub(crate) struct PktHdr {
} }
impl 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 { impl Encode for PktHdr {
@ -89,7 +90,7 @@ impl Encode for PktHdr {
} }
fn size(&self) -> usize { fn size(&self) -> usize {
8 Self::FIXED_PART_SIZE
} }
} }
@ -123,7 +124,7 @@ impl Encode for HandshakeReqPkt {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::HandshakeReq, 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() ..PktHdr::default()
}; };
hdr.encode(dst)?; hdr.encode(dst)?;
@ -141,7 +142,7 @@ impl Encode for HandshakeReqPkt {
} }
fn size(&self) -> usize { fn size(&self) -> usize {
PktHdr::default().size() + 6 PktHdr::FIXED_PART_SIZE + 6
} }
} }
@ -191,7 +192,7 @@ impl Encode for TunnelReqPkt {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::TunnelCreate, 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() ..PktHdr::default()
}; };
hdr.encode(dst)?; hdr.encode(dst)?;
@ -303,14 +304,16 @@ impl Encode for ExtendedAuthPkt {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::ExtendedAuth, ty: PktTy::ExtendedAuth,
length: u32::try_from(self.size()).unwrap(), length: cast_int!("packet length", self.size())?,
..PktHdr::default() ..PktHdr::default()
}; };
hdr.encode(dst)?; hdr.encode(dst)?;
dst.write_u32(self.error_code); 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); dst.write_slice(&self.blob);
Ok(()) Ok(())
} }
@ -349,17 +352,23 @@ impl Encode for TunnelAuthPkt {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::TunnelAuth, ty: PktTy::TunnelAuth,
length: u32::try_from(self.size()).unwrap(), length: cast_int!("packet length", self.size())?,
..PktHdr::default() ..PktHdr::default()
}; };
hdr.encode(dst)?; hdr.encode(dst)?;
dst.write_u16(self.fields_present); 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() { for c in self.client_name.encode_utf16() {
dst.write_u16(c); dst.write_u16(c);
} }
dst.write_u16(0); dst.write_u16(0);
Ok(()) Ok(())
} }
@ -409,19 +418,22 @@ impl Encode for ChannelPkt {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::ChannelCreate, ty: PktTy::ChannelCreate,
length: u32::try_from(self.size()).unwrap(), length: cast_int!("packet length", self.size())?,
..PktHdr::default() ..PktHdr::default()
}; };
hdr.encode(dst)?; 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_u8(0); // alt_names
dst.write_u16(self.port); dst.write_u16(self.port);
dst.write_u16(self.protocol); dst.write_u16(self.protocol);
// 2.2.10.3 HTTP_CHANNEL_PACKET_VARIABLE // 2.2.10.3 HTTP_CHANNEL_PACKET_VARIABLE
for res in &self.resources { 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() { for b in res.encode_utf16() {
dst.write_u16(b); dst.write_u16(b);
} }
@ -496,11 +508,12 @@ impl Encode for DataPkt<'_> {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::Data, ty: PktTy::Data,
length: u32::try_from(self.size()).unwrap(), length: cast_int!("packet length", self.size())?,
..PktHdr::default() ..PktHdr::default()
}; };
hdr.encode(dst)?; 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); dst.write_slice(self.data);
Ok(()) Ok(())
} }
@ -531,7 +544,7 @@ impl Encode for KeepalivePkt {
fn encode(&self, dst: &mut WriteCursor<'_>) -> ironrdp_core::EncodeResult<()> { fn encode(&self, dst: &mut WriteCursor<'_>) -> ironrdp_core::EncodeResult<()> {
let hdr = PktHdr { let hdr = PktHdr {
ty: PktTy::Keepalive, 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() ..PktHdr::default()
}; };
hdr.encode(dst) hdr.encode(dst)