use alloc::collections::BTreeSet;
use alloc::format;
use core::fmt;
use rat_rdp_core::{
Decode, DecodeError, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor, cast_length,
ensure_fixed_part_size, ensure_size, invalid_field_err, unsupported_value_err,
};
use rat_rdp_pdu::utils::{
CharacterSet, checked_sum, encoded_str_len, read_string_from_cursor, strict_sum, write_string_to_cursor,
};
use rat_rdp_svc::SvcEncode;
use crate::{DynamicChannelId, String, Vec};
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum DrdynvcDataPdu {
DataFirst(DataFirstPdu),
Data(DataPdu),
}
impl DrdynvcDataPdu {
pub const MAX_DATA_SIZE: usize = 1590;
pub fn channel_id(&self) -> DynamicChannelId {
match self {
DrdynvcDataPdu::DataFirst(pdu) => pdu.channel_id,
DrdynvcDataPdu::Data(pdu) => pdu.channel_id,
}
}
}
impl Encode for DrdynvcDataPdu {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
match self {
DrdynvcDataPdu::DataFirst(pdu) => pdu.encode(dst),
DrdynvcDataPdu::Data(pdu) => pdu.encode(dst),
}
}
fn name(&self) -> &'static str {
match self {
DrdynvcDataPdu::DataFirst(_) => DataFirstPdu::name(),
DrdynvcDataPdu::Data(_) => DataPdu::name(),
}
}
fn size(&self) -> usize {
match self {
DrdynvcDataPdu::DataFirst(pdu) => pdu.size(),
DrdynvcDataPdu::Data(pdu) => pdu.size(),
}
}
}
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum DrdynvcClientPdu {
Capabilities(CapabilitiesResponsePdu),
Create(CreateResponsePdu),
Close(ClosePdu),
Data(DrdynvcDataPdu),
SoftSyncResponse(SoftSyncResponsePdu),
}
impl Encode for DrdynvcClientPdu {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
match self {
DrdynvcClientPdu::Capabilities(pdu) => pdu.encode(dst),
DrdynvcClientPdu::Create(pdu) => pdu.encode(dst),
DrdynvcClientPdu::Data(pdu) => pdu.encode(dst),
DrdynvcClientPdu::Close(pdu) => pdu.encode(dst),
DrdynvcClientPdu::SoftSyncResponse(pdu) => pdu.encode(dst),
}
}
fn name(&self) -> &'static str {
match self {
DrdynvcClientPdu::Capabilities(_) => CapabilitiesResponsePdu::name(),
DrdynvcClientPdu::Create(_) => CreateResponsePdu::name(),
DrdynvcClientPdu::Data(pdu) => pdu.name(),
DrdynvcClientPdu::Close(_) => ClosePdu::name(),
DrdynvcClientPdu::SoftSyncResponse(_) => SoftSyncResponsePdu::name(),
}
}
fn size(&self) -> usize {
match self {
DrdynvcClientPdu::Capabilities(_) => CapabilitiesResponsePdu::size(),
DrdynvcClientPdu::Create(pdu) => pdu.size(),
DrdynvcClientPdu::Data(pdu) => pdu.size(),
DrdynvcClientPdu::Close(pdu) => pdu.size(),
DrdynvcClientPdu::SoftSyncResponse(pdu) => pdu.size(),
}
}
}
impl Decode<'_> for DrdynvcClientPdu {
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let header = Header::decode(src)?;
match header.cmd {
Cmd::Create => Ok(Self::Create(CreateResponsePdu::decode(header, src)?)),
Cmd::DataFirst => Ok(Self::Data(DrdynvcDataPdu::DataFirst(DataFirstPdu::decode(
header, src,
)?))),
Cmd::Data => Ok(Self::Data(DrdynvcDataPdu::Data(DataPdu::decode(header, src)?))),
Cmd::Close => Ok(Self::Close(ClosePdu::decode(header, src)?)),
Cmd::Capability => Ok(Self::Capabilities(CapabilitiesResponsePdu::decode(header, src)?)),
Cmd::SoftSyncResponse => Ok(Self::SoftSyncResponse(SoftSyncResponsePdu::decode(header, src)?)),
_ => Err(unsupported_value_err!("Cmd", header.cmd.into())),
}
}
}
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum DrdynvcServerPdu {
Capabilities(CapabilitiesRequestPdu),
Create(CreateRequestPdu),
Close(ClosePdu),
Data(DrdynvcDataPdu),
SoftSyncRequest(SoftSyncRequestPdu),
}
impl Encode for DrdynvcServerPdu {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
match self {
DrdynvcServerPdu::Data(pdu) => pdu.encode(dst),
DrdynvcServerPdu::Capabilities(pdu) => pdu.encode(dst),
DrdynvcServerPdu::Create(pdu) => pdu.encode(dst),
DrdynvcServerPdu::Close(pdu) => pdu.encode(dst),
DrdynvcServerPdu::SoftSyncRequest(pdu) => pdu.encode(dst),
}
}
fn name(&self) -> &'static str {
match self {
DrdynvcServerPdu::Data(pdu) => pdu.name(),
DrdynvcServerPdu::Capabilities(pdu) => pdu.name(),
DrdynvcServerPdu::Create(_) => CreateRequestPdu::name(),
DrdynvcServerPdu::Close(_) => ClosePdu::name(),
DrdynvcServerPdu::SoftSyncRequest(_) => SoftSyncRequestPdu::name(),
}
}
fn size(&self) -> usize {
match self {
DrdynvcServerPdu::Data(pdu) => pdu.size(),
DrdynvcServerPdu::Capabilities(pdu) => pdu.size(),
DrdynvcServerPdu::Create(pdu) => pdu.size(),
DrdynvcServerPdu::Close(pdu) => pdu.size(),
DrdynvcServerPdu::SoftSyncRequest(pdu) => pdu.size(),
}
}
}
impl Decode<'_> for DrdynvcServerPdu {
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let header = Header::decode(src)?;
match header.cmd {
Cmd::Create => Ok(Self::Create(CreateRequestPdu::decode(header, src)?)),
Cmd::DataFirst => Ok(Self::Data(DrdynvcDataPdu::DataFirst(DataFirstPdu::decode(
header, src,
)?))),
Cmd::Data => Ok(Self::Data(DrdynvcDataPdu::Data(DataPdu::decode(header, src)?))),
Cmd::Close => Ok(Self::Close(ClosePdu::decode(header, src)?)),
Cmd::Capability => Ok(Self::Capabilities(CapabilitiesRequestPdu::decode(header, src)?)),
Cmd::SoftSyncRequest => Ok(Self::SoftSyncRequest(SoftSyncRequestPdu::decode(header, src)?)),
_ => Err(unsupported_value_err!("Cmd", header.cmd.into())),
}
}
}
impl SvcEncode for DrdynvcDataPdu {}
impl SvcEncode for DrdynvcClientPdu {}
impl SvcEncode for DrdynvcServerPdu {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct SoftSyncTunnelType(u32);
impl SoftSyncTunnelType {
pub const RELIABLE_UDP: Self = Self(0x0000_0001);
pub const LOSSY_UDP: Self = Self(0x0000_0003);
}
impl From<u32> for SoftSyncTunnelType {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<SoftSyncTunnelType> for u32 {
fn from(value: SoftSyncTunnelType) -> Self {
value.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SoftSyncChannelList {
tunnel_type: SoftSyncTunnelType,
channel_ids: Vec<DynamicChannelId>,
}
impl SoftSyncChannelList {
const FIXED_PART_SIZE: usize = 4 + 2 ;
pub fn new(tunnel_type: SoftSyncTunnelType, channel_ids: Vec<DynamicChannelId>) -> Self {
Self {
tunnel_type,
channel_ids,
}
}
pub fn tunnel_type(&self) -> SoftSyncTunnelType {
self.tunnel_type
}
pub fn channel_ids(&self) -> &[DynamicChannelId] {
&self.channel_ids
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
dst.write_u32(self.tunnel_type.into());
dst.write_u16(cast_length!("NumberOfDVCs", self.channel_ids.len())?);
for channel_id in &self.channel_ids {
dst.write_u32(*channel_id);
}
Ok(())
}
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::FIXED_PART_SIZE);
let tunnel_type = SoftSyncTunnelType::from(src.read_u32());
let number_of_dvcs = usize::from(src.read_u16());
ensure_size!(in: src, size: number_of_dvcs * 4 );
let mut channel_ids = Vec::with_capacity(number_of_dvcs);
for _ in 0..number_of_dvcs {
channel_ids.push(src.read_u32());
}
Ok(Self {
tunnel_type,
channel_ids,
})
}
fn size(&self) -> usize {
Self::FIXED_PART_SIZE + self.channel_ids.len() * 4
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SoftSyncRequestPdu {
channel_lists: Vec<SoftSyncChannelList>,
}
impl SoftSyncRequestPdu {
const TCP_FLUSHED: u16 = 0x0001;
const CHANNEL_LIST_PRESENT: u16 = 0x0002;
const LENGTH_FIXED_PART_SIZE: usize = 4 + 2 + 2 ;
const FIXED_PART_SIZE: usize = Header::FIXED_PART_SIZE + 1 + Self::LENGTH_FIXED_PART_SIZE;
pub fn new(channel_lists: Vec<SoftSyncChannelList>) -> Self {
Self { channel_lists }
}
pub fn channel_lists(&self) -> &[SoftSyncChannelList] {
&self.channel_lists
}
fn flags(&self) -> u16 {
Self::TCP_FLUSHED
| if self.channel_lists.is_empty() {
0
} else {
Self::CHANNEL_LIST_PRESENT
}
}
fn validate(&self) -> EncodeResult<()> {
let _: u16 = cast_length!("NumberOfTunnels", self.channel_lists.len())?;
let mut tunnel_types = BTreeSet::new();
let mut channel_ids = BTreeSet::new();
for list in &self.channel_lists {
let _: u16 = cast_length!("NumberOfDVCs", list.channel_ids.len())?;
if !tunnel_types.insert(list.tunnel_type) {
return Err(invalid_field_err!(
"TunnelType",
"appears in more than one channel list"
));
}
for channel_id in &list.channel_ids {
if !channel_ids.insert(*channel_id) {
return Err(invalid_field_err!(
"ListOfDVCIds",
"channel ID appears in more than one channel list"
));
}
}
}
Ok(())
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
if header.cb_id != FieldType::U8 || header.sp != FieldType::U8 {
return Err(invalid_field_err!("Header", "cbId and Sp must be zero"));
}
ensure_size!(in: src, size: Self::FIXED_PART_SIZE - Header::FIXED_PART_SIZE);
let headerless_size = src.len();
let pad = src.read_u8();
if pad != 0 {
return Err(invalid_field_err!("Pad", "must be zero"));
}
let length = usize::try_from(src.read_u32()).map_err(|_| invalid_field_err!("Length", "is too large"))?;
if length != headerless_size - 1
{
return Err(invalid_field_err!(
"Length",
"does not match the bytes following the pad field"
));
}
let flags = src.read_u16();
if flags & Self::TCP_FLUSHED == 0 {
return Err(invalid_field_err!("Flags", "SOFT_SYNC_TCP_FLUSHED must be set"));
}
let number_of_tunnels = usize::from(src.read_u16());
if number_of_tunnels > src.len() / SoftSyncChannelList::FIXED_PART_SIZE {
return Err(invalid_field_err!(
"NumberOfTunnels",
"does not fit in the remaining bytes"
));
}
let mut channel_lists = Vec::with_capacity(number_of_tunnels);
let mut tunnel_types = BTreeSet::new();
let mut channel_ids = BTreeSet::new();
for _ in 0..number_of_tunnels {
let list = SoftSyncChannelList::decode(src)?;
if !tunnel_types.insert(list.tunnel_type) {
return Err(invalid_field_err!(
"TunnelType",
"appears in more than one channel list"
));
}
for channel_id in &list.channel_ids {
if !channel_ids.insert(*channel_id) {
return Err(invalid_field_err!(
"ListOfDVCIds",
"channel ID appears in more than one channel list"
));
}
}
channel_lists.push(list);
}
if !src.is_empty() {
return Err(invalid_field_err!("Length", "contains trailing bytes"));
}
Ok(Self { channel_lists })
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
self.validate()?;
ensure_size!(in: dst, size: self.size());
Header::new(0, 0, Cmd::SoftSyncRequest).encode(dst)?;
dst.write_u8(0);
dst.write_u32(cast_length!("Length", self.length())?);
dst.write_u16(self.flags());
dst.write_u16(cast_length!("NumberOfTunnels", self.channel_lists.len())?);
for list in &self.channel_lists {
list.encode(dst)?;
}
Ok(())
}
fn name() -> &'static str {
"DYNVC_SOFT_SYNC_REQUEST"
}
fn length(&self) -> usize {
Self::LENGTH_FIXED_PART_SIZE + self.channel_lists.iter().map(SoftSyncChannelList::size).sum::<usize>()
}
fn size(&self) -> usize {
Header::FIXED_PART_SIZE + 1 + self.length()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SoftSyncResponsePdu {
tunnels_to_switch: Vec<SoftSyncTunnelType>,
}
impl SoftSyncResponsePdu {
const FIXED_PART_SIZE: usize = Header::FIXED_PART_SIZE + 1 + 4 ;
pub fn new(tunnels_to_switch: Vec<SoftSyncTunnelType>) -> Self {
Self { tunnels_to_switch }
}
pub fn tunnels_to_switch(&self) -> &[SoftSyncTunnelType] {
&self.tunnels_to_switch
}
fn validate(&self) -> EncodeResult<()> {
let _: u32 = cast_length!("NumberOfTunnels", self.tunnels_to_switch.len())?;
let mut tunnels = BTreeSet::new();
for tunnel in &self.tunnels_to_switch {
if !tunnels.insert(*tunnel) {
return Err(invalid_field_err!(
"TunnelsToSwitch",
"contains a duplicate tunnel type"
));
}
}
Ok(())
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
if header.cb_id != FieldType::U8 || header.sp != FieldType::U8 {
return Err(invalid_field_err!("Header", "cbId and Sp must be zero"));
}
ensure_size!(in: src, size: Self::FIXED_PART_SIZE - Header::FIXED_PART_SIZE);
let pad = src.read_u8();
if pad != 0 {
return Err(invalid_field_err!("Pad", "must be zero"));
}
let number_of_tunnels =
usize::try_from(src.read_u32()).map_err(|_| invalid_field_err!("NumberOfTunnels", "is too large"))?;
if number_of_tunnels > src.len() / 4
{
return Err(invalid_field_err!(
"NumberOfTunnels",
"does not fit in the remaining bytes"
));
}
let mut tunnels_to_switch = Vec::with_capacity(number_of_tunnels);
let mut tunnels = BTreeSet::new();
for _ in 0..number_of_tunnels {
let tunnel = SoftSyncTunnelType::from(src.read_u32());
if !tunnels.insert(tunnel) {
return Err(invalid_field_err!(
"TunnelsToSwitch",
"contains a duplicate tunnel type"
));
}
tunnels_to_switch.push(tunnel);
}
if !src.is_empty() {
return Err(invalid_field_err!(
"NumberOfTunnels",
"does not match the remaining bytes"
));
}
Ok(Self { tunnels_to_switch })
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
self.validate()?;
ensure_size!(in: dst, size: self.size());
Header::new(0, 0, Cmd::SoftSyncResponse).encode(dst)?;
dst.write_u8(0);
dst.write_u32(cast_length!("NumberOfTunnels", self.tunnels_to_switch.len())?);
for tunnel in &self.tunnels_to_switch {
dst.write_u32((*tunnel).into());
}
Ok(())
}
fn name() -> &'static str {
"DYNVC_SOFT_SYNC_RESPONSE"
}
fn size(&self) -> usize {
Self::FIXED_PART_SIZE + self.tunnels_to_switch.len() * 4
}
}
#[derive(Debug, PartialEq)]
pub struct Header {
cb_id: FieldType, sp: FieldType, cmd: Cmd, }
impl Header {
pub const FIXED_PART_SIZE: usize = 1;
fn new(cb_id_val: u32, sp_val: u32, cmd: Cmd) -> Self {
Self {
cb_id: FieldType::for_val(cb_id_val),
sp: FieldType::for_val(sp_val),
cmd,
}
}
fn with_cb_id(self, cb_id: FieldType) -> Self {
Self { cb_id, ..self }
}
fn with_sp(self, sp: FieldType) -> Self {
Self { sp, ..self }
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_fixed_part_size!(in: dst);
dst.write_u8(((self.cmd.as_u8()) << 4) | (Into::<u8>::into(self.sp) << 2) | Into::<u8>::into(self.cb_id));
Ok(())
}
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::size());
let byte = src.read_u8();
let cmd = Cmd::try_from(byte >> 4)?;
let sp = FieldType::from((byte >> 2) & 0b11);
let cb_id = FieldType::from(byte & 0b11);
Ok(Self { cb_id, sp, cmd })
}
fn size() -> usize {
Self::FIXED_PART_SIZE
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum Cmd {
Create = 0x01,
DataFirst = 0x02,
Data = 0x03,
Close = 0x04,
Capability = 0x05,
DataFirstCompressed = 0x06,
DataCompressed = 0x07,
SoftSyncRequest = 0x08,
SoftSyncResponse = 0x09,
}
impl Cmd {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn as_u8(self) -> u8 {
self as u8
}
}
impl TryFrom<u8> for Cmd {
type Error = DecodeError;
fn try_from(byte: u8) -> Result<Self, Self::Error> {
match byte {
0x01 => Ok(Self::Create),
0x02 => Ok(Self::DataFirst),
0x03 => Ok(Self::Data),
0x04 => Ok(Self::Close),
0x05 => Ok(Self::Capability),
0x06 => Ok(Self::DataFirstCompressed),
0x07 => Ok(Self::DataCompressed),
0x08 => Ok(Self::SoftSyncRequest),
0x09 => Ok(Self::SoftSyncResponse),
_ => Err(invalid_field_err!("Cmd", "invalid cmd")),
}
}
}
impl fmt::Display for Cmd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Cmd::Create => "Create",
Cmd::DataFirst => "DataFirst",
Cmd::Data => "Data",
Cmd::Close => "Close",
Cmd::Capability => "Capability",
Cmd::DataFirstCompressed => "DataFirstCompressed",
Cmd::DataCompressed => "DataCompressed",
Cmd::SoftSyncRequest => "SoftSyncRequest",
Cmd::SoftSyncResponse => "SoftSyncResponse",
})
}
}
impl From<Cmd> for String {
fn from(cmd: Cmd) -> Self {
format!("{cmd:?}")
}
}
#[derive(Debug, PartialEq)]
pub struct DataFirstPdu {
header: Header,
channel_id: DynamicChannelId,
length: u32,
data: Vec<u8>,
}
impl DataFirstPdu {
pub fn new(channel_id: DynamicChannelId, total_length: u32, data: Vec<u8>) -> Self {
Self {
header: Header::new(channel_id, total_length, Cmd::DataFirst),
channel_id,
length: total_length,
data,
}
}
#[must_use]
pub fn with_cb_id_type(self, cb_id: FieldType) -> Self {
Self {
header: self.header.with_cb_id(cb_id),
..self
}
}
#[must_use]
pub fn with_sp_type(self, sp: FieldType) -> Self {
Self {
header: self.header.with_sp(sp),
..self
}
}
pub fn length(&self) -> u32 {
self.length
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let fixed_part_size = checked_sum(&[header.cb_id.size_of_val(), header.sp.size_of_val()])?;
ensure_size!(in: src, size: fixed_part_size);
let channel_id = header.cb_id.decode_val(src)?;
let length = header.sp.decode_val(src)?;
let data = src.read_remaining().to_vec();
Ok(Self {
header,
channel_id,
length,
data,
})
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.header.encode(dst)?;
self.header.cb_id.encode_val(self.channel_id, dst)?;
self.header
.sp
.encode_val(cast_length!("DataFirstPdu::Length", self.length)?, dst)?;
dst.write_slice(&self.data);
Ok(())
}
fn name() -> &'static str {
"DYNVC_DATA_FIRST"
}
fn size(&self) -> usize {
strict_sum(&[
Header::size(),
self.header.cb_id.size_of_val(),
self.header.sp.size_of_val(),
self.data.len(),
])
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FieldType(u8);
impl FieldType {
pub const U8: Self = Self(0x00);
pub const U16: Self = Self(0x01);
pub const U32: Self = Self(0x02);
fn encode_val(&self, value: u32, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size_of_val());
match *self {
FieldType::U8 => dst.write_u8(cast_length!("FieldType::encode", value)?),
FieldType::U16 => dst.write_u16(cast_length!("FieldType::encode", value)?),
FieldType::U32 => dst.write_u32(value),
_ => return Err(invalid_field_err!("FieldType", "invalid field type")),
};
Ok(())
}
fn decode_val(&self, src: &mut ReadCursor<'_>) -> DecodeResult<u32> {
ensure_size!(in: src, size: self.size_of_val());
match *self {
FieldType::U8 => Ok(u32::from(src.read_u8())),
FieldType::U16 => Ok(u32::from(src.read_u16())),
FieldType::U32 => Ok(src.read_u32()),
_ => Err(invalid_field_err!("FieldType", "invalid field type")),
}
}
fn size_of_val(&self) -> usize {
match *self {
FieldType::U8 => 1,
FieldType::U16 => 2,
FieldType::U32 => 4,
_ => 0,
}
}
fn for_val(value: u32) -> Self {
if u8::try_from(value).is_ok() {
FieldType::U8
} else if u16::try_from(value).is_ok() {
FieldType::U16
} else {
FieldType::U32
}
}
}
impl From<u8> for FieldType {
fn from(byte: u8) -> Self {
match byte {
0x00 => Self::U8,
0x01 => Self::U16,
0x02 => Self::U32,
_ => Self(byte),
}
}
}
impl From<FieldType> for u8 {
fn from(field_type: FieldType) -> Self {
field_type.0
}
}
#[derive(Debug, PartialEq)]
pub struct DataPdu {
header: Header,
channel_id: DynamicChannelId,
data: Vec<u8>,
}
impl DataPdu {
pub fn new(channel_id: DynamicChannelId, data: Vec<u8>) -> Self {
Self {
header: Header::new(channel_id, 0, Cmd::Data),
channel_id,
data,
}
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
pub fn data_mut(&mut self) -> &mut Vec<u8> {
&mut self.data
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: header.cb_id.size_of_val());
let channel_id = header.cb_id.decode_val(src)?;
let data = src.read_remaining().to_vec();
Ok(Self {
header,
channel_id,
data,
})
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.header.encode(dst)?;
self.header.cb_id.encode_val(self.channel_id, dst)?;
dst.write_slice(&self.data);
Ok(())
}
fn name() -> &'static str {
"DYNVC_DATA"
}
fn size(&self) -> usize {
strict_sum(&[
Header::size(),
self.header.cb_id.size_of_val(), self.data.len(), ])
}
}
#[derive(Debug, PartialEq)]
pub struct CreateResponsePdu {
header: Header,
channel_id: DynamicChannelId,
creation_status: CreationStatus,
}
impl CreateResponsePdu {
pub fn new(channel_id: DynamicChannelId, creation_status: CreationStatus) -> Self {
Self {
header: Header::new(channel_id, 0, Cmd::Create),
channel_id,
creation_status,
}
}
pub fn channel_id(&self) -> DynamicChannelId {
self.channel_id
}
pub fn creation_status(&self) -> CreationStatus {
self.creation_status
}
fn name() -> &'static str {
"DYNVC_CREATE_RSP"
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::headerless_size(&header));
let channel_id = header.cb_id.decode_val(src)?;
let creation_status = CreationStatus(src.read_u32());
Ok(Self {
header,
channel_id,
creation_status,
})
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.header.encode(dst)?;
self.header.cb_id.encode_val(self.channel_id, dst)?;
self.creation_status.encode(dst)?;
Ok(())
}
fn headerless_size(header: &Header) -> usize {
strict_sum(&[
header.cb_id.size_of_val(), CreationStatus::size(), ])
}
fn size(&self) -> usize {
strict_sum(&[Header::size(), Self::headerless_size(&self.header)])
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct CreationStatus(u32);
impl CreationStatus {
pub const OK: Self = Self(0x00000000);
pub const NOT_FOUND: Self = Self(0xC0000225);
pub const NO_LISTENER: Self = Self(0xC0000001);
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_u32(self.0);
Ok(())
}
fn size() -> usize {
4
}
}
impl From<CreationStatus> for u32 {
fn from(val: CreationStatus) -> Self {
val.0
}
}
#[derive(Debug, PartialEq)]
pub struct ClosePdu {
header: Header,
channel_id: DynamicChannelId,
}
impl ClosePdu {
pub fn new(channel_id: DynamicChannelId) -> Self {
Self {
header: Header::new(channel_id, 0, Cmd::Close),
channel_id,
}
}
#[must_use]
pub fn with_cb_id_type(self, cb_id: FieldType) -> Self {
Self {
header: self.header.with_cb_id(cb_id),
..self
}
}
pub fn channel_id(&self) -> DynamicChannelId {
self.channel_id
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::headerless_size(&header));
let channel_id = header.cb_id.decode_val(src)?;
Ok(Self { header, channel_id })
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.header.encode(dst)?;
self.header.cb_id.encode_val(self.channel_id, dst)?;
Ok(())
}
fn name() -> &'static str {
"DYNVC_CLOSE"
}
fn headerless_size(header: &Header) -> usize {
header.cb_id.size_of_val()
}
fn size(&self) -> usize {
strict_sum(&[Header::size(), Self::headerless_size(&self.header)])
}
}
#[derive(Debug, PartialEq)]
pub struct CapabilitiesResponsePdu {
header: Header,
version: CapsVersion,
}
impl CapabilitiesResponsePdu {
const HEADERLESS_FIXED_PART_SIZE: usize = 1 + CapsVersion::FIXED_PART_SIZE ;
const FIXED_PART_SIZE: usize = Header::FIXED_PART_SIZE + Self::HEADERLESS_FIXED_PART_SIZE;
pub fn new(version: CapsVersion) -> Self {
Self {
header: Header::new(0, 0, Cmd::Capability),
version,
}
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::HEADERLESS_FIXED_PART_SIZE);
let _pad = src.read_u8();
let version = CapsVersion::try_from(src.read_u16())?;
Ok(Self { header, version })
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
self.header.encode(dst)?;
dst.write_u8(0x00); self.version.encode(dst)?;
Ok(())
}
fn name() -> &'static str {
"DYNVC_CAPS_RSP"
}
fn size() -> usize {
Self::FIXED_PART_SIZE
}
}
#[repr(u16)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CapsVersion {
V1 = 0x0001,
V2 = 0x0002,
V3 = 0x0003,
}
impl CapsVersion {
const FIXED_PART_SIZE: usize = 2;
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_u16(u16::from(*self));
Ok(())
}
fn size() -> usize {
Self::FIXED_PART_SIZE
}
}
impl TryFrom<u16> for CapsVersion {
type Error = DecodeError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0x0001 => Ok(Self::V1),
0x0002 => Ok(Self::V2),
0x0003 => Ok(Self::V3),
_ => Err(invalid_field_err!("CapsVersion", "invalid version")),
}
}
}
impl From<CapsVersion> for u16 {
#[expect(
clippy::as_conversions,
reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
)]
fn from(version: CapsVersion) -> Self {
version as u16
}
}
#[derive(Debug, PartialEq)]
pub enum CapabilitiesRequestPdu {
V1 {
header: Header,
},
V2 {
header: Header,
charges: [u16; CapabilitiesRequestPdu::PRIORITY_CHARGE_COUNT],
},
V3 {
header: Header,
charges: [u16; CapabilitiesRequestPdu::PRIORITY_CHARGE_COUNT],
},
}
impl CapabilitiesRequestPdu {
const HEADERLESS_FIXED_PART_SIZE: usize = 1 + 2 ;
const FIXED_PART_SIZE: usize = Header::FIXED_PART_SIZE + Self::HEADERLESS_FIXED_PART_SIZE;
const PRIORITY_CHARGE_SIZE: usize = 2; const PRIORITY_CHARGE_COUNT: usize = 4; const PRIORITY_CHARGES_SIZE: usize = Self::PRIORITY_CHARGE_COUNT * Self::PRIORITY_CHARGE_SIZE;
pub fn version(&self) -> CapsVersion {
match self {
Self::V1 { .. } => CapsVersion::V1,
Self::V2 { .. } => CapsVersion::V2,
Self::V3 { .. } => CapsVersion::V3,
}
}
pub fn new(version: CapsVersion, charges: Option<[u16; Self::PRIORITY_CHARGE_COUNT]>) -> Self {
let header = Header::new(0, 0, Cmd::Capability);
let charges = charges.unwrap_or([0; Self::PRIORITY_CHARGE_COUNT]);
match version {
CapsVersion::V1 => Self::V1 { header },
CapsVersion::V2 => Self::V2 { header, charges },
CapsVersion::V3 => Self::V3 { header, charges },
}
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::HEADERLESS_FIXED_PART_SIZE);
let _pad = src.read_u8();
let version = CapsVersion::try_from(src.read_u16())?;
match version {
CapsVersion::V1 => Ok(Self::V1 { header }),
_ => {
ensure_size!(in: src, size: Self::PRIORITY_CHARGES_SIZE);
let mut charges = [0u16; Self::PRIORITY_CHARGE_COUNT];
for charge in charges.iter_mut() {
*charge = src.read_u16();
}
match version {
CapsVersion::V2 => Ok(Self::V2 { header, charges }),
CapsVersion::V3 => Ok(Self::V3 { header, charges }),
_ => unreachable!(),
}
}
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
match self {
CapabilitiesRequestPdu::V1 { header }
| CapabilitiesRequestPdu::V2 { header, .. }
| CapabilitiesRequestPdu::V3 { header, .. } => header.encode(dst)?,
};
dst.write_u8(0x00); match self {
CapabilitiesRequestPdu::V1 { .. } => dst.write_u16(CapsVersion::V1.into()),
CapabilitiesRequestPdu::V2 { .. } => dst.write_u16(CapsVersion::V2.into()),
CapabilitiesRequestPdu::V3 { .. } => dst.write_u16(CapsVersion::V3.into()),
}
match self {
CapabilitiesRequestPdu::V1 { .. } => {}
CapabilitiesRequestPdu::V2 { charges, .. } | CapabilitiesRequestPdu::V3 { charges, .. } => {
for charge in charges.iter() {
dst.write_u16(*charge);
}
}
}
Ok(())
}
fn size(&self) -> usize {
match self {
Self::V1 { .. } => Self::FIXED_PART_SIZE,
_ => Self::FIXED_PART_SIZE + Self::PRIORITY_CHARGES_SIZE,
}
}
fn name(&self) -> &'static str {
match self {
Self::V1 { .. } => "DYNVC_CAPS_VERSION1",
Self::V2 { .. } => "DYNVC_CAPS_VERSION2",
Self::V3 { .. } => "DYNVC_CAPS_VERSION3",
}
}
}
#[derive(Debug, PartialEq)]
pub struct CreateRequestPdu {
header: Header,
channel_id: DynamicChannelId,
channel_name: String,
}
impl CreateRequestPdu {
pub fn new(channel_id: DynamicChannelId, channel_name: String) -> Self {
Self {
header: Header::new(channel_id, 0, Cmd::Create),
channel_id,
channel_name,
}
}
pub fn channel_id(&self) -> DynamicChannelId {
self.channel_id
}
pub fn channel_name(&self) -> &str {
&self.channel_name
}
pub fn into_channel_name(self) -> String {
self.channel_name
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::headerless_fixed_part_size(&header));
let channel_id = header.cb_id.decode_val(src)?;
let channel_name = read_string_from_cursor(src, CharacterSet::Ansi, true)?;
Ok(Self {
header,
channel_id,
channel_name,
})
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.header.encode(dst)?;
self.header.cb_id.encode_val(self.channel_id, dst)?;
write_string_to_cursor(dst, &self.channel_name, CharacterSet::Ansi, true)?;
Ok(())
}
fn name() -> &'static str {
"DYNVC_CREATE_REQ"
}
fn headerless_fixed_part_size(header: &Header) -> usize {
header.cb_id.size_of_val() }
fn size(&self) -> usize {
strict_sum(&[
Header::size(),
Self::headerless_fixed_part_size(&self.header), encoded_str_len(&self.channel_name, CharacterSet::Ansi, true), ])
}
}