mirror of
https://github.com/netbirdio/IronRDP.git
synced 2026-05-22 18:43:12 -07:00
feat!: implement new traits on PreconnectionBlob
This commit is contained in:
committed by
Benoît Cortier
parent
5e9990ce38
commit
95faf8bbc6
@@ -13,6 +13,7 @@ pub mod input;
|
||||
pub mod mcs;
|
||||
pub mod nego;
|
||||
pub mod padding;
|
||||
pub mod pcb;
|
||||
pub mod rdp;
|
||||
pub mod tpdu;
|
||||
pub mod tpkt;
|
||||
@@ -23,10 +24,8 @@ pub(crate) mod basic_output;
|
||||
pub(crate) mod ber;
|
||||
pub(crate) mod crypto;
|
||||
pub(crate) mod per;
|
||||
pub(crate) mod preconnection;
|
||||
|
||||
pub use crate::basic_output::{bitmap, fast_path, surface_commands};
|
||||
pub use crate::preconnection::{PreconnectionPdu, PreconnectionPduError};
|
||||
pub use crate::rdp::vc::dvc;
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
//! This module contains the RDP_PRECONNECTION_PDU_V1 and RDP_PRECONNECTION_PDU_V2 structures.
|
||||
|
||||
use crate::{cursor::ReadCursor, padding::Padding, Error, Pdu, PduDecode, PduEncode, Result};
|
||||
|
||||
/// Preconnection PDU version
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PcbVersion(pub u32);
|
||||
|
||||
impl PcbVersion {
|
||||
pub const V1: Self = Self(0x1);
|
||||
pub const V2: Self = Self(0x2);
|
||||
}
|
||||
|
||||
/// RDP preconnection PDU
|
||||
///
|
||||
/// The RDP_PRECONNECTION_PDU_V1 is used by the client to let the listening process
|
||||
/// know which RDP source the connection is intended for.
|
||||
///
|
||||
/// The RDP_PRECONNECTION_PDU_V2 extends the RDP_PRECONNECTION_PDU_V1 packet by
|
||||
/// adding a variable-size Unicode character string. The receiver of this PDU can
|
||||
/// use this string and the Id field of the RDP_PRECONNECTION_PDU_V1 packet to
|
||||
/// determine the RDP source. This string is opaque to the protocol.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PreconnectionBlob {
|
||||
/// Preconnection PDU version
|
||||
pub version: PcbVersion,
|
||||
/// This field is used to uniquely identify the RDP source. Although the Id can be
|
||||
/// as simple as a process ID, it is often client-specific or server-specific and
|
||||
/// can be obfuscated.
|
||||
pub id: u32,
|
||||
/// V2 PCB string
|
||||
pub v2_payload: Option<String>,
|
||||
}
|
||||
|
||||
impl PreconnectionBlob {
|
||||
pub const FIXED_PART_SIZE: usize = 16;
|
||||
}
|
||||
|
||||
impl Pdu for PreconnectionBlob {
|
||||
const NAME: &'static str = "PreconnectionBlob";
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for PreconnectionBlob {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> Result<Self> {
|
||||
ensure_fixed_part_size!(in: src);
|
||||
|
||||
let pcb_size: usize = cast_length!(src.read_u32(), "cbSize")?;
|
||||
|
||||
if pcb_size < Self::FIXED_PART_SIZE {
|
||||
return Err(Error::InvalidMessage {
|
||||
name: Self::NAME,
|
||||
field: "cbSize",
|
||||
reason: "advertised size too small for Preconnection PDU V1",
|
||||
});
|
||||
}
|
||||
|
||||
Padding::<4>::read(src); // flags
|
||||
|
||||
// The version field SHOULD be initialized by the client and SHOULD be ignored by the server,
|
||||
// as specified in sections 3.1.5.1 and 3.2.5.1.
|
||||
// That’s why, following code doesn’t depend on the value of this field.
|
||||
let version = PcbVersion(src.read_u32());
|
||||
|
||||
let id = src.read_u32();
|
||||
|
||||
let remaining_size = pcb_size - Self::FIXED_PART_SIZE;
|
||||
|
||||
ensure_size!(in: src, size: remaining_size);
|
||||
|
||||
if remaining_size >= 2 {
|
||||
let cch_pcb = usize::from(src.read_u16());
|
||||
let cb_pcb = cch_pcb * 2;
|
||||
|
||||
if remaining_size - 2 < cb_pcb {
|
||||
return Err(Error::InvalidMessage {
|
||||
name: Self::NAME,
|
||||
field: "cchPCB",
|
||||
reason: "PCB string bigger than advertised size",
|
||||
});
|
||||
}
|
||||
|
||||
let wsz_pcb_utf16 = src.read_slice(cb_pcb);
|
||||
|
||||
let mut trimed_pcb_utf16: Vec<u16> = Vec::with_capacity(cch_pcb);
|
||||
|
||||
for chunk in wsz_pcb_utf16.chunks_exact(2) {
|
||||
let code_unit = u16::from_le_bytes([chunk[0], chunk[1]]);
|
||||
|
||||
// Stop reading at the null terminator
|
||||
if code_unit == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
trimed_pcb_utf16.push(code_unit);
|
||||
}
|
||||
|
||||
let payload = String::from_utf16(&trimed_pcb_utf16).map_err(|_| Error::InvalidMessage {
|
||||
name: Self::NAME,
|
||||
field: "wszPCB",
|
||||
reason: "invalid UTF-16",
|
||||
})?;
|
||||
|
||||
let leftover_size = remaining_size - 2 - cb_pcb;
|
||||
src.advance(leftover_size); // Consume (unused) leftover data
|
||||
|
||||
Ok(Self {
|
||||
version,
|
||||
id,
|
||||
v2_payload: Some(payload),
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
version,
|
||||
id,
|
||||
v2_payload: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PduEncode for PreconnectionBlob {
|
||||
fn encode(&self, dst: &mut crate::cursor::WriteCursor<'_>) -> Result<()> {
|
||||
if self.v2_payload.is_some() && self.version == PcbVersion::V1 {
|
||||
return Err(Error::InvalidMessage {
|
||||
name: Self::NAME,
|
||||
field: "version",
|
||||
reason: "there is no string payload in Preconnection PDU V1",
|
||||
});
|
||||
}
|
||||
|
||||
let pcb_size = self.size();
|
||||
|
||||
ensure_size!(in: dst, size: pcb_size);
|
||||
|
||||
dst.write_u32(cast_length!(pcb_size, "cbSize")?); // cbSize
|
||||
Padding::<4>::write(dst); // flags
|
||||
dst.write_u32(self.version.0); // version
|
||||
dst.write_u32(self.id); // id
|
||||
|
||||
if let Some(v2_payload) = &self.v2_payload {
|
||||
// cchPCB
|
||||
let utf16_character_count = v2_payload.encode_utf16().count() + 1; // +1 for null terminator
|
||||
dst.write_u16(cast_length!(utf16_character_count, "cchPCB")?);
|
||||
|
||||
// wszPCB
|
||||
v2_payload.encode_utf16().for_each(|c| dst.write_u16(c));
|
||||
dst.write_u16(0); // null terminator
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
let fixed_part_size = Self::FIXED_PART_SIZE;
|
||||
|
||||
let variable_part = if let Some(v2_payload) = &self.v2_payload {
|
||||
let utf16_character_count = v2_payload.encode_utf16().count() + 1; // +1 for null terminator
|
||||
2 + utf16_character_count * 2
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
fixed_part_size + variable_part
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const PRECONNECTION_PDU_V1_NULL_SIZE_BUF: [u8; 16] = [
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x00 = 0 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x01, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 1
|
||||
0xeb, 0x99, 0xc6, 0xee, // -> RDP_PRECONNECTION_PDU_V1::Id = 0xEEC699EB = 4005992939
|
||||
];
|
||||
const PRECONNECTION_PDU_V1_LARGE_SIZE_BUF: [u8; 16] = [
|
||||
0xff, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0xff
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x01, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 1
|
||||
0xeb, 0x99, 0xc6, 0xee, // -> RDP_PRECONNECTION_PDU_V1::Id = 0xEEC699EB = 4005992939
|
||||
];
|
||||
const PRECONNECTION_PDU_V1_BUF: [u8; 16] = [
|
||||
0x10, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x10 = 16 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x01, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 1
|
||||
0xeb, 0x99, 0xc6, 0xee, // -> RDP_PRECONNECTION_PDU_V1::Id = 0xEEC699EB = 4005992939
|
||||
];
|
||||
const PRECONNECTION_PDU_V2_LARGE_PAYLOAD_SIZE_BUF: [u8; 32] = [
|
||||
0x20, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x20 = 32 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x02, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 2
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Id = 0
|
||||
0xff, 0x00, // -> RDP_PRECONNECTION_PDU_V2::cchPCB = 0xff
|
||||
0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00,
|
||||
0x00, // -> RDP_PRECONNECTION_PDU_V2::wszPCB -> "TestVM\0"
|
||||
];
|
||||
const PRECONNECTION_PDU_V2_BUF: [u8; 32] = [
|
||||
0x20, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x20 = 32 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x02, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 2
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Id = 0
|
||||
0x07, 0x00, // -> RDP_PRECONNECTION_PDU_V2::cchPCB = 0x7 = 7 characters
|
||||
0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00,
|
||||
0x00, // -> RDP_PRECONNECTION_PDU_V2::wszPCB -> "TestVM\0"
|
||||
];
|
||||
|
||||
const PRECONNECTION_PDU_V1: PreconnectionBlob = PreconnectionBlob {
|
||||
version: PcbVersion::V1,
|
||||
id: 4_005_992_939,
|
||||
v2_payload: None,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref PRECONNECTION_PDU_V2: PreconnectionBlob = PreconnectionBlob {
|
||||
version: PcbVersion::V2,
|
||||
id: 0,
|
||||
v2_payload: Some(String::from("TestVM")),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_size() {
|
||||
let e = crate::decode::<PreconnectionBlob>(&PRECONNECTION_PDU_V1_NULL_SIZE_BUF)
|
||||
.err()
|
||||
.unwrap();
|
||||
|
||||
if let Error::InvalidMessage { field, reason, .. } = e {
|
||||
assert_eq!(field, "cbSize");
|
||||
assert_eq!(reason, "advertised size too small for Preconnection PDU V1");
|
||||
} else {
|
||||
panic!("unexpected error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncated() {
|
||||
let e = crate::decode::<PreconnectionBlob>(&PRECONNECTION_PDU_V1_LARGE_SIZE_BUF)
|
||||
.err()
|
||||
.unwrap();
|
||||
|
||||
if let Error::NotEnoughBytes { received, expected, .. } = e {
|
||||
assert_eq!(received, 0);
|
||||
assert_eq!(expected, 239);
|
||||
} else {
|
||||
panic!("unexpected error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_decode() {
|
||||
let pcb = crate::decode::<PreconnectionBlob>(&PRECONNECTION_PDU_V1_BUF).unwrap();
|
||||
assert_eq!(pcb, PRECONNECTION_PDU_V1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_encode() {
|
||||
let mut buf = Vec::new();
|
||||
crate::encode_buf(&PRECONNECTION_PDU_V1, &mut buf).unwrap();
|
||||
assert_eq!(buf, PRECONNECTION_PDU_V1_BUF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_size() {
|
||||
assert_eq!(PRECONNECTION_PDU_V1.size(), PRECONNECTION_PDU_V1_BUF.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_string_too_big() {
|
||||
let e = crate::decode::<PreconnectionBlob>(&PRECONNECTION_PDU_V2_LARGE_PAYLOAD_SIZE_BUF)
|
||||
.err()
|
||||
.unwrap();
|
||||
|
||||
if let Error::InvalidMessage { field, reason, .. } = e {
|
||||
assert_eq!(field, "cchPCB");
|
||||
assert_eq!(reason, "PCB string bigger than advertised size");
|
||||
} else {
|
||||
panic!("unexpected error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_decode() {
|
||||
let pcb = crate::decode::<PreconnectionBlob>(&PRECONNECTION_PDU_V2_BUF).unwrap();
|
||||
assert_eq!(pcb, *PRECONNECTION_PDU_V2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_encode() {
|
||||
let mut buf = Vec::new();
|
||||
crate::encode_buf(&*PRECONNECTION_PDU_V2, &mut buf).unwrap();
|
||||
assert_eq!(buf, PRECONNECTION_PDU_V2_BUF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_size() {
|
||||
assert_eq!(PRECONNECTION_PDU_V2.size(), PRECONNECTION_PDU_V2_BUF.len());
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
use std::io;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt as _, WriteBytesExt as _};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive as _, ToPrimitive as _};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::utils::{self, SplitTo};
|
||||
use crate::PduBufferParsing;
|
||||
|
||||
const PRECONNECTION_PDU_V1_SIZE: usize = 16;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PreconnectionPdu {
|
||||
pub id: u32,
|
||||
pub cch_pcb: u16,
|
||||
pub payload: Option<String>,
|
||||
}
|
||||
|
||||
impl PreconnectionPdu {}
|
||||
|
||||
impl PduBufferParsing<'_> for PreconnectionPdu {
|
||||
type Error = PreconnectionPduError;
|
||||
|
||||
fn from_buffer_consume(buffer: &mut &[u8]) -> Result<Self, Self::Error> {
|
||||
if buffer.len() < PRECONNECTION_PDU_V1_SIZE {
|
||||
return Err(PreconnectionPduError::IoError(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"More data required to parse Preconnection PDU header",
|
||||
)));
|
||||
}
|
||||
|
||||
let size = buffer.read_u32::<LittleEndian>()? as usize;
|
||||
|
||||
if (size % 2 != 0) || (size < PRECONNECTION_PDU_V1_SIZE) {
|
||||
return Err(PreconnectionPduError::InvalidHeader);
|
||||
}
|
||||
|
||||
buffer.read_u32::<LittleEndian>()?; // flags
|
||||
let version = buffer.read_u32::<LittleEndian>()?;
|
||||
let version = Version::from_u32(version).ok_or(PreconnectionPduError::UnexpectedVersion(version))?;
|
||||
|
||||
let id = buffer.read_u32::<LittleEndian>()?;
|
||||
|
||||
let remaining_size = size - PRECONNECTION_PDU_V1_SIZE;
|
||||
|
||||
if buffer.len() < remaining_size {
|
||||
return Err(PreconnectionPduError::IoError(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"More data required to parse Preconnection PDU payload",
|
||||
)));
|
||||
}
|
||||
|
||||
let mut buffer = buffer.split_to(remaining_size);
|
||||
|
||||
let (cch_pcb, payload) = match version {
|
||||
Version::V1 => (0, None),
|
||||
Version::V2 => {
|
||||
let cch_pcb = buffer.read_u16::<LittleEndian>()?;
|
||||
if buffer.len() < usize::from(cch_pcb) * 2 {
|
||||
return Err(PreconnectionPduError::InvalidHeader);
|
||||
}
|
||||
|
||||
let payload_bytes = buffer.split_to(usize::from(cch_pcb) * 2);
|
||||
let payload = utils::from_utf16_bytes(payload_bytes).trim_end_matches('\0').into();
|
||||
|
||||
(cch_pcb, Some(payload))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self { id, cch_pcb, payload })
|
||||
}
|
||||
|
||||
fn to_buffer_consume(&self, buffer: &mut &mut [u8]) -> Result<(), Self::Error> {
|
||||
let size = self.buffer_length();
|
||||
|
||||
buffer.write_u32::<LittleEndian>(size as u32)?;
|
||||
buffer.write_u32::<LittleEndian>(0)?; // flags
|
||||
buffer.write_u32::<LittleEndian>(Version::from(self).to_u32().unwrap())?;
|
||||
buffer.write_u32::<LittleEndian>(self.id)?;
|
||||
|
||||
if let Some(ref payload) = self.payload {
|
||||
buffer.write_u16::<LittleEndian>(payload.len() as u16 + 1)?; // + null terminator
|
||||
utils::write_string_with_null_terminator(buffer, payload.as_str(), utils::CharacterSet::Unicode)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_length(&self) -> usize {
|
||||
let cch_pcb = if self.cch_pcb > 0 {
|
||||
self.cch_pcb as usize
|
||||
} else {
|
||||
self.payload.as_ref().map(|p| (p.len() + 1)).unwrap_or(0)
|
||||
};
|
||||
|
||||
let version: Version = self.into();
|
||||
|
||||
match version {
|
||||
Version::V1 => PRECONNECTION_PDU_V1_SIZE,
|
||||
Version::V2 => PRECONNECTION_PDU_V1_SIZE + 2 + (cch_pcb * 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
enum Version {
|
||||
V1 = 0x1,
|
||||
V2 = 0x2,
|
||||
}
|
||||
|
||||
impl From<&PreconnectionPdu> for Version {
|
||||
fn from(p: &PreconnectionPdu) -> Self {
|
||||
if p.payload.is_some() {
|
||||
Self::V2
|
||||
} else {
|
||||
Self::V1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PreconnectionPduError {
|
||||
#[error("IO error")]
|
||||
IoError(#[from] io::Error),
|
||||
#[error("Provided data is not an valid preconnection Pdu")]
|
||||
InvalidHeader,
|
||||
#[error("Unexpected version: {0}")]
|
||||
UnexpectedVersion(u32),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use super::*;
|
||||
|
||||
const PRECONNECTION_PDU_V1_EMPTY_SIZE_BUFFER: [u8; 16] = [
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x00 = 0 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x01, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 1
|
||||
0xeb, 0x99, 0xc6, 0xee, // -> RDP_PRECONNECTION_PDU_V1::Id = 0xEEC699EB = 4005992939
|
||||
];
|
||||
const PRECONNECTION_PDU_V1_LARGE_DATA_LENGTH_BUFFER: [u8; 16] = [
|
||||
0xff, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0xff
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x01, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 1
|
||||
0xeb, 0x99, 0xc6, 0xee, // -> RDP_PRECONNECTION_PDU_V1::Id = 0xEEC699EB = 4005992939
|
||||
];
|
||||
const PRECONNECTION_PDU_V1_BUFFER: [u8; 16] = [
|
||||
0x10, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x10 = 16 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x01, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 1
|
||||
0xeb, 0x99, 0xc6, 0xee, // -> RDP_PRECONNECTION_PDU_V1::Id = 0xEEC699EB = 4005992939
|
||||
];
|
||||
const PRECONNECTION_PDU_V2_LARGE_PAYLOAD_SIZE_BUFFER: [u8; 32] = [
|
||||
0x20, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x20 = 32 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x02, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 2
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Id = 0
|
||||
0xff, 0x00, // -> RDP_PRECONNECTION_PDU_V2::cchPCB = 0xff
|
||||
0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00,
|
||||
0x00, // -> RDP_PRECONNECTION_PDU_V2::wszPCB -> "TestVM" (including null terminator)
|
||||
];
|
||||
const PRECONNECTION_PDU_V2_BUFFER: [u8; 32] = [
|
||||
0x20, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::cbSize = 0x20 = 32 bytes
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Flags = 0
|
||||
0x02, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Version = 2
|
||||
0x00, 0x00, 0x00, 0x00, // -> RDP_PRECONNECTION_PDU_V1::Id = 0
|
||||
0x07, 0x00, // -> RDP_PRECONNECTION_PDU_V2::cchPCB = 0x7 = 7 characters
|
||||
0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00,
|
||||
0x00, // -> RDP_PRECONNECTION_PDU_V2::wszPCB -> "TestVM" (including null terminator)
|
||||
];
|
||||
|
||||
const PRECONNECTION_PDU_V1: PreconnectionPdu = PreconnectionPdu {
|
||||
id: 4_005_992_939,
|
||||
cch_pcb: 0,
|
||||
payload: None,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref PRECONNECTION_PDU_V2: PreconnectionPdu = PreconnectionPdu {
|
||||
id: 0,
|
||||
cch_pcb: 7,
|
||||
payload: Some(String::from("TestVM")),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_for_preconnection_pdu_returns_error_on_empty_size() {
|
||||
assert!(PreconnectionPdu::from_buffer(PRECONNECTION_PDU_V1_EMPTY_SIZE_BUFFER.as_ref()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_for_preconnection_pdu_returns_error_on_data_length_greater_then_available_data() {
|
||||
assert!(PreconnectionPdu::from_buffer(PRECONNECTION_PDU_V1_LARGE_DATA_LENGTH_BUFFER.as_ref()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_correctly_parses_preconnection_pdu_v1() {
|
||||
assert_eq!(
|
||||
PRECONNECTION_PDU_V1,
|
||||
PreconnectionPdu::from_buffer(PRECONNECTION_PDU_V1_BUFFER.as_ref()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_buffer_correctly_serializes_preconnection_pdu_v1() {
|
||||
let expected = PRECONNECTION_PDU_V1_BUFFER.as_ref();
|
||||
let mut buffer = vec![0; expected.len()];
|
||||
|
||||
PRECONNECTION_PDU_V1
|
||||
.to_buffer_consume(&mut buffer.as_mut_slice())
|
||||
.unwrap();
|
||||
assert_eq!(expected, buffer.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_length_is_correct_for_preconnection_pdu_v1() {
|
||||
assert_eq!(PRECONNECTION_PDU_V1_BUFFER.len(), PRECONNECTION_PDU_V1.buffer_length());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_for_preconnection_pdu_v2_returns_error_on_payload_size_greater_then_available_data() {
|
||||
assert!(PreconnectionPdu::from_buffer(PRECONNECTION_PDU_V2_LARGE_PAYLOAD_SIZE_BUFFER.as_ref()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_correctly_parses_preconnection_pdu_v2() {
|
||||
assert_eq!(
|
||||
*PRECONNECTION_PDU_V2,
|
||||
PreconnectionPdu::from_buffer(PRECONNECTION_PDU_V2_BUFFER.as_ref()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_buffer_correctly_serializes_preconnection_pdu_v2() {
|
||||
let expected = PRECONNECTION_PDU_V2_BUFFER.as_ref();
|
||||
let mut buffer = vec![0; expected.len()];
|
||||
|
||||
PRECONNECTION_PDU_V2
|
||||
.to_buffer_consume(&mut buffer.as_mut_slice())
|
||||
.unwrap();
|
||||
assert_eq!(expected, buffer.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_length_is_correct_for_preconnection_pdu_v2() {
|
||||
assert_eq!(PRECONNECTION_PDU_V2_BUFFER.len(), PRECONNECTION_PDU_V2.buffer_length());
|
||||
}
|
||||
}
|
||||
Generated
+86
-66
@@ -24,7 +24,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -56,6 +55,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
@@ -110,6 +115,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
@@ -130,10 +141,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.3.3"
|
||||
name = "der"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"der_derive",
|
||||
"flagset",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
@@ -144,11 +161,22 @@ dependencies = [
|
||||
"asn1-rs",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der_derive"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "114792ba6b7545d3f3dd693794aed3a312a67795cd577fcc725c148d84fabe32"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.2.3"
|
||||
@@ -181,6 +209,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flagset"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
@@ -238,15 +272,9 @@ dependencies = [
|
||||
"sha1",
|
||||
"tap",
|
||||
"thiserror",
|
||||
"x509-parser",
|
||||
"x509-cert",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.26"
|
||||
@@ -351,21 +379,36 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oid-registry"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
@@ -399,12 +442,6 @@ dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.5"
|
||||
@@ -416,6 +453,16 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@@ -476,33 +523,6 @@ dependencies = [
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
@@ -537,18 +557,18 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x509-parser"
|
||||
version = "0.15.0"
|
||||
name = "x509-cert"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab0c2f54ae1d92f4fcb99c0b7ccf0b1e3451cbd395e5f115ccbdbcb18d4f634"
|
||||
checksum = "0103e822c47e037cb45b34873a31e33181dc4db3a97123b2ecce49c6d4081bab"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"data-encoding",
|
||||
"der-parser",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"oid-registry",
|
||||
"rusticata-macros",
|
||||
"thiserror",
|
||||
"time",
|
||||
"const-oid",
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
||||
|
||||
@@ -15,7 +15,7 @@ fuzz_target!(|data: &[u8]| {
|
||||
let _ = ClientInfoPdu::from_buffer(data);
|
||||
let _ = capability_sets::CapabilitySet::from_buffer(data);
|
||||
let _ = headers::ShareControlHeader::from_buffer(data);
|
||||
let _ = PreconnectionPdu::from_buffer(data);
|
||||
let _ = decode::<pcb::PreconnectionBlob>(data);
|
||||
let _ = server_error_info::ServerSetErrorInfoPdu::from_buffer(data);
|
||||
|
||||
let _ = gcc::ClientGccBlocks::from_buffer(data);
|
||||
|
||||
Reference in New Issue
Block a user