use model::link::{Link};
use model::data::{Message, U16, Component, Trame};
use model::error::{RdpResult, RdpError, RdpErrorKind, Error};
use std::io::{Cursor, Write, Read};
use nla::cssp::cssp_connect;
use nla::sspi::AuthenticationProtocol;
pub enum Payload {
Raw(Cursor<Vec<u8>>),
FastPath(u8, Cursor<Vec<u8>>)
}
#[derive(Copy, Clone)]
pub enum Action {
FastPathActionFastPath = 0x0,
FastPathActionX224 = 0x3
}
fn tpkt_header(size: u16) -> Component {
component![
"action" => Action::FastPathActionX224 as u8,
"flag" => 0 as u8,
"size" => U16::BE(size + 4)
]
}
pub struct Client<S> {
transport: Link<S>
}
impl<S: Read + Write> Client<S> {
pub fn new (transport: Link<S>) -> Self {
Client {
transport
}
}
pub fn write<T: 'static>(&mut self, message: T) -> RdpResult<()>
where T: Message {
self.transport.write(
&trame![
tpkt_header(message.length() as u16),
message
]
)
}
pub fn read(&mut self) -> RdpResult<Payload> {
let mut buffer = Cursor::new(self.transport.read(2)?);
let mut action: u8 = 0;
action.read(&mut buffer)?;
if action == Action::FastPathActionX224 as u8 {
let mut padding: u8 = 0;
padding.read(&mut buffer)?;
buffer = Cursor::new(self.transport.read(2)?);
let mut size = U16::BE(0);
size.read(&mut buffer)?;
Ok(Payload::Raw(Cursor::new(self.transport.read(size.inner() as usize - 4)?)))
} else {
let sec_flag = (action >> 6) & 0x3;
let mut short_length: u8 = 0;
short_length.read(&mut buffer)?;
if short_length & 0x80 != 0 {
let mut hi_length: u8 = 0;
hi_length.read(&mut Cursor::new(self.transport.read(1)?))?;
let length :u16 = ((short_length & !0x80) as u16) << 8;
let length = length | hi_length as u16;
Ok(Payload::FastPath(sec_flag, Cursor::new(self.transport.read(length as usize - 3)?)))
}
else {
Ok(Payload::FastPath(sec_flag, Cursor::new(self.transport.read(short_length as usize - 2)?)))
}
}
}
pub fn start_ssl(self, check_certificate: bool) -> RdpResult<Client<S>> {
Ok(Client::new(self.transport.start_ssl(check_certificate)?))
}
pub fn start_nla(self, check_certificate: bool, authentication_protocol: &mut dyn AuthenticationProtocol, restricted_admin_mode: bool) -> RdpResult<Client<S>> {
let mut link = self.transport.start_ssl(check_certificate)?;
cssp_connect(&mut link, authentication_protocol, restricted_admin_mode)?;
Ok(Client::new(link))
}
pub fn shutdown(&mut self) -> RdpResult<()> {
self.transport.shutdown()
}
#[cfg(feature = "integration")]
pub fn get_link(self) -> Link<S> {
self.transport
}
}
#[cfg(test)]
mod test {
use super::*;
use std::io::Cursor;
use model::data::{U32, DataType};
#[test]
fn test_write_tpkt_header() {
let x = U32::BE(1);
let message = trame![
tpkt_header(x.length() as u16),
x
];
let mut buffer = Cursor::new(Vec::new());
message.write(&mut buffer).unwrap();
assert_eq!(buffer.get_ref().as_slice(), [3, 0, 0, 8, 0, 0, 0, 1]);
}
#[test]
fn test_read_tpkt_header() {
let mut message = tpkt_header(0);
let mut buffer = Cursor::new([3, 0, 0, 8, 0, 0, 0, 1]);
message.read(&mut buffer).unwrap();
assert_eq!(cast!(DataType::U16, message["size"]).unwrap(), 8);
assert_eq!(cast!(DataType::U8, message["action"]).unwrap(), Action::FastPathActionX224 as u8);
}
}