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..c099e4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { @@ -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), @@ -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", diff --git a/src/protocol.rs b/src/protocol.rs index 09407ce..d37005e 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -513,7 +513,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 +521,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 +571,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..9e85488 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, Error, 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,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(), bytes_per_pixel); + Err(Error::InconsistentData(msg)) + } + } + Rectangle::Encoding { encoding: _ } => { + // No check is needed + Ok(()) + } + _ => { + // XXX + Ok(()) + } + } + } + + fn write_to(&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(rect: &Rect, writer: &mut W) -> Result<()> { + try!(writer.write_u16::(rect.left)); + try!(writer.write_u16::(rect.top)); + try!(writer.write_u16::(rect.width)); + try!(writer.write_u16::(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 { @@ -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. @@ -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) => { @@ -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 } => { @@ -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(()) }