diff --git a/src/event.rs b/src/event.rs index 83c8d76..1669573 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,50 +1,35 @@ - -use std::collections::LinkedList; - use option::TelnetOption; use negotiation::NegotiationAction; /// /// Events generated by `Telnet`. /// -#[derive(Debug)] -pub enum TelnetEvent { +#[derive(Debug, PartialEq, Eq)] +pub enum Event<'a> { /// Data received (excluding telnet commands) - Data(Box<[u8]>), + Data(&'a [u8]), /// An unrecognized telnet command received UnknownIAC(u8), /// A telnet negotiation received Negotiation(NegotiationAction, TelnetOption), /// A telnet subnegotiation data received Subnegotiation(TelnetOption, Box<[u8]>), - /// Read time out - TimedOut, - /// No data to read - NoData, /// Error encountered during processing read buffer Error(String) } -pub struct TelnetEventQueue { - queue: LinkedList -} +pub struct Events<'a>(std::vec::IntoIter>); -impl TelnetEventQueue { - pub fn new() -> TelnetEventQueue { - TelnetEventQueue { - queue: LinkedList::new() - } - } - - pub fn push_event(&mut self, event: TelnetEvent) { - self.queue.push_back(event); +impl<'a> From>> for Events<'a> { + fn from(events: Vec>) -> Self { + Self(events.into_iter()) } +} - pub fn take_event(&mut self) -> Option { - self.queue.pop_front() - } +impl<'a> Iterator for Events<'a> { + type Item = Event<'a>; - pub fn is_empty(&self) -> bool { - self.queue.is_empty() + fn next(&mut self) -> Option { + self.0.next() } } diff --git a/src/format.rs b/src/format.rs new file mode 100644 index 0000000..1e5f23f --- /dev/null +++ b/src/format.rs @@ -0,0 +1,146 @@ +use byte::*; +use negotiation::NegotiationAction; +use option::TelnetOption; + +pub struct FormattedData<'a>(std::vec::IntoIter<&'a [u8]>); + +impl<'a> FormattedData<'a> { + /// + /// Create owned copy of the formatted data. + /// + /// # Examples + /// ```rust + /// let data = [1, 2, 0xFF, 3, 4]; + /// let data_formatted = telnet::format::data(&data).to_owned(); + /// assert_eq!(data_formatted, vec![1, 2, 0xFF, 0xFF, 3, 4].into_boxed_slice()); + /// ``` + /// + pub fn to_owned(self) -> Box<[u8]> { + self.append_to(Vec::new()).into() + } + + fn append_to(self, mut buffer: Vec) -> Vec { + self.0.for_each(|buf| buffer.extend_from_slice(buf)); + buffer + } +} + +impl<'a> From> for FormattedData<'a> { + fn from(slices: Vec<&'a [u8]>) -> Self { + Self(slices.into_iter()) + } +} + +impl<'a> Iterator for FormattedData<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +/// +/// Formats the data buffer to be sent over Telnet. +/// +/// # Examples +/// ```rust +/// use std::io::Write; +/// +/// let mut telnet_stream = Vec::new(); +/// let data = [1, 2, 0xFF, 3, 4]; +/// +/// for buf in telnet::format::data(&data) { +/// telnet_stream.write_all(buf).unwrap(); +/// } +/// +/// assert_eq!(telnet_stream, vec![1, 2, 0xFF, 0xFF, 3, 4]); +/// ``` +/// +pub fn data(buffer: &[u8]) -> FormattedData { + let mut slices = Vec::new(); + let mut start = 0; + + for i in 0..buffer.len() { + if buffer[i] == BYTE_IAC { + slices.push(&buffer[start..(i + 1)]); + start = i; + } + } + + if start < buffer.len() { + slices.push(&buffer[start..]); + } + + slices.into() +} + +/// +/// Formats a Telnet negotiation to be sent over Telnet. +/// +/// # Examples +/// ```rust +/// use telnet::{NegotiationAction, TelnetOption}; +/// +/// let buffer = telnet::format::negotiation(NegotiationAction::Will, TelnetOption::Echo); +/// assert_eq!(buffer, vec![0xFF, 0xFB, 0x01].into_boxed_slice()); +/// ``` +/// +pub fn negotiation(action: NegotiationAction, opt: TelnetOption) -> Box<[u8]> { + Box::new([BYTE_IAC, action.to_byte(), opt.to_byte()]) +} + +/// +/// Formats a Telnet sub-negotiation to be sent over Telnet. +/// +/// # Examples +/// ```rust +/// use telnet::{NegotiationAction, TelnetOption}; +/// +/// let parameters = [1, 2, 3]; +/// let buffer = telnet::format::sub_negotiation(TelnetOption::TTYPE, ¶meters); +/// assert_eq!(buffer, vec![0xFF, 0xFA, 24, 1, 2, 3, 0xFF, 0xF0].into_boxed_slice()); +/// ``` +/// +pub fn sub_negotiation(opt: TelnetOption, parameters: &[u8]) -> Box<[u8]> { + let mut buffer = data(parameters).append_to(vec![BYTE_IAC, BYTE_SB, opt.to_byte()]); + buffer.extend_from_slice(&[BYTE_IAC, BYTE_SE]); + buffer.into() +} + +#[cfg(test)] +mod tests { + use super::*; + + fn escape_test(buffer: &[u8], result: Box<[u8]>) { + let buffer = data(buffer).to_owned(); + assert_eq!(buffer, result); + } + + #[test] + fn escape_double_iac() { + escape_test( + &[1, 2, 0xFF, 0xFF, 3, 4], + vec![1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 3, 4].into(), + ); + } + + #[test] + fn escape_no_iac() { + escape_test(&[1, 2, 3, 4], vec![1, 2, 3, 4].into()); + } + + #[test] + fn escape_iac_only() { + escape_test(&[0xFF], vec![0xFF, 0xFF].into()); + } + + #[test] + fn escape_iac_sub_negotiation() { + let parameters = [1, 0xFF, 3]; + let buffer = sub_negotiation(TelnetOption::TTYPE, ¶meters); + assert_eq!( + buffer, + vec![0xFF, 0xFA, 24, 1, 0xFF, 0xFF, 3, 0xFF, 0xF0].into() + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 85117fc..eb6625d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,613 +17,82 @@ //! } //! } //! ``` +mod byte; +mod event; +pub mod format; mod negotiation; mod option; -mod event; -mod byte; +mod parse; mod stream; #[cfg(feature = "zcstream")] mod zcstream; #[cfg(feature = "zcstream")] mod zlibstream; +pub use event::{Event, Events}; +pub use negotiation::NegotiationAction; +pub use option::TelnetOption; +pub use parse::Parser; pub use stream::Stream; #[cfg(feature = "zcstream")] -pub use zlibstream::ZlibStream; -#[cfg(feature = "zcstream")] pub use zcstream::ZCStream; -pub use option::TelnetOption; -pub use event::TelnetEvent; -pub use negotiation::NegotiationAction; - -use std::io; -use std::io::{Read, Write, ErrorKind}; -use std::net::{TcpStream, ToSocketAddrs}; -use std::time::Duration; - -use event::TelnetEventQueue; -use byte::*; +#[cfg(feature = "zcstream")] +pub use zlibstream::ZlibStream; #[cfg(feature = "zcstream")] type TStream = zcstream::ZCStream; #[cfg(not(feature = "zcstream"))] type TStream = stream::Stream; -#[derive(Debug)] -enum ProcessState { - NormalData, - IAC, - SB, - SBData(TelnetOption, usize), // (option, start location of option data) - SBDataIAC(TelnetOption, usize), // (option, start location of option data) - Will, Wont, - Do, Dont -} - -/// -/// A telnet connection to a remote host. -/// -/// # Examples -/// ```rust,should_panic -/// use telnet::Telnet; -/// -/// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) -/// .expect("Couldn't connect to the server..."); -/// loop { -/// let event = connection.read().expect("Read Error"); -/// println!("{:?}", event); -/// } -/// ``` -/// -pub struct Telnet { - stream: Box, - event_queue: TelnetEventQueue, +use std::io::{Read, Result, Write}; - // Buffer +pub struct Reader { + parser: Parser, + reader: R, buffer: Box<[u8]>, - buffered_size: usize, - process_buffer: Box<[u8]>, - process_buffered_size: usize } -impl Telnet { - - /// - /// Opens a telnet connection to a remote host using a `TcpStream`. - /// - /// `addr` is an address of the remote host. Note that a remote host usually opens port 23 for - /// a Telnet connection. `buf_size` is a size of the underlying buffer for processing the data - /// read from the remote host. - /// - /// # Examples - /// ```rust,should_panic - /// use telnet::Telnet; - /// - /// let connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// ``` - /// - pub fn connect(addr: A, buf_size: usize) -> io::Result { - let stream = TcpStream::connect(addr)?; // send the error out directly - - #[cfg(feature = "zcstream")] - return Ok(Telnet::from_stream(Box::new(ZlibStream::from_stream(stream)), buf_size)); - #[cfg(not(feature = "zcstream"))] - return Ok(Telnet::from_stream(Box::new(stream), buf_size)); - } - - #[cfg(feature = "zcstream")] - pub fn begin_zlib(&mut self) { - self.stream.begin_zlib() - } - - #[cfg(feature = "zcstream")] - pub fn end_zlib(&mut self) { - self.stream.end_zlib() - } - /// Open a telnet connection to a remote host using a generic stream. - /// - /// Communication will be made with the host using `stream`. `buf_size` is the size of the underlying - /// buffer for processing data from the host. - /// - /// Use this version of the constructor if you want to provide your own stream, for example if you want - /// to mock out the remote host for testing purposes, or want to wrap the data the data with TLS encryption. - pub fn from_stream(stream: Box, buf_size: usize) -> Telnet { - let actual_size = if buf_size == 0 { 1 } else { buf_size }; - - Telnet { - stream: stream, - event_queue: TelnetEventQueue::new(), - buffer: vec![0; actual_size].into_boxed_slice(), - buffered_size: 0, - process_buffer: vec![0; actual_size].into_boxed_slice(), - process_buffered_size: 0 +impl Reader { + pub fn new(reader: R, capacity: usize) -> Self { + Self { + parser: Parser::new(), + reader, + buffer: vec![0; capacity].into(), } } - /// - /// Reads a `TelnetEvent`. - /// - /// If there was not any queued `TelnetEvent`, it would read a chunk of data into its buffer, - /// extract any telnet command in the message, and queue all processed results. Otherwise, it - /// would take a queued `TelnetEvent` without reading data from `TcpStream`. - /// - /// # Examples - /// ```rust,should_panic - /// use telnet::Telnet; - /// - /// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// let event = connection.read().expect("Read Error"); - /// println!("{:?}", event); - /// ``` - /// - pub fn read(&mut self) -> io::Result { - while self.event_queue.is_empty() { - // Set stream settings - self.stream.set_nonblocking(false).expect("set_nonblocking call failed"); - self.stream.set_read_timeout(None).expect("set_read_timeout call failed"); - - // Read bytes to the buffer - match self.stream.read(&mut self.buffer) { - Ok(size) => { - self.buffered_size = size; - }, - Err(e) => return Err(e) - } - - self.process(); - } - - // Return an event - Ok( - match self.event_queue.take_event() { - Some(x) => x, - None => TelnetEvent::Error("Internal Queue error".to_string()) - } - ) + pub fn read(&mut self) -> Result { + let n = self.reader.read(&mut self.buffer)?; + Ok(self.parser.parse(&self.buffer[..n])) } +} - /// - /// Reads a `TelnetEvent`, but the waiting time cannot exceed a given `Duration`. - /// - /// This method is similar to `read()`, but with a time limitation. If the given time was - /// reached, it would return `TelnetEvent::TimedOut`. - /// - /// # Examples - /// ```rust,should_panic - /// use std::time::Duration; - /// use telnet::Telnet; - /// - /// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// let event = connection.read_timeout(Duration::new(5, 0)).expect("Read Error"); - /// println!("{:?}", event); - /// ``` - /// - pub fn read_timeout(&mut self, timeout: Duration) -> io::Result { - if self.event_queue.is_empty() { - // Set stream settings - self.stream.set_nonblocking(false).expect("set_nonblocking call failed"); - self.stream.set_read_timeout(Some(timeout)).expect("set_read_timeout call failed"); - - // Read bytes to the buffer - match self.stream.read(&mut self.buffer) { - Ok(size) => { - self.buffered_size = size; - }, - Err(e) => { - if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut { - return Ok(TelnetEvent::TimedOut); - } else { - return Err(e); - } - } - } - - self.process(); - } - - // Return an event - Ok( - match self.event_queue.take_event() { - Some(x) => x, - None => TelnetEvent::Error("Internal Queue error".to_string()) - } - ) - } - - /// - /// Reads a `TelnetEvent`. Return immediataly if there was no queued event and nothing to read. - /// - /// This method is a non-blocking version of `read()`. If there was no more data, it would - /// return `TelnetEvent::NoData`. - /// - /// # Examples - /// ```rust,should_panic - /// use telnet::Telnet; - /// - /// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// let event = connection.read_nonblocking().expect("Read Error"); - /// println!("{:?}", event); - /// ``` - /// - pub fn read_nonblocking(&mut self) -> io::Result { - if self.event_queue.is_empty() { - // Set stream settings - self.stream.set_nonblocking(true).expect("set_nonblocking call failed"); - self.stream.set_read_timeout(None).expect("set_read_timeout call failed"); - - // Read bytes to the buffer - match self.stream.read(&mut self.buffer) { - Ok(size) => { - self.buffered_size = size; - }, - Err(e) => { - if e.kind() == ErrorKind::WouldBlock { - return Ok(TelnetEvent::NoData); - } else { - return Err(e); - } - } - } - - self.process(); - } - - // Return an event - Ok( - match self.event_queue.take_event() { - Some(x) => x, - None => TelnetEvent::Error("Internal Queue error".to_string()) - } - ) - - } - - /// - /// Writes a given data block to the remote host. It will double any IAC byte. - /// - /// # Examples - /// ```rust,should_panic - /// use telnet::Telnet; - /// - /// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// let buffer: [u8; 4] = [83, 76, 77, 84]; - /// connection.write(&buffer).expect("Write Error"); - /// ``` - /// - pub fn write(&mut self, data: &[u8]) -> io::Result { - let mut write_size = 0; - - let mut start = 0; - for i in 0..data.len() { - if data[i] == BYTE_IAC { - self.stream.write(&data[start .. i + 1])?; - self.stream.write(&[BYTE_IAC])?; - write_size = write_size + (i + 1 - start); - start = i + 1; - } - } - - if start < data.len() { - self.stream.write(&data[start .. data.len()])?; - write_size = write_size + (data.len() - start); - } - - Ok(write_size) - } - - /// - /// Negotiates a telnet option with the remote host. - /// - /// # Examples - /// ```rust,should_panic - /// use telnet::{Telnet, NegotiationAction, TelnetOption}; - /// - /// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// connection.negotiate(NegotiationAction::Will, TelnetOption::Echo); - /// ``` - /// - pub fn negotiate(&mut self, action: NegotiationAction, opt: TelnetOption) { - let buf: &[u8] = &[BYTE_IAC, action.to_byte(), opt.to_byte()]; - self.stream.write(buf).expect("Error sending negotiation"); - } - - /// - /// Send data for sub-negotiation with the remote host. - /// - /// # Examples - /// ```rust,should_panic - /// use telnet::{Telnet, NegotiationAction, TelnetOption}; - /// - /// let mut connection = Telnet::connect(("127.0.0.1", 23), 256) - /// .expect("Couldn't connect to the server..."); - /// connection.negotiate(NegotiationAction::Do, TelnetOption::TTYPE); - /// let data: [u8; 1] = [1]; - /// connection.subnegotiate(TelnetOption::TTYPE, &data); - /// ``` - /// - pub fn subnegotiate(&mut self, opt: TelnetOption, data: &[u8]) { - let buf: &[u8] = &[BYTE_IAC, BYTE_SB, opt.to_byte()]; - self.stream.write(buf).expect("Error sending subnegotiation (START)"); - - self.stream.write(data).expect("Error sending subnegotiation (DATA)"); +pub struct Writer(W); - let buf: &[u8] = &[BYTE_IAC, BYTE_SE]; - self.stream.write(buf).expect("Error sending subnegotiation (END)"); +impl Writer { + pub fn new(writer: W) -> Self { + Self(writer) } - fn process(&mut self) { - let mut current = 0; - let mut state = ProcessState::NormalData; - let mut data_start = 0; - - while current < self.buffered_size { - // Gather a byte - let byte = self.buffer[current]; - - // Process the byte - match state { - // Normal Data - ProcessState::NormalData => { - if byte == BYTE_IAC { - // The following bytes will be commands - - // Update the state - state = ProcessState::IAC; - - // Send the data before this byte - if current > data_start { - let data_end = current; - let data = self.copy_buffered_data(data_start, data_end); - self.event_queue.push_event(TelnetEvent::Data(data)); - - // Update the state - data_start = current; - } - } else if current == self.buffered_size - 1 { - // It reaches the end of the buffer - let data_end = self.buffered_size; - let data = self.copy_buffered_data(data_start, data_end); - self.event_queue.push_event(TelnetEvent::Data(data)); - } - }, - - // Telnet Commands - ProcessState::IAC => { - match byte { - // Negotiation Commands - BYTE_WILL => state = ProcessState::Will, - BYTE_WONT => state = ProcessState::Wont, - BYTE_DO => state = ProcessState::Do, - BYTE_DONT => state = ProcessState::Dont, - // Subnegotiation - BYTE_SB => state = ProcessState::SB, - // Escaping - // TODO: Write a test case for this - BYTE_IAC => { - // Copy the data to the process buffer - self.append_data_to_proc_buffer(data_start, current - 1); - - // Add escaped IAC - self.process_buffer[self.process_buffered_size] = BYTE_IAC; - self.process_buffered_size += 1; - - // Update the state - state = ProcessState::NormalData; - data_start = current + 1; - }, - // Unknown IAC commands - _ => { - state = ProcessState::NormalData; - data_start = current + 1; - self.event_queue.push_event(TelnetEvent::UnknownIAC(byte)); - } - } - }, - - // Negotiation - ProcessState::Will | ProcessState::Wont | - ProcessState::Do | ProcessState::Dont => { - - let opt = TelnetOption::parse(byte); - - match state { - ProcessState::Will => { - self.event_queue.push_event( - TelnetEvent::Negotiation(NegotiationAction::Will, opt)); - }, - ProcessState::Wont => { - self.event_queue.push_event( - TelnetEvent::Negotiation(NegotiationAction::Wont, opt)); - }, - ProcessState::Do => { - self.event_queue.push_event( - TelnetEvent::Negotiation(NegotiationAction::Do, opt)); - }, - ProcessState::Dont => { - self.event_queue.push_event( - TelnetEvent::Negotiation(NegotiationAction::Dont, opt)); - }, - _ => {} // Do nothing - } - - state = ProcessState::NormalData; - data_start = current + 1; - }, - - // Start subnegotiation - ProcessState::SB => { - let opt = TelnetOption::parse(byte); - state = ProcessState::SBData(opt, current + 1); - }, - - // Subnegotiation's data - ProcessState::SBData(opt, data_start) => { - if byte == BYTE_IAC { - state = ProcessState::SBDataIAC(opt, data_start); - } - - // XXX: We may need to consider the case that a SB Data - // sequence may exceed this buffer - }, - - // IAC inside Subnegotiation's data - ProcessState::SBDataIAC(opt, sb_data_start) => { - match byte { - // The end of subnegotiation - BYTE_SE => { - // Update state - state = ProcessState::NormalData; - data_start = current + 1; - - // Return the option - let sb_data_end = current - 1; - let data = self.copy_buffered_data(sb_data_start, sb_data_end); - self.event_queue.push_event(TelnetEvent::Subnegotiation(opt, data)); - }, - // Escaping - // TODO: Write a test case for this - BYTE_IAC => { - // Copy the data to the process buffer - self.append_data_to_proc_buffer(sb_data_start, current - 1); - - // Add escaped IAC - self.process_buffer[self.process_buffered_size] = BYTE_IAC; - self.process_buffered_size += 1; - - // Update the state - state = ProcessState::SBData(opt, current + 1); - }, - // TODO: Write a test case for this - b => { - self.event_queue.push_event(TelnetEvent::Error( - format!("Unexpected byte after IAC inside SB: {}", b))); - - // Copy the data to the process buffer - self.append_data_to_proc_buffer(sb_data_start, current - 1); - // Update the state - state = ProcessState::SBData(opt, current + 1); - } - } - } - } - - // Move to the next byte - current += 1; + pub fn write_all(&mut self, buffer: &[u8]) -> Result<()> { + for buf in format::data(buffer) { + self.0.write_all(buf)?; } + Ok(()) } - // Copy the data to the process buffer - fn append_data_to_proc_buffer(&mut self, data_start: usize, data_end: usize) { - let data_length = data_end - data_start; - let dst_start = self.process_buffered_size; - let dst_end = self.process_buffered_size + data_length; - let dst = &mut self.process_buffer[dst_start .. dst_end]; - dst.copy_from_slice(&self.buffer[data_start .. data_end]); - self.process_buffered_size += data_length; + pub fn negotiate(&mut self, action: NegotiationAction, opt: TelnetOption) -> Result<()> { + let buf = format::negotiation(action, opt); + self.0.write_all(&buf) } - fn copy_buffered_data(&mut self, data_start: usize, data_end: usize) -> Box<[u8]> { - let data = if self.process_buffered_size > 0 { - // Copy the data to the process buffer - self.append_data_to_proc_buffer(data_start, data_end); - - let pbe = self.process_buffered_size; - self.process_buffered_size = 0; - - &self.process_buffer[0 .. pbe] - } else { - &self.buffer[data_start .. data_end] - }; - - Box::from(data) + pub fn sub_negotiate(&mut self, opt: TelnetOption, parameters: &[u8]) -> Result<()> { + let buf = format::sub_negotiation(opt, parameters); + self.0.write_all(&buf) } } #[cfg(test)] mod tests { use super::*; - use std::io::Error; - use std::ops::Deref; - - struct MockStream { - test_data: Vec - } - - impl MockStream { - fn new(data: Vec) -> MockStream { - MockStream { - test_data: data, - } - } - } - - impl stream::Stream for MockStream { - fn set_nonblocking(&self, _nonblocking: bool) -> Result<(), Error> { - return Ok(()) - } - - fn set_read_timeout(&self, _dur: Option) -> Result<(), Error> { - return Ok(()) - } - } - - impl io::Read for MockStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let mut offset = 0; - while offset < buf.len() && offset < self.test_data.len() { - buf[offset] = self.test_data[offset]; - offset += 1; - } - return Ok(offset); - } - } - - impl io::Write for MockStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - return Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - return Ok(()) - } - } - - #[test] - fn escapes_double_iac_correctly() { - let stream = MockStream::new(vec!(0x40, 0x5a, 0xff, 0xff, 0x31, 0x34)); - #[cfg(feature = "zcstream")] - let stream = ZlibStream::from_stream(stream); - let stream = Box::new(stream); - - let mut telnet = Telnet::from_stream(stream, 6); - - let expected_bytes_1: [u8;2] = [0x40, 0x5a]; - let expected_bytes_2: [u8;3] = [0xff, 0x31, 0x34]; - - let event_1 = telnet.read_nonblocking().unwrap(); - match event_1 { - TelnetEvent::Data(buffer) => { - assert_eq!(buffer.deref(), &expected_bytes_1); - }, - _ => { - assert!(false); - } - } - - let event_2 = telnet.read_nonblocking().unwrap(); - match event_2 { - TelnetEvent::Data(buffer) => { - assert_eq!(buffer.deref(), &expected_bytes_2); - }, - _ => { - assert!(false); - } - } - } } diff --git a/src/negotiation.rs b/src/negotiation.rs index bc6e4de..72f59a2 100644 --- a/src/negotiation.rs +++ b/src/negotiation.rs @@ -5,7 +5,7 @@ use byte::*; /// /// Actions for telnet negotiation. /// -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum NegotiationAction { Will, Wont, Do, Dont } diff --git a/src/option.rs b/src/option.rs index 11fdd09..0163bef 100644 --- a/src/option.rs +++ b/src/option.rs @@ -4,7 +4,7 @@ macro_rules! telnet_option { /// /// Telnet options /// - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TelnetOption { $($tno,)+ UnknownOption(u8), diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..a46cf0b --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,225 @@ +use byte::*; +use event::{Events, Event}; +use negotiation::NegotiationAction; +use option::TelnetOption; + +#[derive(Debug)] +enum ParsingState { + NormalData(usize), + IAC, + SB, + SBData(TelnetOption, Vec, bool), // option, data, iac + Negotiation(NegotiationAction), +} + +pub struct Parser { + state: ParsingState, +} + +impl Parser { + /// + /// Create new parser. + /// + pub fn new() -> Parser { + Parser { + state: ParsingState::NormalData(0), + } + } + + /// + /// Parses all the `Event`s from the supplied buffer. + /// + /// # Examples + /// ```rust + /// use telnet::{Parser, Event::Data}; + /// + /// let buffer = [1, 2, 0xFF, 0xFF, 3, 4]; + /// + /// let mut parser = Parser::new(); + /// let events: Vec<_> = parser.parse(&buffer).collect(); + /// + /// assert_eq!(events, vec![Data(&[1, 2]), Data(&[0xFF, 3, 4])]); + /// ``` + /// + pub fn parse<'a>(&mut self, buffer: &'a [u8]) -> Events<'a> { + if let ParsingState::NormalData(data_start) = self.state { + assert!(data_start == 0); + } + + let mut events: Vec = (0..buffer.len()) + .filter_map(|i| self.parse_byte(buffer, i)) + .collect(); + + if let ParsingState::NormalData(data_start) = self.state { + if data_start < buffer.len() { + events.push(Event::Data(&buffer[data_start..])); + } + + // Reset for next call to read + self.state = ParsingState::NormalData(0); + } + + events.into() + } + + /// + /// Parses a single byte from the supplied buffer. + /// + /// Expects to be called as part of a loop where `index`, starting at 0, is incremented by 1 + /// on each call until the end of the buffer is reached. + /// + fn parse_byte<'a>(&mut self, buffer: &'a [u8], index: usize) -> Option> { + let byte = buffer[index]; + + match self.state { + // Normal Data + ParsingState::NormalData(data_start) => { + if byte == BYTE_IAC { + // The following bytes will be commands + + // Update the state + self.state = ParsingState::IAC; + + // Send the data before this byte + if data_start < index { + return Some(Event::Data(&buffer[data_start..index])); + } + } + } + + // Telnet Commands + ParsingState::IAC => { + let mut err = false; + + self.state = match byte { + // Negotiation Commands + BYTE_WILL => ParsingState::Negotiation(NegotiationAction::Will), + BYTE_WONT => ParsingState::Negotiation(NegotiationAction::Wont), + BYTE_DO => ParsingState::Negotiation(NegotiationAction::Do), + BYTE_DONT => ParsingState::Negotiation(NegotiationAction::Dont), + // Subnegotiation + BYTE_SB => ParsingState::SB, + // Escaping + // TODO: Write a test case for this + BYTE_IAC => ParsingState::NormalData(index), + // Unknown IAC commands + _ => { + err = true; + ParsingState::NormalData(index + 1) + } + }; + + // TODO: Write a test case for this + if err { + return Some(Event::UnknownIAC(byte)); + } + } + + // Negotiation + ParsingState::Negotiation(action) => { + self.state = ParsingState::NormalData(index + 1); + let opt = TelnetOption::parse(byte); + return Some(Event::Negotiation(action, opt)); + } + + // Start subnegotiation + ParsingState::SB => { + let opt = TelnetOption::parse(byte); + self.state = ParsingState::SBData(opt, Vec::new(), false); + } + + // Subnegotiation's data + ParsingState::SBData(opt, ref mut data, ref mut iac) => { + if *iac { + // IAC inside Subnegotiation's data + *iac = false; + + match byte { + // The end of subnegotiation + BYTE_SE => { + let data_boxed = data.clone().into(); + self.state = ParsingState::NormalData(index + 1); + return Some(Event::Subnegotiation(opt, data_boxed)); + } + // Escaping + BYTE_IAC => data.push(BYTE_IAC), + // TODO: Write a test case for this + b => { + return Some(Event::Error(format!( + "Unexpected byte after IAC inside SB: {}", + b + ))) + } + } + } else { + if byte == BYTE_IAC { + *iac = true; + } else { + data.push(byte); + } + } + } + } + + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn parse_test(buffer: &[u8], result: Vec) { + let mut parser = Parser::new(); + for i in 0..buffer.len() { + let events: Vec<_> = parser.parse(&buffer[..i]) + .chain(parser.parse(&buffer[i..])) + .collect(); + assert_eq!(events, result); + } + } + + #[test] + fn parse_double_iac() { + let buffer = [1, 0xFF, 0xFF, 0xFF, 0xFF]; + parse_test(&buffer, vec![ + Event::Data(&[1]), + Event::Data(&[0xFF]), + Event::Data(&[0xFF]) + ]); + } + + #[test] + fn parse_iac_only() { + let buffer = [0xFF, 0xFF]; + parse_test(&buffer, vec![Event::Data(&[0xFF])]); + } + + #[test] + fn parse_negotiation() { + let buffer = [1, 0xFF, 0xFB, 0x01, 2]; + parse_test(&buffer, vec![ + Event::Data(&[1]), + Event::Negotiation(NegotiationAction::Will, TelnetOption::Echo), + Event::Data(&[2]) + ]); + } + + #[test] + fn parse_sub_negotiation() { + let buffer = [1, 0xFF, 0xFA, 24, 1, 2, 3, 0xFF, 0xF0, 2]; + parse_test(&buffer, vec![ + Event::Data(&[1]), + Event::Subnegotiation(TelnetOption::TTYPE, vec![1, 2, 3].into()), + Event::Data(&[2]) + ]); + } + + #[test] + fn parse_iac_sub_negotiation() { + let buffer = [0xFF, 0xFA, 24, 1, 0xFF, 0xFF, 3, 0xFF, 0xF0]; + parse_test(&buffer, vec![ + Event::Subnegotiation(TelnetOption::TTYPE, vec![1, 0xFF, 3].into()), + ]); + } +}