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.
#### [`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.

141
Cargo.lock generated
View file

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

View file

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

View file

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

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
//! * 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<GwErrorKind>;
pub enum GwErrorKind {
InvalidGwTarget,
Connect,
PacketEOF,
PacketEof,
UnsupportedFeature,
Custom,
Encode,
Decode,
}
@ -74,12 +77,13 @@ impl GwErrorExt for ironrdp_error::Error<GwErrorKind> {
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())))

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