Skip to content

Commit

Permalink
Improve server-side API
Browse files Browse the repository at this point in the history
  • Loading branch information
proton-decay committed Jun 18, 2017
1 parent 7e4453b commit b6922a5
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Event {
},
protocol::S2C::FramebufferUpdate { count } => {
for _ in 0..count {
let rectangle = try!(protocol::Rectangle::read_from(&mut stream));
let rectangle = try!(protocol::RectangleHeader::read_from(&mut stream));
debug!("<- {:?}", rectangle);

let dst = Rect {
Expand Down
16 changes: 15 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub mod server;
pub use protocol::{PixelFormat, Colour, Encoding};
pub use client::Client;
pub use proxy::Proxy;
pub use server::Server;
pub use server::{Server, Rectangle};

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Rect {
Expand All @@ -29,10 +29,23 @@ pub struct Rect {
pub height: u16
}

impl Rect {
/// Constructs new zero-sized `Rect` placed at (0, 0).
pub fn new_empty() -> Self {
Rect {
left: 0,
top: 0,
width: 0,
height: 0,
}
}
}

#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Unexpected(&'static str),
InconsistentData(String),
Server(String),
AuthenticationUnavailable,
AuthenticationFailure(String),
Expand All @@ -59,6 +72,7 @@ impl std::error::Error for Error {
match self {
&Error::Io(ref inner) => inner.description(),
&Error::Unexpected(_) => "unexpected value",
&Error::InconsistentData(_) => "atempt to send inconsistent data",
&Error::Server(_) => "server error",
&Error::AuthenticationUnavailable => "authentication unavailable",
&Error::AuthenticationFailure(_) => "authentication failure",
Expand Down
10 changes: 5 additions & 5 deletions src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,17 +513,17 @@ impl Message for C2S {
}

#[derive(Debug)]
pub struct Rectangle {
pub struct RectangleHeader {
pub x_position: u16,
pub y_position: u16,
pub width: u16,
pub height: u16,
pub encoding: Encoding,
}

impl Message for Rectangle {
fn read_from<R: Read>(reader: &mut R) -> Result<Rectangle> {
Ok(Rectangle {
impl Message for RectangleHeader {
fn read_from<R: Read>(reader: &mut R) -> Result<RectangleHeader> {
Ok(RectangleHeader {
x_position: try!(reader.read_u16::<BigEndian>()),
y_position: try!(reader.read_u16::<BigEndian>()),
width: try!(reader.read_u16::<BigEndian>()),
Expand Down Expand Up @@ -571,7 +571,7 @@ pub enum S2C {
// core spec
FramebufferUpdate {
count: u16,
/* Vec<Rectangle> has to be read out manually */
// Vec<RectangleHeader> has to be read out manually
},
SetColourMapEntries {
first_colour: u16,
Expand Down
4 changes: 2 additions & 2 deletions src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ impl Proxy {
match message {
protocol::S2C::FramebufferUpdate { count } => {
for _ in 0..count {
let rectangle = try!(protocol::Rectangle::read_from(server_stream));
let rectangle = try!(protocol::RectangleHeader::read_from(server_stream));
debug!("c<-s {:?}", rectangle);
try!(protocol::Rectangle::write_to(&rectangle, &mut buffer_stream));
try!(protocol::RectangleHeader::write_to(&rectangle, &mut buffer_stream));

match rectangle.encoding {
protocol::Encoding::Raw => {
Expand Down
163 changes: 122 additions & 41 deletions src/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io::Write;
use std::net::{TcpStream, Shutdown};
use ::{protocol, Result};
use byteorder::{BigEndian, WriteBytesExt};
use ::{protocol, Rect, Error, Result};
use protocol::Message;

/// Definitions of events received by server from client.
Expand Down Expand Up @@ -46,10 +47,7 @@ pub enum Event {
/// `FramebufferUpdate`.
FramebufferUpdateRequest {
incremental: bool,
x_position: u16,
y_position: u16,
width: u16,
height: u16,
rect: Rect,
},

/// A `KeyEvent` message indicates a key press or release. `down` flag is `true` if the key is
Expand Down Expand Up @@ -85,12 +83,12 @@ pub enum Event {

/// This encoding allows the client to send an extended key event containing a keycode, in
/// addition to a keysym. The advantage of providing the keycode is that it enables the server
/// to interpret the key event independantly of the clients’ locale specific keymap. This can
/// to interpret the key event independently of the clients’ locale specific keymap. This can
/// be important for virtual desktops whose key input device requires scancodes, for example,
/// virtual machines emulating a PS/2 keycode. Prior to this extension, RFB servers for such
/// virtualization software would have to be configured with a keymap matching the client. With
/// this extension it is sufficient for the guest operating system to be configured with the
/// matching keymap. The VNC server is keymap independant.
/// matching keymap. The VNC server is keymap independent.
///
/// The `keysym` and `down`-flag fields also take the same values as described for the KeyEvent
/// message. The keycode is the XT keycode that produced the keysym.
Expand All @@ -101,9 +99,93 @@ pub enum Event {
},
}

/// Data structure containing data to be sent by server in messages containing rectangles.
#[derive(Debug)]
pub enum Rectangle<'a> {
/// Raw pixel data.
Raw { rect: Rect, pixel_data: &'a [u8] },

/// Rectangle in pixel data owned by client to copy data from.
CopyRect { src: Rect, dst: Rect },

/// Compressed pixel data.
Zrle { rect: Rect, zlib_data: &'a [u8] },

/// Data for drawing cursor.
SetCursor { size: (u16, u16), hotspot: (u16, u16), pixels: &'a [u8], mask_bits: &'a [u8] },

/// New size of framebuffer.
DesktopSize { width: u16, height: u16 },

/// Confirmation of support of pseudo-encoding.
Encoding { encoding: protocol::Encoding },
}

impl<'a> Rectangle<'a> {
fn check(&self, bytes_per_pixel: u16) -> Result<()> {
match *self {
Rectangle::Raw { ref rect, pixel_data } => {
let expected_num_bytes = rect.width as usize *
rect.height as usize *
bytes_per_pixel as usize;
if expected_num_bytes == pixel_data.len() {
Ok(())
} else {
let msg = format!("Expected data length for rectangle {:?} \
is {} while given {}",
rect,
expected_num_bytes,
pixel_data.len());
Err(Error::InconsistentData(msg))
}
}
Rectangle::Encoding { encoding: _ } => {
// No check is needed
Ok(())
}
_ => {
// XXX
Ok(())
}
}
}

fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
match *self {
Rectangle::Raw { ref rect, pixel_data } => {
try!(Self::write_rect_to(rect, writer));
try!(protocol::Encoding::Raw.write_to(writer));
try!(writer.write_all(pixel_data));
Ok(())
}
Rectangle::Encoding { encoding } => {
try!(Self::write_rect_to(&Rect::new_empty(), writer));
try!(encoding.write_to(writer));
Ok(())
}
_ => {
// XXX
Ok(())
}
}
}

fn write_rect_to<W: Write>(rect: &Rect, writer: &mut W) -> Result<()> {
try!(writer.write_u16::<BigEndian>(rect.left));
try!(writer.write_u16::<BigEndian>(rect.top));
try!(writer.write_u16::<BigEndian>(rect.width));
try!(writer.write_u16::<BigEndian>(rect.height));
Ok(())
}
}

/// This structure provides basic server-side functionality of RDP protocol.
pub struct Server {
/// The TCP stream.
stream: TcpStream,

/// Number of bytes per pixel used to check validity of sent data extracted from `PixelFormat`.
bytes_per_pixel: u16,
}

impl Server {
Expand Down Expand Up @@ -153,7 +235,10 @@ impl Server {

try!(server_init.write_to(&mut stream));

Ok((Server { stream: stream }, client_init.shared))
Ok((Server {
stream: stream,
bytes_per_pixel: (pixel_format.bits_per_pixel as u16 + 7) / 8,
}, client_init.shared))
}

/// Reads the socket and returns received event.
Expand All @@ -162,6 +247,9 @@ impl Server {
Ok(package) => {
match package {
protocol::C2S::SetPixelFormat(pixel_format) => {
// Update bytes per pixel number. Server must obey this message and from
// now send data in format requested by client.
self.bytes_per_pixel = (pixel_format.bits_per_pixel as u16 + 7) / 8;
Ok(Event::SetPixelFormat(pixel_format))
}
protocol::C2S::SetEncodings(encodings) => {
Expand All @@ -176,10 +264,12 @@ impl Server {
} => {
Ok(Event::FramebufferUpdateRequest {
incremental,
x_position,
y_position,
width,
height,
rect: Rect {
left: x_position,
top: y_position,
width: width,
height: height,
},
})
}
protocol::C2S::KeyEvent { down, key } => {
Expand All @@ -200,40 +290,31 @@ impl Server {
}
}

/// Sends header of `FramebufferUpdate` message containing number of rectangles to be sent.
/// Checks validity of passed data and sends `FramebufferUpdate` message.
///
/// Call to this method must be followed by `count` calls to `send_rectangle_header`.
pub fn send_framebuffer_update_header(&mut self, count: u16) -> Result<()> {
try!(protocol::S2C::FramebufferUpdate{count}.write_to(&mut self.stream));
Ok(())
}
/// This is all-or-nothing method. Rectangles will be sent only if all rectangles are valid.
pub fn send_framebuffer_update(&mut self, rectangles: &[Rectangle]) -> Result<()> {
// Check validity
for rectangle in rectangles {
try!(rectangle.check(self.bytes_per_pixel));
}

/// Sends rectangle header.
///
/// The rectangle header must be followed by the pixel data in the specified encoding.
pub fn send_rectangle_header(&mut self,
x: u16,
y: u16,
width: u16,
height: u16,
encoding: protocol::Encoding)
-> Result<()> {
try!(protocol::Rectangle {
x_position: x,
y_position: y,
width: width,
height: height,
encoding: encoding,
}.write_to(&mut self.stream));
// Send the message
let count = rectangles.len() as u16;
try!(protocol::S2C::FramebufferUpdate{count}.write_to(&mut self.stream));
for rectangle in rectangles {
try!(rectangle.write_to(&mut self.stream));
}
Ok(())
}

/// Writes raw data to the socket.
///
/// This method may be used to send pixel data following rectangle header sent by
/// `send_rectangle_header` method.
pub fn send_raw_data(&mut self, data: &[u8]) -> Result<()> {
try!(self.stream.write_all(data));
/// Sends `FramebufferUpdate` message without checking validity of its contents.
pub fn send_framebuffer_update_unchecked(&mut self, rectangles: &[Rectangle]) -> Result<()> {
let count = rectangles.len() as u16;
try!(protocol::S2C::FramebufferUpdate{count}.write_to(&mut self.stream));
for rectangle in rectangles {
try!(rectangle.write_to(&mut self.stream));
}
Ok(())
}

Expand Down

0 comments on commit b6922a5

Please sign in to comment.