From c266486691697bd16fa6f31e676e313b8330de15 Mon Sep 17 00:00:00 2001 From: Wojciech Kluczka Date: Sun, 18 Jun 2017 10:10:59 +0200 Subject: [PATCH 1/4] Provide support for ExtendedKeyEvent in protocol module --- src/protocol.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/protocol.rs b/src/protocol.rs index 84c2a47..09407ce 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -330,7 +330,9 @@ pub enum Encoding { Zrle, Cursor, DesktopSize, + // extensions + ExtendedKeyEvent, } impl Message for Encoding { @@ -344,6 +346,7 @@ impl Message for Encoding { 16 => Ok(Encoding::Zrle), -239 => Ok(Encoding::Cursor), -223 => Ok(Encoding::DesktopSize), + -258 => Ok(Encoding::ExtendedKeyEvent), n => Ok(Encoding::Unknown(n)) } } @@ -357,6 +360,7 @@ impl Message for Encoding { &Encoding::Zrle => 16, &Encoding::Cursor => -239, &Encoding::DesktopSize => -223, + &Encoding::ExtendedKeyEvent => -258, &Encoding::Unknown(n) => n }; try!(writer.write_i32::(encoding)); @@ -386,7 +390,13 @@ pub enum C2S { y_position: u16 }, CutText(String), + // extensions + ExtendedKeyEvent { + down: bool, + keysym: u32, + keycode: u32, + }, } impl Message for C2S { @@ -437,6 +447,18 @@ impl Message for C2S { try!(reader.read_exact(&mut [0u8; 3])); Ok(C2S::CutText(try!(String::read_from(reader)))) }, + 255 => { + let submessage_type = try!(reader.read_u8()); + match submessage_type { + 0 => { + let down = try!(reader.read_u16::()) != 0; + let keysym = try!(reader.read_u32::()); + let keycode = try!(reader.read_u32::()); + Ok(C2S::ExtendedKeyEvent { down: down, keysym: keysym, keycode: keycode }) + } + _ => Err(Error::Unexpected("server to client QEMU submessage type")) + } + } _ => Err(Error::Unexpected("client to server message type")) } } @@ -478,6 +500,13 @@ impl Message for C2S { &C2S::CutText(ref text) => { try!(String::write_to(text, writer)); } + &C2S::ExtendedKeyEvent { down, keysym, keycode } => { + try!(writer.write_u8(255)); + try!(writer.write_u8(0)); + try!(writer.write_u16::(if down { 1 } else { 0 })); + try!(writer.write_u32::(keysym)); + try!(writer.write_u32::(keycode)); + } } Ok(()) } From 7e4453b40d25cc5effd7e43de4b3a1188097de3d Mon Sep 17 00:00:00 2001 From: Wojciech Kluczka Date: Sun, 18 Jun 2017 10:12:46 +0200 Subject: [PATCH 2/4] Add basic support for server-side --- src/lib.rs | 2 + src/server.rs | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 src/server.rs diff --git a/src/lib.rs b/src/lib.rs index 55fe70b..75e2ce0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,12 @@ mod security; pub mod client; pub mod proxy; +pub mod server; pub use protocol::{PixelFormat, Colour, Encoding}; pub use client::Client; pub use proxy::Proxy; +pub use server::Server; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Rect { diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..b190f01 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,245 @@ +use std::io::Write; +use std::net::{TcpStream, Shutdown}; +use ::{protocol, Result}; +use protocol::Message; + +/// Definitions of events received by server from client. +#[derive(Debug)] +pub enum Event { + /// A `SetPixelFormat` message sets the format in which pixel values should be sent in + /// `FramebufferUpdate` messages. If the client does not send a `SetPixelFormat` message, then + /// the server sends pixel values in its natural format as specified in the ServerInit message. + SetPixelFormat(protocol::PixelFormat), + + /// A `SetEncodings` message sets the encoding types in which pixel data can be sent by the + /// server. The order of the encoding types given in this message is a hint by the client as to + /// its preference (the first encoding specified being most preferred). The server may or may + /// not choose to make use of this hint. Pixel data may always be sent in raw encoding even if + /// not specified explicitly here. + /// + /// In addition to genuine encodings, a client can request "pseudo-encodings" to declare to the + /// server that it supports certain extensions to the protocol. A server that does not support + /// the extension will simply ignore the pseudo-encoding. Note that this means the client must + /// assume that the server does not support the extension until it gets some extension-specific + /// confirmation from the server. + SetEncodings(Vec), + + /// A `FramebufferUpdateRequest` message notifies the server that the client is interested in + /// the area of the framebuffer specified by `x_position`, `y_position`, `width` and `height`. + /// The server usually responds to a `FramebufferUpdateRequest` by sending a + /// `FramebufferUpdate`. A single `FramebufferUpdate` may be sent in reply to several + /// `FramebufferUpdateRequests`. + /// + /// The server assumes that the client keeps a copy of all parts of the framebuffer in which it + /// is interested. This means that normally the server only needs to send incremental updates to + /// the client. + /// + /// If the client has lost the contents of a particular area that it needs, then the client + /// sends a FramebufferUpdateRequest with incremental set to false. This requests that the + /// server send the entire contents of the specified area as soon as possible. The area will not + /// be updated using the `CopyRect` encoding. + /// + /// If the client has not lost any contents of the area in which it is interested, then it sends + /// a `FramebufferUpdateRequest` with incremental set to `true`. If and when there are changes + /// to the specified area of the framebuffer, the server will send a `FramebufferUpdate`. Note + /// that there may be an indefinite period between the `FramebufferUpdateRequest` and the + /// `FramebufferUpdate`. + FramebufferUpdateRequest { + incremental: bool, + x_position: u16, + y_position: u16, + width: u16, + height: u16, + }, + + /// A `KeyEvent` message indicates a key press or release. `down` flag is `true` if the key is + /// now pressed, and false if it is now released. The key itself is specified using the "keysym" + /// values defined by the X Window System, even if the client or server is not running the X + /// Window System. + KeyEvent { + down: bool, + key: u32, + }, + + /// A `PointerEvent` message indicates either pointer movement or a pointer button press or + /// release. The pointer is now at (`x_position`, `y_position`), and the current state of + /// buttons 1 to 8 are represented by bits 0 to 7 of button-mask, respectively; 0 means up, 1 + /// means down (pressed). + /// + /// On a conventional mouse, buttons 1, 2, and 3 correspond to the left, middle, and right + /// buttons on the mouse. On a wheel mouse, each step of the wheel upwards is represented by a + /// press and release of button 4, and each step downwards is represented by a press and release + /// of button 5. + PointerEvent { + button_mask: u8, + x_position: u16, + y_position: u16 + }, + + /// RFB provides limited support for synchronizing the "cut buffer" of selected text between + /// client and server. This message tells the server that the client has new ISO 8859-1 + /// (Latin-1) text in its cut buffer. Ends of lines are represented by the newline character + /// (hex 0a) alone. No carriage-return (hex 0d) is used. There is no way to transfer text + /// outside the Latin-1 character set. + CutText(String), + + /// 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 + /// 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. + /// + /// 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. + ExtendedKeyEvent { + down: bool, + keysym: u32, + keycode: u32, + }, +} + +/// This structure provides basic server-side functionality of RDP protocol. +pub struct Server { + stream: TcpStream, +} + +impl Server { + /// Constructs new `Server`. + /// + /// Returns new `Server` instance and `shared` flag. + /// + /// `shared` flag is `true` if the server should try to share the desktop by leaving other + /// clients connected, and `false` if it should give exclusive access to this client by + /// disconnecting all other clients. + pub fn from_tcp_stream(mut stream: TcpStream, + width: u16, + height: u16, + pixel_format: protocol::PixelFormat, + name: String) + -> Result<(Server, bool)> { + // Start version handshake - send highest supported version. Client may respond with lower + // version but never higher. + try!(protocol::Version::Rfb38.write_to(&mut stream)); + let version = try!(protocol::Version::read_from(&mut stream)); + + // Start security handshake. + // TODO: Add support for more security types and handle errors if negotiations fail. + match version { + protocol::Version::Rfb33 => { + try!(protocol::SecurityType::None.write_to(&mut stream)); + } + _ => { + let security_types = vec![protocol::SecurityType::None]; + try!(protocol::SecurityTypes(security_types).write_to(&mut stream)); + } + } + + let _security_type = try!(protocol::SecurityType::read_from(&mut stream)); + try!(protocol::SecurityResult::Succeeded.write_to(&mut stream)); + + // Wait for client init message + let client_init = try!(protocol::ClientInit::read_from(&mut stream)); + + // Send server init message + let server_init = protocol::ServerInit { + framebuffer_width: width, + framebuffer_height: height, + pixel_format: pixel_format, + name: name, + }; + + try!(server_init.write_to(&mut stream)); + + Ok((Server { stream: stream }, client_init.shared)) + } + + /// Reads the socket and returns received event. + pub fn read_event(&mut self) -> Result { + match protocol::C2S::read_from(&mut self.stream) { + Ok(package) => { + match package { + protocol::C2S::SetPixelFormat(pixel_format) => { + Ok(Event::SetPixelFormat(pixel_format)) + } + protocol::C2S::SetEncodings(encodings) => { + Ok(Event::SetEncodings(encodings)) + } + protocol::C2S::FramebufferUpdateRequest { + incremental, + x_position, + y_position, + width, + height, + } => { + Ok(Event::FramebufferUpdateRequest { + incremental, + x_position, + y_position, + width, + height, + }) + } + protocol::C2S::KeyEvent { down, key } => { + Ok(Event::KeyEvent { down, key }) + } + protocol::C2S::PointerEvent { button_mask, x_position, y_position } => { + Ok(Event::PointerEvent { button_mask, x_position, y_position }) + } + protocol::C2S::CutText(clipboard) => { + Ok(Event::CutText(clipboard)) + } + protocol::C2S::ExtendedKeyEvent { down, keysym, keycode } => { + Ok(Event::ExtendedKeyEvent { down, keysym, keycode }) + } + } + } + Err(error) => Err(error) + } + } + + /// Sends header of `FramebufferUpdate` message containing number of rectangles to be sent. + /// + /// 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(()) + } + + /// 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)); + 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)); + Ok(()) + } + + /// Shuts down communication over TCP stream in both directions. + pub fn disconnect(self) -> Result<()> { + try!(self.stream.shutdown(Shutdown::Both)); + Ok(()) + } +} From 9637b433c0875a42d0f42228b35e701a7021cb44 Mon Sep 17 00:00:00 2001 From: Wojciech Kluczka Date: Sun, 18 Jun 2017 10:12:46 +0200 Subject: [PATCH 3/4] Improve server-side API --- src/client.rs | 2 +- src/lib.rs | 34 ++++ src/protocol.rs | 44 ++++- src/proxy.rs | 4 +- src/server.rs | 432 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 464 insertions(+), 52 deletions(-) diff --git a/src/client.rs b/src/client.rs index e980796..2a7e2ef 100644 --- a/src/client.rs +++ b/src/client.rs @@ -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 { diff --git a/src/lib.rs b/src/lib.rs index 75e2ce0..f8aa278 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,9 @@ extern crate octavo; #[cfg(feature = "apple-auth")] extern crate crypto; +use std::io::Write; +use byteorder::{BigEndian, WriteBytesExt}; + mod protocol; mod zrle; mod security; @@ -29,6 +32,37 @@ pub struct Rect { pub height: u16 } +impl Rect { + /// Constructs new `Rect`. + pub fn new(left: u16, top: u16, width: u16, height: u16) -> Self { + Rect { + left: left, + top: top, + width: width, + height: height, + } + } + + /// Constructs new zero-sized `Rect` placed at (0, 0). + pub fn new_empty() -> Self { + Rect { + left: 0, + top: 0, + width: 0, + height: 0, + } + } + + /// Writes `Rect` to given stream. + fn write_to(&self, writer: &mut W) -> Result<()> { + try!(writer.write_u16::(self.left)); + try!(writer.write_u16::(self.top)); + try!(writer.write_u16::(self.width)); + try!(writer.write_u16::(self.height)); + Ok(()) + } +} + #[derive(Debug)] pub enum Error { Io(std::io::Error), diff --git a/src/protocol.rs b/src/protocol.rs index 09407ce..f76bc31 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -237,6 +237,40 @@ pub struct PixelFormat { pub blue_shift: u8, } +impl PixelFormat { + /// Creates RGB pixel format with 4 bytes per pixel and 3 bytes of depth. + pub fn new_rgb8888() -> Self { + PixelFormat { + bits_per_pixel: 32, + depth: 24, + big_endian: true, + true_colour: true, + red_max: 255, + green_max: 255, + blue_max: 255, + red_shift: 0, + green_shift: 8, + blue_shift: 16, + } + } + + /// Creates BGR pixel format with 4 bytes per pixel and 3 bytes of depth. + pub fn new_bgr8888() -> Self { + PixelFormat { + bits_per_pixel: 32, + depth: 24, + big_endian: true, + true_colour: true, + red_max: 255, + green_max: 255, + blue_max: 255, + red_shift: 16, + green_shift: 8, + blue_shift: 0, + } + } +} + impl Message for PixelFormat { fn read_from(reader: &mut R) -> Result { let pixel_format = PixelFormat { @@ -513,7 +547,7 @@ impl Message for C2S { } #[derive(Debug)] -pub struct Rectangle { +pub struct RectangleHeader { pub x_position: u16, pub y_position: u16, pub width: u16, @@ -521,9 +555,9 @@ pub struct Rectangle { pub encoding: Encoding, } -impl Message for Rectangle { - fn read_from(reader: &mut R) -> Result { - Ok(Rectangle { +impl Message for RectangleHeader { + fn read_from(reader: &mut R) -> Result { + Ok(RectangleHeader { x_position: try!(reader.read_u16::()), y_position: try!(reader.read_u16::()), width: try!(reader.read_u16::()), @@ -571,7 +605,7 @@ pub enum S2C { // core spec FramebufferUpdate { count: u16, - /* Vec has to be read out manually */ + // Vec has to be read out manually }, SetColourMapEntries { first_colour: u16, diff --git a/src/proxy.rs b/src/proxy.rs index af9ec0e..3910f0a 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -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 => { diff --git a/src/server.rs b/src/server.rs index b190f01..ae457f5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,7 @@ use std::io::Write; use std::net::{TcpStream, Shutdown}; -use ::{protocol, Result}; +use byteorder::{BigEndian, WriteBytesExt}; +use ::{protocol, Rect, Result}; use protocol::Message; /// Definitions of events received by server from client. @@ -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 @@ -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. @@ -101,9 +99,269 @@ pub enum Event { }, } +/// Helper data structure containing data to be sent by server in messages containing rectangles. +#[derive(Debug)] +enum Update<'a> { + Raw { + rect: Rect, + pixel_data: &'a [u8], + }, + CopyRect { + dst: Rect, + src_x_position: u16, + src_y_position: u16, + }, + Zrle { + rect: Rect, + zlib_data: &'a [u8], + }, + SetCursor { + size: (u16, u16), + hotspot: (u16, u16), + pixels: &'a [u8], + mask_bits: &'a [u8], + }, + DesktopSize { + width: u16, + height: u16, + }, + Encoding { encoding: protocol::Encoding }, +} + +impl<'a> Update<'a> { + /// Checks validity of given `Update`. Panics if it is not valid. + fn check(&self, validation_data: &ValidationData) { + match *self { + Update::Raw { ref rect, pixel_data } => { + let expected_num_bytes = rect.width as usize * + rect.height as usize * + validation_data.bytes_per_pixel as usize; + if expected_num_bytes != pixel_data.len() { + panic!("Expected data length for rectangle {:?} is {} while given {}", + rect, + expected_num_bytes, + pixel_data.len()); + } + } + Update::CopyRect { dst: _, src_x_position: _, src_y_position: _ } => { + // No check is needed + } + Update::Zrle { rect: _, zlib_data } => { + if zlib_data.len() > u32::max_value() as usize { + panic!("Maximal length of compressed data is {}", u32::max_value()); + } + } + Update::SetCursor { size: (width, height), hotspot: _, pixels, mask_bits } => { + // Check pixel data length + let expected_num_bytes = width as usize * + height as usize * + validation_data.bytes_per_pixel as usize; + if expected_num_bytes != pixels.len() { + panic!("Expected data length is {} while given {}", + expected_num_bytes, + pixels.len()); + } + + // Check bit mask length + let expected_num_bytes = ((width as usize + 7) / 8) * height as usize; + if expected_num_bytes != mask_bits.len() { + panic!("Expected bit mask length is {} while given {}", + expected_num_bytes, + mask_bits.len()); + } + } + Update::DesktopSize { width: _, height: _ } => { + // No check is needed + } + Update::Encoding { encoding: _ } => { + // No check is needed + } + } + } + + /// Serializes `Update` to given stream. + fn write_to(&self, writer: &mut W) -> Result<()> { + match *self { + Update::Raw { ref rect, pixel_data } => { + try!(rect.write_to(writer)); + try!(protocol::Encoding::Raw.write_to(writer)); + try!(writer.write_all(pixel_data)); + } + Update::CopyRect { ref dst, src_x_position, src_y_position } => { + try!(dst.write_to(writer)); + try!(protocol::Encoding::CopyRect.write_to(writer)); + try!(writer.write_u16::(src_x_position)); + try!(writer.write_u16::(src_y_position)); + } + Update::Zrle { ref rect, zlib_data } => { + try!(rect.write_to(writer)); + try!(protocol::Encoding::Zrle.write_to(writer)); + try!(writer.write_u32::(zlib_data.len() as u32)); + try!(writer.write_all(zlib_data)); + } + Update::SetCursor { size, hotspot, pixels, mask_bits } => { + try!(writer.write_u16::(hotspot.0)); + try!(writer.write_u16::(hotspot.1)); + try!(writer.write_u16::(size.0)); + try!(writer.write_u16::(size.1)); + try!(protocol::Encoding::Cursor.write_to(writer)); + try!(writer.write_all(pixels)); + try!(writer.write_all(mask_bits)); + } + Update::DesktopSize { width, height } => { + try!(writer.write_u16::(0)); + try!(writer.write_u16::(0)); + try!(writer.write_u16::(width)); + try!(writer.write_u16::(height)); + try!(protocol::Encoding::DesktopSize.write_to(writer)); + } + Update::Encoding { encoding } => { + try!(Rect::new_empty().write_to(writer)); + try!(encoding.write_to(writer)); + } + } + Ok(()) + } +} + +/// Builder of `FramebufferUpdate` message. +pub struct FramebufferUpdate<'a> { + updates: Vec>, +} + +impl<'a> FramebufferUpdate<'a> { + /// Constructs new `FramebufferUpdate`. + pub fn new() -> Self { + FramebufferUpdate { + updates: Vec::new(), + } + } + + /// Adds raw pixel data. + pub fn add_raw_pixels(&mut self, rect: Rect, pixel_data: &'a [u8]) -> &mut Self { + let update = Update::Raw { + rect: rect, + pixel_data: pixel_data + }; + + self.updates.push(update); + self + } + + /// Adds `CopyRect` update message instructing client to reuse pixel data it already owns. + pub fn add_copy_rect(&mut self, + dst: Rect, + src_x_position: u16, + src_y_position: u16) + -> &mut Self { + let update = Update::CopyRect { + dst: dst, + src_x_position: src_x_position, + src_y_position: src_y_position, + }; + + self.updates.push(update); + self + } + + /// Adds compressed pixel data. + /// + /// TODO: add method taking uncompressed data and compressing them. + pub fn add_compressed_pixels(&mut self, rect: Rect, zlib_data: &'a [u8]) -> &mut Self { + let update = Update::Zrle { + rect: rect, + zlib_data: zlib_data + }; + + self.updates.push(update); + self + } + + /// Add data for drawing cursor. + pub fn add_cursor(&mut self, + width: u16, + height: u16, + hotspot_x: u16, + hotspot_y: u16, + pixels: &'a [u8], + mask_bits: &'a [u8]) + -> &mut Self { + let update = Update::SetCursor { + size: (width, height), + hotspot: (hotspot_x, hotspot_y), + pixels: pixels, + mask_bits: mask_bits + }; + + self.updates.push(update); + self + } + + /// Adds notification about framebuffer resize. + pub fn add_desktop_size(&mut self, width: u16, height: u16) -> &mut Self { + let update = Update::DesktopSize { + width: width, + height: height, + }; + + self.updates.push(update); + self + } + + /// Adds confirmation of support of pseudo-encoding. + pub fn add_pseudo_encoding(&mut self, encoding: protocol::Encoding) -> &mut Self { + let update = Update::Encoding { encoding: encoding }; + + self.updates.push(update); + self + } + + /// Checks if all updates are valid. + /// + /// Panics if any of the updates is not valid. + fn check(&self, validation_data: &ValidationData) { + for update in self.updates.iter() { + update.check(validation_data); + } + } + + /// Serializes this structure and sends it using given `writer`. + fn write_to(&self, writer: &mut W) -> Result<()> { + for chunk in self.updates.chunks(u16::max_value() as usize) { + let count = chunk.len() as u16; + try!(protocol::S2C::FramebufferUpdate{count}.write_to(writer)); + for update in chunk { + try!(update.write_to(writer)); + } + } + Ok(()) + } +} + +/// Gathers all data needed to validate framebuffer updates. +struct ValidationData { + /// Number of bytes per pixel used to check validity of sent data extracted from `PixelFormat`. + bytes_per_pixel: u16, +} + +impl ValidationData { + /// Constructs new `ValidationData`. + fn new(pixel_format: &protocol::PixelFormat) -> Self { + let mut mine = ValidationData { bytes_per_pixel: 0 }; + mine.update(pixel_format); + mine + } + + /// Updates bytes per pixel from `PixelFormat`. + fn update(&mut self, pixel_format: &protocol::PixelFormat) { + self.bytes_per_pixel = (pixel_format.bits_per_pixel as u16 + 7) / 8; + } +} + /// This structure provides basic server-side functionality of RDP protocol. pub struct Server { stream: TcpStream, + validation_data: ValidationData } impl Server { @@ -153,7 +411,10 @@ impl Server { try!(server_init.write_to(&mut stream)); - Ok((Server { stream: stream }, client_init.shared)) + Ok((Server { + stream: stream, + validation_data: ValidationData::new(&pixel_format), + }, client_init.shared)) } /// Reads the socket and returns received event. @@ -162,6 +423,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.validation_data.update(&pixel_format); Ok(Event::SetPixelFormat(pixel_format)) } protocol::C2S::SetEncodings(encodings) => { @@ -176,10 +440,7 @@ impl Server { } => { Ok(Event::FramebufferUpdateRequest { incremental, - x_position, - y_position, - width, - height, + rect: Rect::new(x_position, y_position, width, height), }) } protocol::C2S::KeyEvent { down, key } => { @@ -200,46 +461,129 @@ impl Server { } } - /// Sends header of `FramebufferUpdate` message containing number of rectangles to be sent. + /// 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)); + /// Panics if given updates are not valid. All validity checks are done before sending any + /// update. + pub fn send_update(&mut self, updates: &FramebufferUpdate) -> Result<()> { + updates.check(&self.validation_data); + try!(updates.write_to(&mut self.stream)); Ok(()) } - /// 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)); + /// Shuts down communication over TCP stream in both directions. + pub fn disconnect(self) -> Result<()> { + try!(self.stream.shutdown(Shutdown::Both)); 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)); - Ok(()) +#[cfg(test)] +mod test { + use super::{protocol, Rect, Update, ValidationData}; + + /// Checks if `ValidationData` correctly converts bits per pixel from `PixelFormat` to bytes + /// per pixel. + #[test] + fn check_if_validation_data_correctly_rounds_bits_to_bytes() { + let mut format = protocol::PixelFormat::new_rgb8888(); + let test_data = vec![(8, 1), (23, 3), (24, 3), (25, 4), (31, 4), (32, 4), (33, 5)]; + for (bits, expected_bytes) in test_data { + format.bits_per_pixel = bits; + let data = ValidationData::new(&format); + assert_eq!(data.bytes_per_pixel, expected_bytes); + } } - /// Shuts down communication over TCP stream in both directions. - pub fn disconnect(self) -> Result<()> { - try!(self.stream.shutdown(Shutdown::Both)); - Ok(()) + /// Checks if `Update::Raw` accepts valid data both in case of very small and big buffer which + /// could cause `u16` overflow. + #[test] + fn check_if_raw_update_accepts_valid_data() { + let data = vec![0; 4 * 800 * 100]; + let pixel_format = protocol::PixelFormat::new_rgb8888(); + let validation_data = ValidationData::new(&pixel_format); + + // Small rectangle + Update::Raw { + rect: Rect::new(0, 0, 8, 8), + pixel_data: &data[0 .. (4 * 8 * 8)], + }.check(&validation_data); + + // Big rectangle (bigger than `u16::MAX`) + Update::Raw { + rect: Rect::new(0, 0, 800, 100), + pixel_data: &data, + }.check(&validation_data); + } + + /// Checks if `Update::Raw` rejects data with invalid length. + #[test] + #[should_panic] + fn check_if_raw_update_rejects_invalid_data() { + let data = vec![0; 5]; + let pixel_format = protocol::PixelFormat::new_rgb8888(); + let validation_data = ValidationData::new(&pixel_format); + + Update::Raw { + rect: Rect::new(0, 0, 8, 8), + pixel_data: &data, + }.check(&validation_data); + } + + /// Checks if `Update::SetCursor` accepts valid data both in case of very small and big buffer + /// which could cause `u16` overflow. + #[test] + fn check_if_set_cursor_update_accepts_valid_data() { + let data = vec![0; 4 * 800 * 100]; + let pixel_format = protocol::PixelFormat::new_rgb8888(); + let validation_data = ValidationData::new(&pixel_format); + + // Small rectangle + Update::SetCursor { + size: (8, 8), + hotspot: (0, 0), + pixels: &data[0 .. (4 * 8 * 8)], + mask_bits: &data[0 .. 8], + }.check(&validation_data); + + // Big rectangle (bigger than `u16::MAX`) + Update::SetCursor { + size: (800, 100), + hotspot: (0, 0), + pixels: &data, + mask_bits: &data[0 .. 10000], + }.check(&validation_data); + } + + /// Checks if `Update::SetCursor` rejects data with invalid pixel length. + #[test] + #[should_panic] + fn check_if_set_cursor_update_rejects_invalid_pixel_data() { + let data = vec![0; 15]; + let pixel_format = protocol::PixelFormat::new_rgb8888(); + let validation_data = ValidationData::new(&pixel_format); + + Update::SetCursor { + size: (8, 8), + hotspot: (0, 0), + pixels: &data, + mask_bits: &data[0 .. 8], + }.check(&validation_data); + } + + /// Checks if `Update::SetCursor` rejects data with invalid bit mask length. + #[test] + #[should_panic] + fn check_if_set_cursor_update_rejects_invalid_bit_mask_data() { + let data = vec![0; 16]; + let pixel_format = protocol::PixelFormat::new_rgb8888(); + let validation_data = ValidationData::new(&pixel_format); + + Update::SetCursor { + size: (8, 8), + hotspot: (0, 0), + pixels: &data, + mask_bits: &data[0 .. 7], + }.check(&validation_data); } } From 771716f46ec8be26fbecb75367ebc2e54a03b688 Mon Sep 17 00:00:00 2001 From: Wojciech Kluczka Date: Mon, 19 Jun 2017 23:17:32 +0200 Subject: [PATCH 4/4] Improve server-side API - part 2 --- src/client.rs | 30 ++++++-------- src/lib.rs | 66 +++++++++++++------------------ src/protocol.rs | 73 ++++++++++++++++++---------------- src/server.rs | 103 +++++++++++++++++++++++++++++------------------- src/zrle.rs | 9 ++--- 5 files changed, 147 insertions(+), 134 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2a7e2ef..1f132b9 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,7 +4,7 @@ use std::thread; use std::sync::{Arc, Mutex}; use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError}; use byteorder::{BigEndian, ReadBytesExt}; -use ::{zrle, protocol, Rect, Colour, Error, Result}; +use ::{zrle, protocol, Colour, Error, Result}; use protocol::Message; use security::des; #[cfg(feature = "apple-auth")] @@ -35,8 +35,8 @@ pub enum Event { Disconnected(Option), Resize(u16, u16), SetColourMap { first_colour: u16, colours: Vec }, - PutPixels(Rect, Vec), - CopyPixels { src: Rect, dst: Rect }, + PutPixels(protocol::Rect, Vec), + CopyPixels { src: protocol::Rect, dst: protocol::Rect }, EndOfFrame, SetCursor { size: (u16, u16), hotspot: (u16, u16), pixels: Vec, mask_bits: Vec }, Clipboard(String), @@ -80,12 +80,10 @@ impl Event { let rectangle = try!(protocol::RectangleHeader::read_from(&mut stream)); debug!("<- {:?}", rectangle); - let dst = Rect { - left: rectangle.x_position, - top: rectangle.y_position, - width: rectangle.width, - height: rectangle.height - }; + let dst = protocol::Rect::new(rectangle.x_position, + rectangle.y_position, + rectangle.width, + rectangle.height); match rectangle.encoding { protocol::Encoding::Raw => { let length = (rectangle.width as usize) * @@ -99,12 +97,10 @@ impl Event { }, protocol::Encoding::CopyRect => { let copy_rect = try!(protocol::CopyRect::read_from(&mut stream)); - let src = Rect { - left: copy_rect.src_x_position, - top: copy_rect.src_y_position, - width: rectangle.width, - height: rectangle.height - }; + let src = protocol::Rect::new(copy_rect.src_x_position, + copy_rect.src_y_position, + rectangle.width, + rectangle.height); send!(tx_events, Event::CopyPixels { src: src, dst: dst }) }, protocol::Encoding::Zrle => { @@ -321,7 +317,7 @@ impl Client { Ok(()) } - pub fn request_update(&mut self, rect: Rect, incremental: bool) -> Result<()> { + pub fn request_update(&mut self, rect: protocol::Rect, incremental: bool) -> Result<()> { let update_req = protocol::C2S::FramebufferUpdateRequest { incremental: incremental, x_position: rect.left, @@ -371,7 +367,7 @@ impl Client { // are no FramebufferUpdate's in the buffers somewhere. // This is not fully robust though (and cannot possibly be). let _ = self.poll_iter().count(); // drain it - let framebuffer_rect = Rect { left: 0, top: 0, width: self.size.0, height: self.size.1 }; + let framebuffer_rect = protocol::Rect::new(0, 0, self.size.0, self.size.1); try!(self.request_update(framebuffer_rect, false)); 'outer: loop { for event in self.poll_iter() { diff --git a/src/lib.rs b/src/lib.rs index f8aa278..b572a1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,6 @@ extern crate octavo; #[cfg(feature = "apple-auth")] extern crate crypto; -use std::io::Write; -use byteorder::{BigEndian, WriteBytesExt}; - mod protocol; mod zrle; mod security; @@ -24,43 +21,36 @@ pub use client::Client; pub use proxy::Proxy; pub use server::Server; -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct Rect { - pub left: u16, - pub top: u16, - pub width: u16, - pub height: u16 -} - -impl Rect { - /// Constructs new `Rect`. - pub fn new(left: u16, top: u16, width: u16, height: u16) -> Self { - Rect { - left: left, - top: top, - width: width, - height: height, - } - } +pub mod pixel_format { + use super::PixelFormat; - /// Constructs new zero-sized `Rect` placed at (0, 0). - pub fn new_empty() -> Self { - Rect { - left: 0, - top: 0, - width: 0, - height: 0, - } - } + /// RGB pixel format with 4 bytes per pixel and 3 bytes of depth. + pub const RGB8888: PixelFormat = PixelFormat { + bits_per_pixel: 32, + depth: 24, + big_endian: true, + true_colour: true, + red_max: 255, + green_max: 255, + blue_max: 255, + red_shift: 0, + green_shift: 8, + blue_shift: 16, + }; - /// Writes `Rect` to given stream. - fn write_to(&self, writer: &mut W) -> Result<()> { - try!(writer.write_u16::(self.left)); - try!(writer.write_u16::(self.top)); - try!(writer.write_u16::(self.width)); - try!(writer.write_u16::(self.height)); - Ok(()) - } + /// BGR pixel format with 4 bytes per pixel and 3 bytes of depth. + pub const BGR8888: PixelFormat = PixelFormat { + bits_per_pixel: 32, + depth: 24, + big_endian: true, + true_colour: true, + red_max: 255, + green_max: 255, + blue_max: 255, + red_shift: 16, + green_shift: 8, + blue_shift: 0, + }; } #[derive(Debug)] diff --git a/src/protocol.rs b/src/protocol.rs index f76bc31..c033853 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -237,40 +237,6 @@ pub struct PixelFormat { pub blue_shift: u8, } -impl PixelFormat { - /// Creates RGB pixel format with 4 bytes per pixel and 3 bytes of depth. - pub fn new_rgb8888() -> Self { - PixelFormat { - bits_per_pixel: 32, - depth: 24, - big_endian: true, - true_colour: true, - red_max: 255, - green_max: 255, - blue_max: 255, - red_shift: 0, - green_shift: 8, - blue_shift: 16, - } - } - - /// Creates BGR pixel format with 4 bytes per pixel and 3 bytes of depth. - pub fn new_bgr8888() -> Self { - PixelFormat { - bits_per_pixel: 32, - depth: 24, - big_endian: true, - true_colour: true, - red_max: 255, - green_max: 255, - blue_max: 255, - red_shift: 16, - green_shift: 8, - blue_shift: 0, - } - } -} - impl Message for PixelFormat { fn read_from(reader: &mut R) -> Result { let pixel_format = PixelFormat { @@ -546,6 +512,45 @@ impl Message for C2S { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct Rect { + pub left: u16, + pub top: u16, + pub width: u16, + pub height: u16 +} + +impl Rect { + /// Constructs new `Rect`. + pub fn new(left: u16, top: u16, width: u16, height: u16) -> Self { + Rect { + left: left, + top: top, + width: width, + height: height, + } + } +} + +impl Message for Rect { + fn read_from(reader: &mut R) -> Result { + Ok(Rect { + left: try!(reader.read_u16::()), + top: try!(reader.read_u16::()), + width: try!(reader.read_u16::()), + height: try!(reader.read_u16::()), + }) + } + + fn write_to(&self, writer: &mut W) -> Result<()> { + try!(writer.write_u16::(self.left)); + try!(writer.write_u16::(self.top)); + try!(writer.write_u16::(self.width)); + try!(writer.write_u16::(self.height)); + Ok(()) + } +} + #[derive(Debug)] pub struct RectangleHeader { pub x_position: u16, diff --git a/src/server.rs b/src/server.rs index ae457f5..beeeeea 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,7 @@ use std::io::Write; use std::net::{TcpStream, Shutdown}; use byteorder::{BigEndian, WriteBytesExt}; -use ::{protocol, Rect, Result}; +use ::{protocol, Result}; use protocol::Message; /// Definitions of events received by server from client. @@ -47,7 +47,7 @@ pub enum Event { /// `FramebufferUpdate`. FramebufferUpdateRequest { incremental: bool, - rect: Rect, + rect: protocol::Rect, }, /// A `KeyEvent` message indicates a key press or release. `down` flag is `true` if the key is @@ -103,16 +103,16 @@ pub enum Event { #[derive(Debug)] enum Update<'a> { Raw { - rect: Rect, + rect: protocol::Rect, pixel_data: &'a [u8], }, CopyRect { - dst: Rect, + dst: protocol::Rect, src_x_position: u16, src_y_position: u16, }, Zrle { - rect: Rect, + rect: protocol::Rect, zlib_data: &'a [u8], }, SetCursor { @@ -216,7 +216,7 @@ impl<'a> Update<'a> { try!(protocol::Encoding::DesktopSize.write_to(writer)); } Update::Encoding { encoding } => { - try!(Rect::new_empty().write_to(writer)); + try!(protocol::Rect::new(0, 0, 0, 0).write_to(writer)); try!(encoding.write_to(writer)); } } @@ -225,32 +225,39 @@ impl<'a> Update<'a> { } /// Builder of `FramebufferUpdate` message. -pub struct FramebufferUpdate<'a> { +/// +/// This structure can be constructed with `Server::create_update`. +pub struct FramebufferUpdateBuilder<'a, 'b> { updates: Vec>, + validation_data: &'b ValidationData, } -impl<'a> FramebufferUpdate<'a> { - /// Constructs new `FramebufferUpdate`. - pub fn new() -> Self { - FramebufferUpdate { +impl<'a, 'b> FramebufferUpdateBuilder<'a, 'b> { + /// Constructs new `FramebufferUpdateBuilder`. + fn new(validation_data: &'b ValidationData) -> Self { + FramebufferUpdateBuilder { updates: Vec::new(), + validation_data: validation_data, } } /// Adds raw pixel data. - pub fn add_raw_pixels(&mut self, rect: Rect, pixel_data: &'a [u8]) -> &mut Self { + /// + /// Panics if length of pixel data does not match rectangle size. + pub fn add_raw_pixels(&mut self, rect: protocol::Rect, pixel_data: &'a [u8]) -> &mut Self { let update = Update::Raw { rect: rect, pixel_data: pixel_data }; + update.check(self.validation_data); self.updates.push(update); self } /// Adds `CopyRect` update message instructing client to reuse pixel data it already owns. pub fn add_copy_rect(&mut self, - dst: Rect, + dst: protocol::Rect, src_x_position: u16, src_y_position: u16) -> &mut Self { @@ -260,24 +267,30 @@ impl<'a> FramebufferUpdate<'a> { src_y_position: src_y_position, }; + update.check(self.validation_data); self.updates.push(update); self } /// Adds compressed pixel data. /// + /// Panics if length of compressed data is bigger than `u32::MAX`. + /// /// TODO: add method taking uncompressed data and compressing them. - pub fn add_compressed_pixels(&mut self, rect: Rect, zlib_data: &'a [u8]) -> &mut Self { + pub fn add_compressed_pixels(&mut self, rect: protocol::Rect, zlib_data: &'a [u8]) -> &mut Self { let update = Update::Zrle { rect: rect, zlib_data: zlib_data }; + update.check(self.validation_data); self.updates.push(update); self } - /// Add data for drawing cursor. + /// Adds data for drawing cursor. + /// + /// Panics if pixel data or mask bits length does not match size of the cursor. pub fn add_cursor(&mut self, width: u16, height: u16, @@ -293,6 +306,7 @@ impl<'a> FramebufferUpdate<'a> { mask_bits: mask_bits }; + update.check(self.validation_data); self.updates.push(update); self } @@ -304,6 +318,7 @@ impl<'a> FramebufferUpdate<'a> { height: height, }; + update.check(self.validation_data); self.updates.push(update); self } @@ -312,27 +327,33 @@ impl<'a> FramebufferUpdate<'a> { pub fn add_pseudo_encoding(&mut self, encoding: protocol::Encoding) -> &mut Self { let update = Update::Encoding { encoding: encoding }; + update.check(self.validation_data); self.updates.push(update); self } - /// Checks if all updates are valid. - /// - /// Panics if any of the updates is not valid. - fn check(&self, validation_data: &ValidationData) { - for update in self.updates.iter() { - update.check(validation_data); - } + /// Consumes `FramebufferUpdateBuilder` and returns `FramebufferUpdate`. + pub fn done(self) -> FramebufferUpdate<'a> { + FramebufferUpdate { updates: self.updates } } +} +/// Represents `FramebufferUpdate` message. +/// +/// Use `FramebufferUpdateBuilder` to construct this struct. +pub struct FramebufferUpdate<'a> { + updates: Vec>, +} + +impl<'a> FramebufferUpdate<'a> { /// Serializes this structure and sends it using given `writer`. fn write_to(&self, writer: &mut W) -> Result<()> { for chunk in self.updates.chunks(u16::max_value() as usize) { let count = chunk.len() as u16; - try!(protocol::S2C::FramebufferUpdate{count}.write_to(writer)); - for update in chunk { + try!(protocol::S2C::FramebufferUpdate{count}.write_to(writer)); + for update in chunk { try!(update.write_to(writer)); - } + } } Ok(()) } @@ -417,6 +438,12 @@ impl Server { }, client_init.shared)) } + /// Constructs new `FramebufferUpdateBuilder` structure used to build `FramebufferUpdate` + /// message. + pub fn create_update<'a, 'b>(&'b self) -> FramebufferUpdateBuilder<'a, 'b> { + FramebufferUpdateBuilder::new(&self.validation_data) + } + /// Reads the socket and returns received event. pub fn read_event(&mut self) -> Result { match protocol::C2S::read_from(&mut self.stream) { @@ -440,7 +467,7 @@ impl Server { } => { Ok(Event::FramebufferUpdateRequest { incremental, - rect: Rect::new(x_position, y_position, width, height), + rect: protocol::Rect::new(x_position, y_position, width, height), }) } protocol::C2S::KeyEvent { down, key } => { @@ -462,11 +489,7 @@ impl Server { } /// Sends `FramebufferUpdate` message. - /// - /// Panics if given updates are not valid. All validity checks are done before sending any - /// update. pub fn send_update(&mut self, updates: &FramebufferUpdate) -> Result<()> { - updates.check(&self.validation_data); try!(updates.write_to(&mut self.stream)); Ok(()) } @@ -480,13 +503,13 @@ impl Server { #[cfg(test)] mod test { - use super::{protocol, Rect, Update, ValidationData}; + use super::{protocol, Update, ValidationData}; /// Checks if `ValidationData` correctly converts bits per pixel from `PixelFormat` to bytes /// per pixel. #[test] fn check_if_validation_data_correctly_rounds_bits_to_bytes() { - let mut format = protocol::PixelFormat::new_rgb8888(); + let mut format = ::pixel_format::BGR8888; let test_data = vec![(8, 1), (23, 3), (24, 3), (25, 4), (31, 4), (32, 4), (33, 5)]; for (bits, expected_bytes) in test_data { format.bits_per_pixel = bits; @@ -500,18 +523,18 @@ mod test { #[test] fn check_if_raw_update_accepts_valid_data() { let data = vec![0; 4 * 800 * 100]; - let pixel_format = protocol::PixelFormat::new_rgb8888(); + let pixel_format = ::pixel_format::BGR8888; let validation_data = ValidationData::new(&pixel_format); // Small rectangle Update::Raw { - rect: Rect::new(0, 0, 8, 8), + rect: protocol::Rect::new(0, 0, 8, 8), pixel_data: &data[0 .. (4 * 8 * 8)], }.check(&validation_data); // Big rectangle (bigger than `u16::MAX`) Update::Raw { - rect: Rect::new(0, 0, 800, 100), + rect: protocol::Rect::new(0, 0, 800, 100), pixel_data: &data, }.check(&validation_data); } @@ -521,11 +544,11 @@ mod test { #[should_panic] fn check_if_raw_update_rejects_invalid_data() { let data = vec![0; 5]; - let pixel_format = protocol::PixelFormat::new_rgb8888(); + let pixel_format = ::pixel_format::BGR8888; let validation_data = ValidationData::new(&pixel_format); Update::Raw { - rect: Rect::new(0, 0, 8, 8), + rect: protocol::Rect::new(0, 0, 8, 8), pixel_data: &data, }.check(&validation_data); } @@ -535,7 +558,7 @@ mod test { #[test] fn check_if_set_cursor_update_accepts_valid_data() { let data = vec![0; 4 * 800 * 100]; - let pixel_format = protocol::PixelFormat::new_rgb8888(); + let pixel_format = ::pixel_format::BGR8888; let validation_data = ValidationData::new(&pixel_format); // Small rectangle @@ -560,7 +583,7 @@ mod test { #[should_panic] fn check_if_set_cursor_update_rejects_invalid_pixel_data() { let data = vec![0; 15]; - let pixel_format = protocol::PixelFormat::new_rgb8888(); + let pixel_format = ::pixel_format::BGR8888; let validation_data = ValidationData::new(&pixel_format); Update::SetCursor { @@ -576,7 +599,7 @@ mod test { #[should_panic] fn check_if_set_cursor_update_rejects_invalid_bit_mask_data() { let data = vec![0; 16]; - let pixel_format = protocol::PixelFormat::new_rgb8888(); + let pixel_format = ::pixel_format::BGR8888; let validation_data = ValidationData::new(&pixel_format); Update::SetCursor { diff --git a/src/zrle.rs b/src/zrle.rs index 2a276bb..f40a628 100644 --- a/src/zrle.rs +++ b/src/zrle.rs @@ -2,7 +2,7 @@ use std; use std::io::Read; use flate2; use byteorder::ReadBytesExt; -use ::{protocol, Error, Result, Rect}; +use ::{protocol, Error, Result}; struct ZlibReader<'a> { decompressor: flate2::Decompress, @@ -111,9 +111,9 @@ impl Decoder { Decoder { decompressor: Some(flate2::Decompress::new(/*zlib_header*/true)) } } - pub fn decode(&mut self, format: protocol::PixelFormat, rect: Rect, + pub fn decode(&mut self, format: protocol::PixelFormat, rect: protocol::Rect, input: &[u8], mut callback: F) -> Result - where F: FnMut(Rect, Vec) -> Result { + where F: FnMut(protocol::Rect, Vec) -> Result { fn read_run_length(reader: &mut Read) -> Result { let mut run_length_part = try!(reader.read_u8()); let mut run_length = 1 + run_length_part as usize; @@ -235,8 +235,7 @@ impl Decoder { _ => return Err(Error::Unexpected("ZRLE subencoding")) } - let tile = Rect { top: rect.top + y, left: rect.left + x, - width: width, height: height }; + let tile = protocol::Rect::new(rect.top + y, rect.left + x, width, height); if let false = try!(callback(tile, pixels)) { return Ok(false) }