diff --git a/CHANGELOG.md b/CHANGELOG.md index 48eda6c..4045765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ Games: - [Just Cause 2: Multiplayer](https://store.steampowered.com/app/259080/Just_Cause_2_Multiplayer_Mod/) support. - [Warsow](https://warsow.net/) support. +Internal: +- Buffer reader rewrite, resulting in more data checks and better code quality (thanks [@cainthebest](https://github.com/cainthebest)). + ### Breaking... Protocols: - Quake 2: Renamed the players frags field to score to be more inline with the other protocols. diff --git a/src/buffer.rs b/src/buffer.rs new file mode 100644 index 0000000..692f547 --- /dev/null +++ b/src/buffer.rs @@ -0,0 +1,548 @@ +use crate::GDError::{PacketBad, PacketUnderflow}; +use crate::GDResult; +use byteorder::{BigEndian, ByteOrder, LittleEndian}; +use std::{convert::TryInto, marker::PhantomData}; + +/// A struct representing a buffer with a specific byte order. +/// +/// It's comprised of a byte slice that it reads from, a cursor to keep track of +/// the current position within the byte slice, and a `PhantomData` marker to +/// bind it to a specific byte order (BigEndian or LittleEndian). +/// +/// The byte order is defined by the `B: ByteOrder` generic parameter. +pub(crate) struct Buffer<'a, B: ByteOrder> { + /// The byte slice that the buffer reads from. + data: &'a [u8], + /// The cursor marking our current position in the buffer. + cursor: usize, + /// A phantom field used to bind the `Buffer` to a specific `ByteOrder`. + _marker: PhantomData, +} + +impl<'a, B: ByteOrder> Buffer<'a, B> { + /// Creates and returns a new `Buffer` with the given data. + /// + /// The cursor is set to the start of the buffer (position 0) upon + /// initialization. + /// + /// # Arguments + /// + /// * `data` - A byte slice that the buffer will read from. + pub(crate) fn new(data: &'a [u8]) -> Self { + Self { + data, + cursor: 0, + _marker: PhantomData, + } + } + + pub(crate) fn current_position(&self) -> usize { self.cursor } + + /// Returns the length of the remaining bytes from the current cursor + /// position. + pub(crate) fn remaining_length(&self) -> usize { self.data.len() - self.cursor } + + /// Returns the length of the buffer data. + pub(crate) fn data_length(&self) -> usize { self.data.len() } + + // Added for legacy support just for the refactoring + // Not Tested + pub(crate) fn remaining_bytes(&self) -> &[u8] { &self.data[self.cursor ..] } + + /// Moves the cursor forward or backward by a specified offset. + /// + /// # Arguments + /// + /// * `offset` - The amount to move the cursor. Use a negative value to move + /// backwards. + /// + /// # Errors + /// + /// Returns a `BufferError` if the attempted move would position the cursor + /// out of bounds. + pub(crate) fn move_cursor(&mut self, offset: isize) -> GDResult<()> { + // Compute the new cursor position by adding the offset to the current cursor + // position. The checked_add method is used for safe addition, + // preventing overflow and underflow. + let new_cursor = (self.cursor as isize).checked_add(offset); + + match new_cursor { + // If the addition was not successful (i.e., it resulted in an overflow or underflow), + // return an error indicating that the cursor is out of bounds. + None => Err(PacketBad), + + // If the new cursor position is either less than zero (i.e., before the start of the buffer) + // or greater than the remaining length of the buffer (i.e., past the end of the buffer), + // return an error indicating that the cursor is out of bounds. + Some(x) if x < 0 || x as usize > self.data_length() => Err(PacketBad), + + // If the new cursor position is within the bounds of the buffer, update the cursor + // position and return Ok. + Some(x) => { + self.cursor = x as usize; + Ok(()) + } + } + } + + /// Reads a value of type `T` from the buffer, and advances the cursor by + /// the size of `T`. + /// + /// # Type Parameters + /// + /// * `T` - The type of value to be read from the buffer. This type must + /// implement the `BufferRead` trait with the same byte order as the + /// buffer. + /// + /// # Errors + /// + /// Returns a `BufferError` if there is not enough data remaining in the + /// buffer to read a value of type `T`. + pub(crate) fn read>(&mut self) -> GDResult { + // Get the size of `T` in bytes. + let size = std::mem::size_of::(); + // Calculate remaining length of the buffer. + let remaining = self.remaining_length(); + + // If the size of `T` is larger than the remaining length, return an error + // because we don't have enough data left to read. + if size > remaining { + return Err(PacketUnderflow); + } + + // Slice the data array from the current cursor position for `size` amount of + // bytes. + let bytes = &self.data[self.cursor .. self.cursor + size]; + + // Move the cursor forward by `size`. + self.cursor += size; + + // Use the `read_from_buffer` function of the `BufferRead` implementation for + // `T` to convert the bytes into an instance of `T`. + T::read_from_buffer(bytes) + } + + /// Reads a string from the buffer using a specified `StringDecoder`, until + /// an optional delimiter. + /// + /// # Type Parameters + /// + /// * `D` - The type of string decoder to use. This type must implement the + /// `StringDecoder` trait with the same byte order as the buffer. + /// + /// # Arguments + /// + /// * `until` - An optional delimiter. If provided, the method will read + /// until this + /// delimiter is encountered. If not provided, the method will read until + /// the default delimiter of the decoder. + /// + /// # Errors + /// + /// Returns a `BufferError` if there is an error decoding the string. + pub(crate) fn read_string(&mut self, until: Option) -> GDResult { + // Slice the data array from the current cursor position to the end. + let data_slice = &self.data[self.cursor ..]; + + // Use the provided delimiter if one was given, or default to the + // delimiter specified by the StringDecoder. + let delimiter = until.unwrap_or(D::DELIMITER); + + // Invoke the decode_string function of the provided StringDecoder, + // passing in the remaining data slice, the mutable reference to the + // cursor, and the delimiter. + let result = D::decode_string(data_slice, &mut self.cursor, delimiter)?; + + // If decoding was successful, return the decoded string. The cursor + // position has been updated within the decode_string call to reflect + // the new position after reading. + Ok(result) + } +} + +/// A trait that provides an interface to switch endianness. +/// +/// The trait `SwitchEndian` is used for types that have a specific +/// byte order (endianness) and can switch to another byte order. +/// The type of the switched endianness is determined by the associated +/// type `Output`. +/// +/// The associated type `Output` must implement the `ByteOrder` trait. +pub(crate) trait SwitchEndian { + type Output: ByteOrder; +} + +/// An implementation of `SwitchEndian` for `LittleEndian`. +/// +/// The switched endianness type is `BigEndian`. +impl SwitchEndian for LittleEndian { + type Output = BigEndian; +} + +/// An implementation of `SwitchEndian` for `BigEndian`. +/// +/// The switched endianness type is `LittleEndian`. +impl SwitchEndian for BigEndian { + type Output = LittleEndian; +} + +impl<'a, B: SwitchEndian + ByteOrder> Buffer<'a, B> { + /// Switches the byte order of a chunk in the buffer. + /// + /// This method consumes the buffer and returns a new buffer + /// with a chunk of the original buffer's data, starting from the + /// original cursor position and of the given size, where the byte + /// order is switched according to the implementation + /// of `SwitchEndian` for `B`. + /// + /// Note: The method also advances the cursor of the original buffer + /// by `size`. + /// + /// # Parameters + /// + /// * `size`: The size of the chunk to be taken from the original buffer. + pub(crate) fn switch_endian_chunk(&mut self, size: usize) -> GDResult> { + let old_cursor = self.cursor; + self.move_cursor(size as isize)?; + + Ok(Buffer { + data: &self.data[old_cursor .. old_cursor + size], + cursor: 0, + _marker: PhantomData, + }) + } +} + +/// A trait defining a protocol for reading values of a certain type from a +/// buffer. +/// +/// Implementors of this trait provide a method for reading their type from a +/// byte buffer with a specific byte order. +pub(crate) trait BufferRead: Sized { + fn read_from_buffer(data: &[u8]) -> GDResult; +} + +/// Macro to implement the `BufferRead` trait for byte types. +/// +/// This macro generates an implementation of the `BufferRead` trait for a +/// specified byte type. The implementation will read a single byte from the +/// buffer and convert it to the target type using the provided map function. +/// +/// # Arguments +/// +/// * `$type` - The target type to implement `BufferRead` for. +/// * `$map_func` - The function to map a byte to the target type. +macro_rules! impl_buffer_read_byte { + ($type:ty, $map_func:expr) => { + impl BufferRead for $type { + fn read_from_buffer(data: &[u8]) -> GDResult { + // Use the `first` method to get the first byte from the data array. + data.first() + // Apply the $map_func function to convert the raw byte to the $type. + .map($map_func) + // If the data array is empty (and thus `first` returns None), + // `ok_or_else` will return a BufferError. + .ok_or_else(|| PacketBad) + } + } + }; +} + +/// Macro to implement the `BufferRead` trait for multi-byte types. +/// +/// This macro generates an implementation of the `BufferRead` trait for a +/// specified multi-byte type. The implementation will read the appropriate +/// number of bytes from the buffer and convert them to the target type using +/// the provided read function. +/// +/// # Arguments +/// +/// * `$type` - The target type to implement `BufferRead` for. +/// * `$read_func` - The function to read the bytes into the target type. +macro_rules! impl_buffer_read { + ($type:ty, $read_func:ident) => { + impl BufferRead for $type { + fn read_from_buffer(data: &[u8]) -> GDResult { + // Convert the byte slice into an array of the appropriate type. + let array = data.try_into().map_err(|_| { + // If conversion fails, return an error indicating the required and provided + // lengths. + PacketBad + })?; + + // Use the provided function to read the data from the array into the given + // type. + Ok(B::$read_func(array)) + } + } + }; +} + +impl_buffer_read_byte!(u8, |&b| b); +impl_buffer_read_byte!(i8, |&b| b as i8); + +impl_buffer_read!(u16, read_u16); +impl_buffer_read!(i16, read_i16); +impl_buffer_read!(u32, read_u32); +impl_buffer_read!(i32, read_i32); +impl_buffer_read!(u64, read_u64); +impl_buffer_read!(i64, read_i64); +impl_buffer_read!(f32, read_f32); +impl_buffer_read!(f64, read_f64); + +/// A trait defining a protocol for decoding strings from a buffer. +/// +/// This trait should be implemented by types that can decode strings from a +/// byte buffer with a specific byte order and delimiter. +pub(crate) trait StringDecoder { + /// The type of the delimiter used by the decoder. + type Delimiter: AsRef<[u8]>; + + /// The default delimiter used by the decoder. + const DELIMITER: Self::Delimiter; + + /// Decodes a string from the provided byte slice, and updates the cursor + /// position accordingly. + /// + /// # Arguments + /// + /// * `data` - The byte slice to decode the string from. + /// * `cursor` - The current position in the byte slice. + /// * `delimiter` - The delimiter to use for decoding the string. + /// + /// # Errors + /// + /// Returns a `BufferError` if there is an error decoding the string. + fn decode_string(data: &[u8], cursor: &mut usize, delimiter: Self::Delimiter) -> GDResult; +} + +/// A decoder for UTF-8 encoded strings. +/// +/// This decoder uses a single null byte (`0x00`) as the default delimiter. +pub(crate) struct Utf8Decoder; + +impl StringDecoder for Utf8Decoder { + type Delimiter = [u8; 1]; + + const DELIMITER: Self::Delimiter = [0x00]; + + /// Decodes a UTF-8 string from the given data, updating the cursor position + /// accordingly. + fn decode_string(data: &[u8], cursor: &mut usize, delimiter: Self::Delimiter) -> GDResult { + // Find the position of the delimiter in the data. If the delimiter is not + // found, the length of the data is returned. + let position = data + // Create an iterator over the data. + .iter() + // Find the position of the delimiter + .position(|&b| b == delimiter.as_ref()[0]) + // If the delimiter is not found, use the whole data slice. + .unwrap_or(data.len()); + + // Convert the data until the found position into a UTF-8 string. + let result = std::str::from_utf8( + // Take a slice of data until the position. + &data[.. position] + ) + // If the data cannot be converted into a UTF-8 string, return an error + .map_err(|_| PacketBad)? + // Convert the resulting &str into a String + .to_owned(); + + // Update the cursor position + // The +1 is to skip the delimiter + *cursor += position + 1; + + Ok(result) + } +} + +/// A decoder for UTF-16 encoded strings. +/// +/// This decoder uses a pair of null bytes (`0x00, 0x00`) as the default +/// delimiter. +/// +/// # Type Parameters +/// +/// * `B` - The byte order to use when decoding the string. +pub(crate) struct Utf16Decoder { + _marker: PhantomData, +} + +impl StringDecoder for Utf16Decoder { + type Delimiter = [u8; 2]; + + const DELIMITER: Self::Delimiter = [0x00, 0x00]; + + /// Decodes a UTF-16 string from the given data, updating the cursor + /// position accordingly. + fn decode_string(data: &[u8], cursor: &mut usize, delimiter: Self::Delimiter) -> GDResult { + // Try to find the position of the delimiter in the data + let position = data + // Split the data into 2-byte chunks (as UTF-16 uses 2 bytes per character) + .chunks_exact(2) + // Find the position of the delimiter + .position(|chunk| chunk == delimiter.as_ref()) + // If the delimiter is not found, use the whole data, otherwise use the position of the delimiter + .map_or(data.len(), |pos| pos * 2); + + // Create a buffer of u16 values to hold the decoded characters + let mut paired_buf: Vec = vec![0; position / 2]; + + // Decode the data into the buffer + B::read_u16_into(&data[.. position], &mut paired_buf); + + // Convert the buffer of u16 values into a String + let result = String::from_utf16(&paired_buf).map_err(|_| PacketBad)?; + + // Update the cursor position + // The +2 accounts for the delimiter + *cursor += position + 2; + + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use byteorder::BigEndian; + + #[test] + fn test_new_buffer() { + let data: &[u8] = &[1, 2, 3, 4]; + let buffer = Buffer::::new(data); + + assert_eq!(buffer.data, data); + assert_eq!(buffer.cursor, 0); + } + + #[test] + fn test_remaining_length() { + let data: &[u8] = &[1, 2, 3, 4]; + let mut buffer = Buffer::::new(data); + + assert_eq!(buffer.remaining_length(), 4); + + buffer.cursor = 2; + assert_eq!(buffer.remaining_length(), 2); + } + + #[test] + fn test_move_cursor() { + let data: &[u8] = &[1, 2, 3, 4]; + let mut buffer = Buffer::::new(data); + + // Test moving forward + assert!(buffer.move_cursor(2).is_ok()); + assert_eq!(buffer.cursor, 2); + + // Test moving backward + assert!(buffer.move_cursor(-1).is_ok()); + assert_eq!(buffer.cursor, 1); + + // Test moving beyond data limits + assert!(buffer.move_cursor(5).is_err()); + assert!(buffer.move_cursor(-2).is_err()); + } + + #[test] + fn test_switch_endian_chunk_le_be() { + let data = [0x01, 0x02, 0x03, 0x04]; + let mut buffer = Buffer::::new(&data[..]); + + let switched_buffer = buffer.switch_endian_chunk(2).unwrap(); + + assert_eq!(switched_buffer.data, [0x01, 0x02]); + assert_eq!(switched_buffer.cursor, 0); + + assert_eq!(buffer.remaining_bytes(), [0x03, 0x04]); + assert_eq!(buffer.cursor, 2); + } + + #[test] + fn test_switch_endian_chunk_be_le() { + let data = [0x01, 0x02, 0x03, 0x04]; + let mut buffer = Buffer::::new(&data[..]); + + let switched_buffer = buffer.switch_endian_chunk(2).unwrap(); + + assert_eq!(switched_buffer.data, [0x01, 0x02]); + assert_eq!(switched_buffer.cursor, 0); + + assert_eq!(buffer.remaining_bytes(), [0x03, 0x04]); + assert_eq!(buffer.cursor, 2); + } + + #[test] + fn test_buffer_read_u8() { + let data: &[u8] = &[1, 2, 3, 4]; + let mut buffer = Buffer::::new(data); + + let result: Result = buffer.read(); + assert_eq!(result.unwrap(), 1); + assert_eq!(buffer.cursor, 1); + } + + #[test] + fn test_buffer_read_u16() { + let data: &[u8] = &[1, 2, 3, 4]; + let mut buffer = Buffer::::new(data); + + let result: Result = buffer.read(); + assert_eq!(result.unwrap(), 0x0201); + assert_eq!(buffer.cursor, 2); + } + + #[test] + fn test_buffer_read_u16_big_endian() { + let data: &[u8] = &[1, 2, 3, 4]; + let mut buffer = Buffer::::new(data); + + let result: Result = buffer.read(); + assert_eq!(result.unwrap(), 0x0102); + assert_eq!(buffer.cursor, 2); + } + + #[test] + fn test_decode_string_utf8() { + let data: &[u8] = b"Hello\0World\0"; + let mut cursor = 0; + let delimiter = [0x00]; + + let result = Utf8Decoder::decode_string(data, &mut cursor, delimiter); + assert_eq!(result.unwrap(), "Hello"); + assert_eq!(cursor, 6); + } + + #[test] + fn test_decode_string_utf16_le() { + let data: &[u8] = &[0x48, 0x00, 0x65, 0x00, 0x00, 0x00]; + let mut cursor = 0; + let delimiter = [0x00, 0x00]; + + let result = Utf16Decoder::::decode_string(data, &mut cursor, delimiter); + assert_eq!(result.unwrap(), "He"); + assert_eq!(cursor, 6); + } + + #[test] + fn test_decode_string_utf16_be() { + let data: &[u8] = &[0x00, 0x48, 0x00, 0x65, 0x00, 0x00]; + let mut cursor = 0; + let delimiter = [0x00, 0x00]; + + let result = Utf16Decoder::::decode_string(data, &mut cursor, delimiter); + assert_eq!(result.unwrap(), "He"); + assert_eq!(cursor, 6); + } + + #[test] + fn test_buffer_underflow_error() { + let data: &[u8] = &[1, 2]; + let mut buffer = Buffer::::new(data); + + let result: Result = buffer.read(); + assert_eq!(result.unwrap_err(), PacketUnderflow); + } +} diff --git a/src/bufferer.rs b/src/bufferer.rs deleted file mode 100644 index a7df551..0000000 --- a/src/bufferer.rs +++ /dev/null @@ -1,308 +0,0 @@ -use crate::{ - GDError::{PacketBad, PacketUnderflow}, - GDResult, -}; - -use byteorder::{BigEndian, ByteOrder, LittleEndian}; - -pub enum Endianess { - Little, - Big, -} - -pub struct Bufferer { - data: Vec, - endianess: Endianess, - position: usize, -} - -impl Bufferer { - pub fn new_with_data(endianess: Endianess, data: &[u8]) -> Self { - Bufferer { - data: data.to_vec(), - endianess, - position: 0, - } - } - - fn check_size(&self, by: usize) -> bool { by > self.remaining_length() } - - pub fn get_u8(&mut self) -> GDResult { - if self.check_size(1) { - return Err(PacketUnderflow); - } - - let value = self.data[self.position]; - self.move_position_ahead(1); - Ok(value) - } - - pub fn get_u16(&mut self) -> GDResult { - if self.check_size(2) { - return Err(PacketUnderflow); - } - - let value = match self.endianess { - Endianess::Little => LittleEndian::read_u16(self.remaining_data()), - Endianess::Big => BigEndian::read_u16(self.remaining_data()), - }; - - self.move_position_ahead(2); - Ok(value) - } - - pub fn get_u32(&mut self) -> GDResult { - if self.check_size(4) { - return Err(PacketUnderflow); - } - - let value = match self.endianess { - Endianess::Little => LittleEndian::read_u32(self.remaining_data()), - Endianess::Big => BigEndian::read_u32(self.remaining_data()), - }; - - self.move_position_ahead(4); - Ok(value) - } - - pub fn get_f32(&mut self) -> GDResult { - if self.check_size(4) { - return Err(PacketUnderflow); - } - - let value = match self.endianess { - Endianess::Little => LittleEndian::read_f32(self.remaining_data()), - Endianess::Big => BigEndian::read_f32(self.remaining_data()), - }; - - self.move_position_ahead(4); - Ok(value) - } - - pub fn get_u64(&mut self) -> GDResult { - if self.check_size(8) { - return Err(PacketUnderflow); - } - - let value = match self.endianess { - Endianess::Little => LittleEndian::read_u64(self.remaining_data()), - Endianess::Big => BigEndian::read_u64(self.remaining_data()), - }; - - self.move_position_ahead(8); - Ok(value) - } - - fn get_string_utf8_until(&mut self, until: u8) -> GDResult { - let sub_buf = self.remaining_data(); - if sub_buf.is_empty() { - return Err(PacketUnderflow); - } - - let first_null_position = sub_buf.iter().position(|&x| x == until).ok_or(PacketBad)?; - let value = std::str::from_utf8(&sub_buf[.. first_null_position]) - .map_err(|_| PacketBad)? - .to_string(); - - self.move_position_ahead(value.len() + 1); - Ok(value) - } - - pub fn get_string_utf8(&mut self) -> GDResult { self.get_string_utf8_until(0) } - - pub fn get_string_utf8_newline(&mut self) -> GDResult { self.get_string_utf8_until(10) } - - pub fn get_string_utf8_optional(&mut self) -> GDResult { - match self.get_string_utf8() { - Ok(data) => Ok(data), - Err(e) => { - match e { - PacketUnderflow => Ok(String::new()), - x => Err(x), - } - } - } - } - - pub fn get_string_utf8_unended(&mut self) -> GDResult { - let sub_buf = self.remaining_data(); - if sub_buf.is_empty() { - return Err(PacketUnderflow); - } - - let value = std::str::from_utf8(sub_buf) - .map_err(|_| PacketBad)? - .to_string(); - - self.move_position_ahead(value.len()); - Ok(value) - } - - pub fn get_string_utf16(&mut self) -> GDResult { - let sub_buf = self.remaining_data(); - if sub_buf.is_empty() { - return Err(PacketUnderflow); - } - - let paired_buf: Vec = sub_buf - .chunks_exact(2) - .map(|pair| { - match self.endianess { - Endianess::Little => LittleEndian::read_u16(pair), - Endianess::Big => BigEndian::read_u16(pair), - } - }) - .collect(); - - let value = String::from_utf16(&paired_buf).map_err(|_| PacketBad)?; - - self.move_position_ahead(value.len() * 2); - Ok(value) - } - - pub fn move_position_ahead(&mut self, by: usize) { self.position += by; } - - pub fn move_position_backward(&mut self, by: usize) { self.position -= by; } - - pub fn remaining_data(&self) -> &[u8] { &self.data[self.position ..] } - - pub fn remaining_length(&self) -> usize { self.data.len() - self.position } - - pub fn is_remaining_empty(&self) -> bool { self.remaining_length() == 0 } - - pub fn as_endianess(&self, endianess: Endianess) -> Self { - Bufferer { - data: self.data.clone(), - endianess, - position: self.position, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn get_u8() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72]); - - assert_eq!(buffer.get_u8().unwrap(), 72); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u8().is_err()); - } - - #[test] - fn get_u16_le() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72, 79]); - - assert_eq!(buffer.get_u16().unwrap(), 20296); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u16().is_err()); - } - - #[test] - fn get_u16_be() { - let mut buffer = Bufferer::new_with_data(Endianess::Big, &[29, 72]); - - assert_eq!(buffer.get_u16().unwrap(), 7496); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u16().is_err()); - } - - #[test] - fn get_u32_le() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72, 29, 128, 100]); - - assert_eq!(buffer.get_u32().unwrap(), 1686117704); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u32().is_err()); - } - - #[test] - fn get_u32_be() { - let mut buffer = Bufferer::new_with_data(Endianess::Big, &[72, 29, 128, 100]); - - assert_eq!(buffer.get_u32().unwrap(), 1209892964); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u32().is_err()); - } - - #[test] - fn get_f32_le() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72, 29, 128, 100]); - - assert_eq!(buffer.get_f32().unwrap(), 1.8906345e22); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_f32().is_err()); - } - - #[test] - fn get_f32_be() { - let mut buffer = Bufferer::new_with_data(Endianess::Big, &[72, 29, 128, 100]); - - assert_eq!(buffer.get_f32().unwrap(), 161281.56); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_f32().is_err()); - } - - #[test] - fn get_u64_le() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72, 29, 128, 99, 69, 4, 2, 0]); - - assert_eq!(buffer.get_u64().unwrap(), 567646022016328); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u64().is_err()); - } - - #[test] - fn get_u64_be() { - let mut buffer = Bufferer::new_with_data(Endianess::Big, &[72, 29, 128, 99, 69, 4, 2, 0]); - - assert_eq!(buffer.get_u64().unwrap(), 5196450708903428608); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_u64().is_err()); - } - - #[test] - fn get_string_utf8() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72, 101, 108, 108, 111, 0, 72]); - - assert_eq!(buffer.get_string_utf8().unwrap(), "Hello"); - assert_eq!(buffer.remaining_length(), 1); - assert!(buffer.get_string_utf8().is_err()); - } - - #[test] - fn get_string_utf8_unended() { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &[72, 101, 108, 108, 111]); - - assert_eq!(buffer.get_string_utf8_unended().unwrap(), "Hello"); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_string_utf8_unended().is_err()); - } - - #[test] - fn get_string_utf16_le() { - let mut buffer = Bufferer::new_with_data( - Endianess::Little, - &[0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00], - ); - - assert_eq!(buffer.get_string_utf16().unwrap(), "Hello"); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_string_utf16().is_err()); - } - - #[test] - fn get_string_utf16_be() { - let mut buffer = Bufferer::new_with_data( - Endianess::Big, - &[0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f], - ); - - assert_eq!(buffer.get_string_utf16().unwrap(), "Hello"); - assert_eq!(buffer.remaining_length(), 0); - assert!(buffer.get_string_utf16().is_err()); - } -} diff --git a/src/games/ffow.rs b/src/games/ffow.rs index efd9a78..4546ed2 100644 --- a/src/games/ffow.rs +++ b/src/games/ffow.rs @@ -1,7 +1,9 @@ +use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::types::{CommonResponse, TimeoutSettings}; use crate::protocols::valve::{Engine, Environment, Server, ValveProtocol}; use crate::protocols::GenericResponse; use crate::GDResult; +use byteorder::LittleEndian; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::net::{IpAddr, SocketAddr}; @@ -68,31 +70,33 @@ pub fn query_with_timeout( &SocketAddr::new(*address, port.unwrap_or(5478)), timeout_settings, )?; - let mut buffer = client.get_request_data( + let data = client.get_request_data( &Engine::GoldSrc(true), 0, 0x46, String::from("LSQ").into_bytes(), )?; - let protocol = buffer.get_u8()?; - let name = buffer.get_string_utf8()?; - let map = buffer.get_string_utf8()?; - let active_mod = buffer.get_string_utf8()?; - let game_mode = buffer.get_string_utf8()?; - let description = buffer.get_string_utf8()?; - let version = buffer.get_string_utf8()?; - buffer.move_position_ahead(2); - let players_online = buffer.get_u8()?; - let players_maximum = buffer.get_u8()?; - let server_type = Server::from_gldsrc(buffer.get_u8()?)?; - let environment_type = Environment::from_gldsrc(buffer.get_u8()?)?; - let has_password = buffer.get_u8()? == 1; - let vac_secured = buffer.get_u8()? == 1; - buffer.move_position_ahead(1); //average fps - let round = buffer.get_u8()?; - let rounds_maximum = buffer.get_u8()?; - let time_left = buffer.get_u16()?; + let mut buffer = Buffer::::new(&data); + + let protocol = buffer.read::()?; + let name = buffer.read_string::(None)?; + let map = buffer.read_string::(None)?; + let active_mod = buffer.read_string::(None)?; + let game_mode = buffer.read_string::(None)?; + let description = buffer.read_string::(None)?; + let version = buffer.read_string::(None)?; + buffer.move_cursor(2)?; + let players_online = buffer.read::()?; + let players_maximum = buffer.read::()?; + let server_type = Server::from_gldsrc(buffer.read::()?)?; + let environment_type = Environment::from_gldsrc(buffer.read::()?)?; + let has_password = buffer.read::()? == 1; + let vac_secured = buffer.read::()? == 1; + buffer.move_cursor(1)?; //average fps + let round = buffer.read::()?; + let rounds_maximum = buffer.read::()?; + let time_left = buffer.read::()?; Ok(Response { protocol, diff --git a/src/games/jc2mp.rs b/src/games/jc2mp.rs index 5b800bc..4a8aaff 100644 --- a/src/games/jc2mp.rs +++ b/src/games/jc2mp.rs @@ -1,9 +1,10 @@ -use crate::bufferer::{Bufferer, Endianess}; +use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::gamespy::common::has_password; use crate::protocols::gamespy::three::{data_to_map, GameSpy3}; use crate::protocols::types::{CommonPlayer, CommonResponse, GenericPlayer, TimeoutSettings}; use crate::protocols::GenericResponse; use crate::{GDError, GDResult}; +use byteorder::BigEndian; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::net::{IpAddr, SocketAddr}; @@ -59,16 +60,16 @@ impl CommonResponse for Response { } fn parse_players_and_teams(packet: Vec) -> GDResult> { - let mut buf = Bufferer::new_with_data(Endianess::Big, &packet); + let mut buf = Buffer::::new(&packet); - let count = buf.get_u16()?; + let count = buf.read::()?; let mut players = Vec::with_capacity(count as usize); - while !buf.is_remaining_empty() { + while buf.remaining_length() != 0 { players.push(Player { - name: buf.get_string_utf8()?, - steam_id: buf.get_string_utf8()?, - ping: buf.get_u16()?, + name: buf.read_string::(None)?, + steam_id: buf.read_string::(None)?, + ping: buf.read::()?, }) } diff --git a/src/lib.rs b/src/lib.rs index b902b9c..2819ab9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ pub mod protocols; #[cfg(feature = "services")] pub mod services; -mod bufferer; +mod buffer; mod socket; mod utils; diff --git a/src/protocols/gamespy/protocols/one/protocol.rs b/src/protocols/gamespy/protocols/one/protocol.rs index 7e689d5..e8fdc08 100644 --- a/src/protocols/gamespy/protocols/one/protocol.rs +++ b/src/protocols/gamespy/protocols/one/protocol.rs @@ -1,6 +1,9 @@ +use byteorder::LittleEndian; + +use crate::buffer::Utf8Decoder; use crate::protocols::gamespy::common::has_password; use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::Buffer, protocols::{ gamespy::one::{Player, Response}, types::TimeoutSettings, @@ -29,9 +32,9 @@ fn get_server_values( while !is_finished { let data = socket.receive(None)?; - let mut bufferer = Bufferer::new_with_data(Endianess::Little, &data); + let mut bufferer = Buffer::::new(&data); - let mut as_string = bufferer.get_string_utf8_unended()?; + let mut as_string = bufferer.read_string::(None)?; as_string.remove(0); let splited: Vec = as_string.split('\\').map(str::to_string).collect(); @@ -47,8 +50,7 @@ fn get_server_values( server_values.insert(key, value); } - is_finished = server_values.contains_key("final"); - server_values.remove("final"); + is_finished = server_values.remove("final").is_some(); let query_data = server_values.get("queryid"); diff --git a/src/protocols/gamespy/protocols/three/protocol.rs b/src/protocols/gamespy/protocols/three/protocol.rs index ae22c01..b2927d4 100644 --- a/src/protocols/gamespy/protocols/three/protocol.rs +++ b/src/protocols/gamespy/protocols/three/protocol.rs @@ -1,4 +1,6 @@ -use crate::bufferer::{Bufferer, Endianess}; +use byteorder::{BigEndian, LittleEndian}; + +use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::gamespy::common::has_password; use crate::protocols::gamespy::three::{Player, Response, Team}; use crate::protocols::types::TimeoutSettings; @@ -73,19 +75,19 @@ impl GameSpy3 { }) } - fn receive(&mut self, size: Option, kind: u8) -> GDResult { + fn receive(&mut self, size: Option, kind: u8) -> GDResult> { let received = self.socket.receive(size.or(Some(PACKET_SIZE)))?; - let mut buf = Bufferer::new_with_data(Endianess::Big, &received); + let mut buf = Buffer::::new(&received); - if buf.get_u8()? != kind { + if buf.read::()? != kind { return Err(GDError::PacketBad); } - if buf.get_u32()? != THIS_SESSION_ID { + if buf.read::()? != THIS_SESSION_ID { return Err(GDError::PacketBad); } - Ok(buf) + Ok(buf.remaining_bytes().to_vec()) } fn make_initial_handshake(&mut self) -> GDResult> { @@ -100,9 +102,10 @@ impl GameSpy3 { .to_bytes(), )?; - let mut buf = self.receive(Some(16), 9)?; + let data = self.receive(Some(16), 9)?; + let mut buf = Buffer::::new(&data); - let challenge_as_string = buf.get_string_utf8()?; + let challenge_as_string = buf.read_string::(None)?; let challenge = challenge_as_string .parse() .map_err(|_| GDError::TypeParse)?; @@ -135,21 +138,22 @@ impl GameSpy3 { let mut expected_number_of_packets: Option = None; while expected_number_of_packets.is_none() || values.len() != expected_number_of_packets.unwrap() { - let mut buf = self.receive(None, 0)?; + let received_data = self.receive(None, 0)?; + let mut buf = Buffer::::new(&received_data); if self.single_packets { - buf.move_position_ahead(11); - return Ok(vec![buf.remaining_data().to_vec()]); + buf.move_cursor(11)?; + return Ok(vec![buf.remaining_bytes().to_vec()]); } - if buf.get_string_utf8()? != "splitnum" { + if buf.read_string::(None)? != "splitnum" { return Err(GDError::PacketBad); } - let id = buf.get_u8()?; + let id = buf.read::()?; let is_last = (id & 0x80) > 0; let packet_id = (id & 0x7f) as usize; - buf.move_position_ahead(1); //unknown byte regarding packet no. + buf.move_cursor(1)?; //unknown byte regarding packet no. if is_last { expected_number_of_packets = Some(packet_id + 1); @@ -159,7 +163,7 @@ impl GameSpy3 { values.push(Vec::new()); } - values[packet_id] = buf.remaining_data().to_vec(); + values[packet_id] = buf.remaining_bytes().to_vec(); } if values.iter().any(|v| v.is_empty()) { @@ -173,19 +177,19 @@ impl GameSpy3 { pub(crate) fn data_to_map(packet: &[u8]) -> GDResult<(HashMap, Vec)> { let mut vars = HashMap::new(); - let mut buf = Bufferer::new_with_data(Endianess::Big, packet); - while !buf.is_remaining_empty() { - let key = buf.get_string_utf8()?; + let mut buf = Buffer::::new(packet); + while buf.remaining_length() != 0 { + let key = buf.read_string::(None)?; if key.is_empty() { break; } - let value = buf.get_string_utf8_optional()?; + let value = buf.read_string::(None)?; vars.insert(key, value); } - Ok((vars, buf.remaining_data().to_vec())) + Ok((vars, buf.remaining_bytes().to_vec())) } /// If there are parsing problems using the `query` function, you can directly @@ -212,16 +216,16 @@ fn parse_players_and_teams(packets: Vec>) -> GDResult<(Vec, Vec< let mut teams_data: Vec> = vec![HashMap::new()]; for packet in packets { - let mut buf = Bufferer::new_with_data(Endianess::Little, &packet); + let mut buf = Buffer::::new(&packet); - while !buf.is_remaining_empty() { - if buf.get_u8()? < 3 { + while buf.remaining_length() != 0 { + if buf.read::()? < 3 { continue; } - buf.move_position_backward(1); + buf.move_cursor(1)?; - let field = buf.get_string_utf8()?; + let field = buf.read_string::(None)?; if field.is_empty() { continue; } @@ -248,15 +252,15 @@ fn parse_players_and_teams(packets: Vec>) -> GDResult<(Vec, Vec< } }; - let mut offset = buf.get_u8()? as usize; + let mut offset = buf.read::()? as usize; let data = match field_type.is_none() { true => &mut players_data, false => &mut teams_data, }; - while !buf.is_remaining_empty() { - let item = buf.get_string_utf8()?; + while buf.remaining_length() != 0 { + let item = buf.read_string::(None)?; if item.is_empty() { break; } diff --git a/src/protocols/gamespy/protocols/two/protocol.rs b/src/protocols/gamespy/protocols/two/protocol.rs index 2dd32bc..b3cc0b1 100644 --- a/src/protocols/gamespy/protocols/two/protocol.rs +++ b/src/protocols/gamespy/protocols/two/protocol.rs @@ -1,8 +1,9 @@ -use crate::bufferer::{Bufferer, Endianess}; +use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::gamespy::two::{Player, Response, Team}; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; use crate::{GDError, GDResult}; +use byteorder::BigEndian; use std::collections::HashMap; use std::net::SocketAddr; @@ -28,12 +29,12 @@ macro_rules! table_extract_parse { }; } -fn data_as_table(data: &mut Bufferer) -> GDResult<(HashMap>, usize)> { - if data.get_u8()? != 0 { +fn data_as_table(data: &mut Buffer) -> GDResult<(HashMap>, usize)> { + if data.read::()? != 0 { Err(GDError::PacketBad)? } - let rows = data.get_u8()? as usize; + let rows = data.read::()? as usize; if rows == 0 { return Ok((HashMap::new(), 0)); @@ -41,21 +42,28 @@ fn data_as_table(data: &mut Bufferer) -> GDResult<(HashMap>, let mut column_heads = Vec::new(); - let mut current_column = data.get_string_utf8()?; + let mut current_column = data.read_string::(None)?; while !current_column.is_empty() { column_heads.push(current_column); - current_column = data.get_string_utf8()?; + current_column = data.read_string::(None)? } let columns = column_heads.len(); let mut table = HashMap::with_capacity(columns); for head in &column_heads { - table.insert(head.clone(), Vec::new()); // TODO: This doesn't look good nor it is performant, fix later + // TODO: This doesn't look good nor it is performant, fix later + // By using &column_heads in the for loop instead of cloning column_heads, you + // avoid creating an unnecessary copy. However, column_heads is a + // Vec and head is a &String (a reference to a string). Hence, to use + // head as a key to the HashMap, we still need to call clone(). This is because + // HashMap takes ownership of its keys and we cannot give it a reference to a + // local variable (head) that will be dropped at the end of the function. + table.insert(head.clone(), Vec::new()); } for _ in 0 .. rows { for column in column_heads.iter() { - let value = data.get_string_utf8()?; + let value = data.read_string::(None)?; table.get_mut(column).ok_or(GDError::PacketBad)?.push(value); } } @@ -71,36 +79,33 @@ impl GameSpy2 { Ok(Self { socket }) } - fn request_data(&mut self) -> GDResult { + fn request_data(&mut self) -> GDResult<(Vec, usize)> { self.socket .send(&[0xFE, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF])?; let received = self.socket.receive(None)?; - let mut buf = Bufferer::new_with_data(Endianess::Big, &received); - if buf.get_u8()? != 0 { + let mut buf = Buffer::::new(&received); + if buf.read::()? != 0 || buf.read::()? != 1 { return Err(GDError::PacketBad); } - if buf.get_u32()? != 1 { - return Err(GDError::PacketBad); - } - - Ok(buf) + let buf_index = buf.current_position(); + Ok((received, buf_index)) } } -fn get_server_vars(bufferer: &mut Bufferer) -> GDResult> { +fn get_server_vars(bufferer: &mut Buffer) -> GDResult> { let mut values = HashMap::new(); let mut done_processing_vars = false; - while !done_processing_vars && !bufferer.is_remaining_empty() { - let key = bufferer.get_string_utf8()?; - let value = bufferer.get_string_utf8_optional()?; + while !done_processing_vars && bufferer.remaining_length() != 0 { + let key = bufferer.read_string::(None)?; + let value = bufferer.read_string::(None)?; if key.is_empty() { if value.is_empty() { - bufferer.move_position_backward(1); + bufferer.move_cursor(-1)?; done_processing_vars = true; } @@ -113,7 +118,7 @@ fn get_server_vars(bufferer: &mut Bufferer) -> GDResult> Ok(values) } -fn get_teams(bufferer: &mut Bufferer) -> GDResult> { +fn get_teams(bufferer: &mut Buffer) -> GDResult> { let mut teams = Vec::new(); let (table, entries) = data_as_table(bufferer)?; @@ -128,7 +133,7 @@ fn get_teams(bufferer: &mut Bufferer) -> GDResult> { Ok(teams) } -fn get_players(bufferer: &mut Bufferer) -> GDResult> { +fn get_players(bufferer: &mut Buffer) -> GDResult> { let mut players = Vec::new(); let (table, entries) = data_as_table(bufferer)?; @@ -147,10 +152,13 @@ fn get_players(bufferer: &mut Bufferer) -> GDResult> { pub fn query(address: &SocketAddr, timeout_settings: Option) -> GDResult { let mut client = GameSpy2::new(address, timeout_settings)?; - let mut data = client.request_data()?; + let (data, buf_index) = client.request_data()?; - let mut server_vars = get_server_vars(&mut data)?; - let players = get_players(&mut data)?; + let mut buffer = Buffer::::new(&data); + buffer.move_cursor(buf_index as isize)?; + + let mut server_vars = get_server_vars(&mut buffer)?; + let players = get_players(&mut buffer)?; let players_online = match server_vars.remove("numplayers") { None => players.len(), @@ -171,7 +179,7 @@ pub fn query(address: &SocketAddr, timeout_settings: Option) -> name: server_vars.remove("hostname").ok_or(GDError::PacketBad)?, map: server_vars.remove("mapname").ok_or(GDError::PacketBad)?, has_password: server_vars.remove("password").ok_or(GDError::PacketBad)? == "1", - teams: get_teams(&mut data)?, + teams: get_teams(&mut buffer)?, players_maximum: server_vars .remove("maxplayers") .ok_or(GDError::PacketBad)? diff --git a/src/protocols/minecraft/protocol/bedrock.rs b/src/protocols/minecraft/protocol/bedrock.rs index 6e83e7c..46b3062 100644 --- a/src/protocols/minecraft/protocol/bedrock.rs +++ b/src/protocols/minecraft/protocol/bedrock.rs @@ -1,8 +1,7 @@ // This file has code that has been documented by the NodeJS GameDig library // (MIT) from https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js - use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::{Buffer, Utf8Decoder}, protocols::{ minecraft::{BedrockResponse, GameMode, Server}, types::TimeoutSettings, @@ -12,8 +11,11 @@ use crate::{ GDError::{PacketBad, TypeParse}, GDResult, }; + use std::net::SocketAddr; +use byteorder::LittleEndian; + pub struct Bedrock { socket: UdpSocket, } @@ -40,35 +42,36 @@ impl Bedrock { fn get_info(&mut self) -> GDResult { self.send_status_request()?; - let mut buffer = Bufferer::new_with_data(Endianess::Little, &self.socket.receive(None)?); + let received = self.socket.receive(None)?; + let mut buffer = Buffer::::new(&received); - if buffer.get_u8()? != 0x1c { + if buffer.read::()? != 0x1c { return Err(PacketBad); } // Checking for our nonce directly from a u64 (as the nonce is 8 bytes). - if buffer.get_u64()? != 9833440827789222417 { + if buffer.read::()? != 9833440827789222417 { return Err(PacketBad); } // These 8 bytes are identical to the serverId string we receive in decimal // below - buffer.move_position_ahead(8); + buffer.move_cursor(8)?; // Verifying the magic value (as we need 16 bytes, cast to two u64 values) - if buffer.get_u64()? != 18374403896610127616 { + if buffer.read::()? != 18374403896610127616 { return Err(PacketBad); } - if buffer.get_u64()? != 8671175388723805693 { + if buffer.read::()? != 8671175388723805693 { return Err(PacketBad); } - let remaining_length = buffer.as_endianess(Endianess::Big).get_u16()? as usize; - buffer.move_position_ahead(2); + let remaining_length = buffer.switch_endian_chunk(2)?.read::()? as usize; + error_by_expected_size(remaining_length, buffer.remaining_length())?; - let binding = buffer.get_string_utf8_unended()?; + let binding = buffer.read_string::(None)?; let status: Vec<&str> = binding.split(';').collect(); // We must have at least 6 values diff --git a/src/protocols/minecraft/protocol/java.rs b/src/protocols/minecraft/protocol/java.rs index 19da757..d014a0a 100644 --- a/src/protocols/minecraft/protocol/java.rs +++ b/src/protocols/minecraft/protocol/java.rs @@ -1,5 +1,5 @@ use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::Buffer, protocols::{ minecraft::{as_varint, get_string, get_varint, JavaResponse, Player, Server}, types::TimeoutSettings, @@ -8,8 +8,10 @@ use crate::{ GDError::{JsonParse, PacketBad}, GDResult, }; + use std::net::SocketAddr; +use byteorder::LittleEndian; use serde_json::Value; #[rustfmt::skip] @@ -43,14 +45,15 @@ impl Java { .send(&[as_varint(data.len() as i32), data].concat()) } - fn receive(&mut self) -> GDResult { - let mut buffer = Bufferer::new_with_data(Endianess::Little, &self.socket.receive(None)?); + fn receive(&mut self) -> GDResult> { + let data = &self.socket.receive(None)?; + let mut buffer = Buffer::::new(data); let _packet_length = get_varint(&mut buffer)? as usize; // this declared 'packet length' from within the packet might be wrong (?), not // checking with it... - Ok(buffer) + Ok(buffer.remaining_bytes().to_vec()) } fn send_handshake(&mut self) -> GDResult<()> { @@ -82,7 +85,8 @@ impl Java { self.send_status_request()?; self.send_ping_request()?; - let mut buffer = self.receive()?; + let socket_data = self.receive()?; + let mut buffer = Buffer::::new(&socket_data); if get_varint(&mut buffer)? != 0 { // first var int is the packet id diff --git a/src/protocols/minecraft/protocol/legacy_bv1_8.rs b/src/protocols/minecraft/protocol/legacy_bv1_8.rs index ea1767c..b67618b 100644 --- a/src/protocols/minecraft/protocol/legacy_bv1_8.rs +++ b/src/protocols/minecraft/protocol/legacy_bv1_8.rs @@ -1,5 +1,5 @@ use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::{Buffer, Utf16Decoder}, protocols::{ minecraft::{JavaResponse, LegacyGroup, Server}, types::TimeoutSettings, @@ -9,8 +9,11 @@ use crate::{ GDError::{PacketBad, ProtocolFormat}, GDResult, }; + use std::net::SocketAddr; +use byteorder::BigEndian; + pub struct LegacyBV1_8 { socket: TcpSocket, } @@ -29,16 +32,16 @@ impl LegacyBV1_8 { self.send_initial_request()?; let data = self.socket.receive(None)?; - let mut buffer = Bufferer::new_with_data(Endianess::Big, &data); + let mut buffer = Buffer::::new(&data); - if buffer.get_u8()? != 0xFF { + if buffer.read::()? != 0xFF { return Err(ProtocolFormat); } - let length = buffer.get_u16()? * 2; + let length = buffer.read::()? * 2; error_by_expected_size((length + 3) as usize, data.len())?; - let packet_string = buffer.get_string_utf16()?; + let packet_string = buffer.read_string::>(None)?; let split: Vec<&str> = packet_string.split('§').collect(); error_by_expected_size(3, split.len())?; diff --git a/src/protocols/minecraft/protocol/legacy_v1_4.rs b/src/protocols/minecraft/protocol/legacy_v1_4.rs index 5c4244c..e67b60b 100644 --- a/src/protocols/minecraft/protocol/legacy_v1_4.rs +++ b/src/protocols/minecraft/protocol/legacy_v1_4.rs @@ -1,5 +1,7 @@ +use byteorder::BigEndian; + use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::{Buffer, Utf16Decoder}, protocols::{ minecraft::{protocol::legacy_v1_6::LegacyV1_6, JavaResponse, LegacyGroup, Server}, types::TimeoutSettings, @@ -29,20 +31,20 @@ impl LegacyV1_4 { self.send_initial_request()?; let data = self.socket.receive(None)?; - let mut buffer = Bufferer::new_with_data(Endianess::Big, &data); + let mut buffer = Buffer::::new(&data); - if buffer.get_u8()? != 0xFF { + if buffer.read::()? != 0xFF { return Err(ProtocolFormat); } - let length = buffer.get_u16()? * 2; + let length = buffer.read::()? * 2; error_by_expected_size((length + 3) as usize, data.len())?; if LegacyV1_6::is_protocol(&mut buffer)? { return LegacyV1_6::get_response(&mut buffer); } - let packet_string = buffer.get_string_utf16()?; + let packet_string = buffer.read_string::>(None)?; let split: Vec<&str> = packet_string.split('§').collect(); error_by_expected_size(3, split.len())?; diff --git a/src/protocols/minecraft/protocol/legacy_v1_6.rs b/src/protocols/minecraft/protocol/legacy_v1_6.rs index 3e0e618..013825d 100644 --- a/src/protocols/minecraft/protocol/legacy_v1_6.rs +++ b/src/protocols/minecraft/protocol/legacy_v1_6.rs @@ -1,5 +1,7 @@ +use byteorder::BigEndian; + use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::{Buffer, Utf16Decoder}, protocols::{ minecraft::{JavaResponse, LegacyGroup, Server}, types::TimeoutSettings, @@ -36,29 +38,34 @@ impl LegacyV1_6 { Ok(()) } - pub fn is_protocol(buffer: &mut Bufferer) -> GDResult { + pub(crate) fn is_protocol(buffer: &mut Buffer) -> GDResult { let state = buffer - .remaining_data() + .remaining_bytes() .starts_with(&[0x00, 0xA7, 0x00, 0x31, 0x00, 0x00]); if state { - buffer.move_position_ahead(6); + buffer.move_cursor(6)?; } Ok(state) } - pub fn get_response(buffer: &mut Bufferer) -> GDResult { - let packet_string = buffer.get_string_utf16()?; - - let split: Vec<&str> = packet_string.split('\x00').collect(); - error_by_expected_size(5, split.len())?; - - let version_protocol = split[0].parse().map_err(|_| PacketBad)?; - let version_name = split[1].to_string(); - let description = split[2].to_string(); - let online_players = split[3].parse().map_err(|_| PacketBad)?; - let max_players = split[4].parse().map_err(|_| PacketBad)?; + pub(crate) fn get_response(buffer: &mut Buffer) -> GDResult { + // This is a specific order! + let version_protocol = buffer + .read_string::>(None)? + .parse() + .map_err(|_| PacketBad)?; + let version_name = buffer.read_string::>(None)?; + let description = buffer.read_string::>(None)?; + let online_players = buffer + .read_string::>(None)? + .parse() + .map_err(|_| PacketBad)?; + let max_players = buffer + .read_string::>(None)? + .parse() + .map_err(|_| PacketBad)?; Ok(JavaResponse { version_name, @@ -78,13 +85,13 @@ impl LegacyV1_6 { self.send_initial_request()?; let data = self.socket.receive(None)?; - let mut buffer = Bufferer::new_with_data(Endianess::Big, &data); + let mut buffer = Buffer::::new(&data); - if buffer.get_u8()? != 0xFF { + if buffer.read::()? != 0xFF { return Err(ProtocolFormat); } - let length = buffer.get_u16()? * 2; + let length = buffer.read::()? * 2; error_by_expected_size((length + 3) as usize, data.len())?; if !LegacyV1_6::is_protocol(&mut buffer)? { diff --git a/src/protocols/minecraft/types.rs b/src/protocols/minecraft/types.rs index b8ccaff..7523a7f 100644 --- a/src/protocols/minecraft/types.rs +++ b/src/protocols/minecraft/types.rs @@ -3,7 +3,7 @@ // https://github.com/thisjaiden/golden_apple/blob/master/src/lib.rs use crate::{ - bufferer::Bufferer, + buffer::Buffer, protocols::{ types::{CommonPlayer, CommonResponse, GenericPlayer}, GenericResponse, @@ -12,6 +12,7 @@ use crate::{ GDResult, }; +use byteorder::ByteOrder; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -179,14 +180,14 @@ impl GameMode { } } -pub(crate) fn get_varint(buffer: &mut Bufferer) -> GDResult { +pub(crate) fn get_varint(buffer: &mut Buffer) -> GDResult { let mut result = 0; let msb: u8 = 0b10000000; let mask: u8 = !msb; for i in 0 .. 5 { - let current_byte = buffer.get_u8()?; + let current_byte = buffer.read::()?; result |= ((current_byte & mask) as i32) << (7 * i); @@ -227,12 +228,12 @@ pub(crate) fn as_varint(value: i32) -> Vec { bytes } -pub(crate) fn get_string(buffer: &mut Bufferer) -> GDResult { +pub(crate) fn get_string(buffer: &mut Buffer) -> GDResult { let length = get_varint(buffer)? as usize; let mut text = Vec::with_capacity(length); for _ in 0 .. length { - text.push(buffer.get_u8()?) + text.push(buffer.read::()?) } String::from_utf8(text).map_err(|_| PacketBad) diff --git a/src/protocols/quake/client.rs b/src/protocols/quake/client.rs index 180215c..a2beb4a 100644 --- a/src/protocols/quake/client.rs +++ b/src/protocols/quake/client.rs @@ -1,4 +1,6 @@ -use crate::bufferer::{Bufferer, Endianess}; +use byteorder::LittleEndian; + +use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::quake::types::Response; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; @@ -15,10 +17,7 @@ pub(crate) trait QuakeClient { fn parse_player_string(data: Iter<&str>) -> GDResult; } -fn get_data( - address: &SocketAddr, - timeout_settings: Option, -) -> GDResult { +fn get_data(address: &SocketAddr, timeout_settings: Option) -> GDResult> { let mut socket = UdpSocket::new(address)?; socket.apply_timeout(timeout_settings)?; @@ -32,24 +31,24 @@ fn get_data( )?; let data = socket.receive(None)?; - let mut bufferer = Bufferer::new_with_data(Endianess::Little, &data); + let mut bufferer = Buffer::::new(&data); - if bufferer.get_u32()? != 4294967295 { + if bufferer.read::()? != 4294967295 { return Err(GDError::PacketBad); } let response_header = Client::get_response_header().as_bytes(); - if !bufferer.remaining_data().starts_with(response_header) { + if !bufferer.remaining_bytes().starts_with(response_header) { Err(GDError::PacketBad)? } - bufferer.move_position_ahead(response_header.len()); + bufferer.move_cursor(response_header.len() as isize)?; - Ok(bufferer) + Ok(bufferer.remaining_bytes().to_vec()) //TODO: Maybe fix? } -fn get_server_values(bufferer: &mut Bufferer) -> GDResult> { - let data = bufferer.get_string_utf8_newline()?; +fn get_server_values(bufferer: &mut Buffer) -> GDResult> { + let data = bufferer.read_string::(Some([0x0A]))?; let mut data_split = data.split('\\').collect::>(); if let Some(first) = data_split.first() { if first == &"" { @@ -74,11 +73,14 @@ fn get_server_values(bufferer: &mut Bufferer) -> GDResult(bufferer: &mut Bufferer) -> GDResult> { +fn get_players(bufferer: &mut Buffer) -> GDResult> { let mut players: Vec = Vec::new(); - while !bufferer.is_remaining_empty() && bufferer.remaining_data() != [0x00] { - let data = bufferer.get_string_utf8_newline()?; + // this needs to be looked at again as theres no way to check if the buffer has + // a remaining null byte the original code was: + // while !bufferer.is_remaining_empty() && bufferer.remaining_data() != [0x00] + while !bufferer.remaining_length() == 0 { + let data = bufferer.read_string::(Some([0x0A]))?; let data_split = data.split(' ').collect::>(); let data_iter = data_split.iter(); @@ -92,7 +94,8 @@ pub(crate) fn client_query( address: &SocketAddr, timeout_settings: Option, ) -> GDResult> { - let mut bufferer = get_data::(address, timeout_settings)?; + let data = get_data::(address, timeout_settings)?; + let mut bufferer = Buffer::::new(&data); let mut server_vars = get_server_values(&mut bufferer)?; let players = get_players::(&mut bufferer)?; diff --git a/src/protocols/quake/two.rs b/src/protocols/quake/two.rs index 256a5ce..a45bb45 100644 --- a/src/protocols/quake/two.rs +++ b/src/protocols/quake/two.rs @@ -56,10 +56,7 @@ impl QuakeClient for QuakeTwo { None => Err(GDError::PacketBad)?, Some(v) => remove_wrapping_quotes(v).to_string(), }, - address: match data.next() { - None => None, - Some(v) => Some(remove_wrapping_quotes(v).to_string()), - }, + address: data.next().map(|v| remove_wrapping_quotes(v).to_string()), }) } } diff --git a/src/protocols/valve/protocol.rs b/src/protocols/valve/protocol.rs index a673bfb..ba17854 100644 --- a/src/protocols/valve/protocol.rs +++ b/src/protocols/valve/protocol.rs @@ -1,5 +1,5 @@ use crate::{ - bufferer::{Bufferer, Endianess}, + buffer::Buffer, protocols::{ types::TimeoutSettings, valve::{ @@ -27,7 +27,9 @@ use crate::{ use bzip2_rs::decoder::Decoder; +use crate::buffer::Utf8Decoder; use crate::protocols::valve::Packet; +use byteorder::LittleEndian; use std::collections::HashMap; use std::net::SocketAddr; @@ -46,26 +48,27 @@ struct SplitPacket { } impl SplitPacket { - fn new(engine: &Engine, protocol: u8, buffer: &mut Bufferer) -> GDResult { - let header = buffer.get_u32()?; - let id = buffer.get_u32()?; + fn new(engine: &Engine, protocol: u8, buffer: &mut Buffer) -> GDResult { + let header = buffer.read()?; //buffer.get_u32()?; + let id = buffer.read()?; let (total, number, size, compressed, decompressed_size, uncompressed_crc32) = match engine { Engine::GoldSrc(_) => { - let (lower, upper) = u8_lower_upper(buffer.get_u8()?); + let (lower, upper) = u8_lower_upper(buffer.read()?); (lower, upper, 0, false, None, None) } Engine::Source(_) => { - let total = buffer.get_u8()?; - let number = buffer.get_u8()?; + let total = buffer.read()?; + let number = buffer.read()?; let size = match protocol == 7 && (*engine == SteamApp::CSS.as_engine()) { // certain apps with protocol = 7 dont have this field - false => buffer.get_u16()?, + false => buffer.read()?, true => 1248, }; - let compressed = ((id >> 31) & 1) == 1; + let compressed = ((id >> 31) & 1u32) == 1u32; + let (decompressed_size, uncompressed_crc32) = match compressed { false => (None, None), - true => (Some(buffer.get_u32()?), Some(buffer.get_u32()?)), + true => (Some(buffer.read()?), Some(buffer.read()?)), }; ( total, @@ -87,7 +90,7 @@ impl SplitPacket { compressed, decompressed_size, uncompressed_crc32, - payload: buffer.remaining_data().to_vec(), + payload: buffer.remaining_bytes().to_vec(), }) } @@ -133,10 +136,10 @@ impl ValveProtocol { fn receive(&mut self, engine: &Engine, protocol: u8, buffer_size: usize) -> GDResult { let data = self.socket.receive(Some(buffer_size))?; - let mut buffer = Bufferer::new_with_data(Endianess::Little, &data); + let mut buffer = Buffer::::new(&data); - let header = buffer.get_u8()?; - buffer.move_position_backward(1); + let header: u8 = buffer.read()?; + buffer.move_cursor(-1)?; if header == 0xFE { // the packet is split let mut main_packet = SplitPacket::new(engine, protocol, &mut buffer)?; @@ -144,7 +147,7 @@ impl ValveProtocol { for _ in 1 .. main_packet.total { let new_data = self.socket.receive(Some(buffer_size))?; - buffer = Bufferer::new_with_data(Endianess::Little, &new_data); + buffer = Buffer::::new(&new_data); let chunk_packet = SplitPacket::new(engine, protocol, &mut buffer)?; chunk_packets.push(chunk_packet); } @@ -155,25 +158,21 @@ impl ValveProtocol { main_packet.payload.extend(chunk_packet.payload); } - let mut new_packet_buffer = Bufferer::new_with_data(Endianess::Little, &main_packet.get_payload()?); + let payload = main_packet.get_payload()?; // Creating a non-temporary value here + let mut new_packet_buffer = Buffer::::new(&payload); // Using the non-temporary value here Ok(Packet::new_from_bufferer(&mut new_packet_buffer)?) } else { Packet::new_from_bufferer(&mut buffer) } } - pub fn get_kind_request_data(&mut self, engine: &Engine, protocol: u8, kind: Request) -> GDResult { - self.get_request_data(engine, protocol, kind as u8, kind.get_default_payload()) + pub fn get_kind_request_data(&mut self, engine: &Engine, protocol: u8, kind: Request) -> GDResult> { + let data = self.get_request_data(engine, protocol, kind as u8, kind.get_default_payload())?; + Ok(data) } /// Ask for a specific request only. - pub fn get_request_data( - &mut self, - engine: &Engine, - protocol: u8, - kind: u8, - payload: Vec, - ) -> GDResult { + pub fn get_request_data(&mut self, engine: &Engine, protocol: u8, kind: u8, payload: Vec) -> GDResult> { let request_initial_packet = Packet::new(kind, payload).to_bytes(); self.socket.send(&request_initial_packet)?; @@ -182,7 +181,7 @@ impl ValveProtocol { // 'A' let challenge = packet.payload; - const INFO: u8 = Request::Info as u8; // hmm, this could be unwanted and problematic + const INFO: u8 = Request::Info as u8; let challenge_packet = Packet::new( kind, match kind { @@ -197,48 +196,47 @@ impl ValveProtocol { packet = self.receive(engine, protocol, PACKET_SIZE)?; } - let data = packet.payload; - Ok(Bufferer::new_with_data(Endianess::Little, &data)) + Ok(packet.payload) } - fn get_goldsrc_server_info(buffer: &mut Bufferer) -> GDResult { - buffer.get_u8()?; //get the header (useless info) - buffer.get_string_utf8()?; //get the server address (useless info) - let name = buffer.get_string_utf8()?; - let map = buffer.get_string_utf8()?; - let folder = buffer.get_string_utf8()?; - let game = buffer.get_string_utf8()?; - let players = buffer.get_u8()?; - let max_players = buffer.get_u8()?; - let protocol = buffer.get_u8()?; - let server_type = match buffer.get_u8()? { + fn get_goldsrc_server_info(buffer: &mut Buffer) -> GDResult { + let _header: u8 = buffer.read()?; //get the header (useless info) + let _address: String = buffer.read_string::(None)?; //get the server address (useless info) + let name = buffer.read_string::(None)?; + let map = buffer.read_string::(None)?; + let folder = buffer.read_string::(None)?; + let game = buffer.read_string::(None)?; + let players = buffer.read()?; + let max_players = buffer.read()?; + let protocol = buffer.read()?; + let server_type = match buffer.read::()? { 68 => Server::Dedicated, //'D' 76 => Server::NonDedicated, //'L' 80 => Server::TV, //'P' _ => Err(UnknownEnumCast)?, }; - let environment_type = match buffer.get_u8()? { + let environment_type = match buffer.read::()? { 76 => Environment::Linux, //'L' 87 => Environment::Windows, //'W' _ => Err(UnknownEnumCast)?, }; - let has_password = buffer.get_u8()? == 1; - let is_mod = buffer.get_u8()? == 1; + let has_password = buffer.read::()? == 1; + let is_mod = buffer.read::()? == 1; let mod_data = match is_mod { false => None, true => { Some(ModData { - link: buffer.get_string_utf8()?, - download_link: buffer.get_string_utf8()?, - version: buffer.get_u32()?, - size: buffer.get_u32()?, - multiplayer_only: buffer.get_u8()? == 1, - has_own_dll: buffer.get_u8()? == 1, + link: buffer.read_string::(None)?, + download_link: buffer.read_string::(None)?, + version: buffer.read()?, + size: buffer.read()?, + multiplayer_only: buffer.read::()? == 1, + has_own_dll: buffer.read::()? == 1, }) } }; - let vac_secured = buffer.get_u8()? == 1; - let bots = buffer.get_u8()?; + let vac_secured = buffer.read::()? == 1; + let bots = buffer.read::()?; Ok(ServerInfo { protocol, @@ -264,7 +262,8 @@ impl ValveProtocol { /// Get the server information's. fn get_server_info(&mut self, engine: &Engine) -> GDResult { - let mut buffer = self.get_kind_request_data(engine, 0, Request::Info)?; + let data = self.get_kind_request_data(engine, 0, Request::Info)?; + let mut buffer = Buffer::::new(&data); if let Engine::GoldSrc(force) = engine { if *force { @@ -272,58 +271,58 @@ impl ValveProtocol { } } - let protocol = buffer.get_u8()?; - let name = buffer.get_string_utf8()?; - let map = buffer.get_string_utf8()?; - let folder = buffer.get_string_utf8()?; - let game = buffer.get_string_utf8()?; - let mut appid = buffer.get_u16()? as u32; - let players = buffer.get_u8()?; - let max_players = buffer.get_u8()?; - let bots = buffer.get_u8()?; - let server_type = Server::from_gldsrc(buffer.get_u8()?)?; - let environment_type = Environment::from_gldsrc(buffer.get_u8()?)?; - let has_password = buffer.get_u8()? == 1; - let vac_secured = buffer.get_u8()? == 1; + let protocol = buffer.read()?; + let name = buffer.read_string::(None)?; + let map = buffer.read_string::(None)?; + let folder = buffer.read_string::(None)?; + let game = buffer.read_string::(None)?; + let mut appid = buffer.read::()? as u32; + let players = buffer.read()?; + let max_players = buffer.read()?; + let bots = buffer.read()?; + let server_type = Server::from_gldsrc(buffer.read()?)?; + let environment_type = Environment::from_gldsrc(buffer.read()?)?; + let has_password = buffer.read::()? == 1; + let vac_secured = buffer.read::()? == 1; let the_ship = match *engine == SteamApp::TS.as_engine() { false => None, true => { Some(TheShip { - mode: buffer.get_u8()?, - witnesses: buffer.get_u8()?, - duration: buffer.get_u8()?, + mode: buffer.read()?, + witnesses: buffer.read()?, + duration: buffer.read()?, }) } }; - let version = buffer.get_string_utf8()?; - let extra_data = match buffer.get_u8() { + let version = buffer.read_string::(None)?; + let extra_data = match buffer.read::() { Err(_) => None, Ok(value) => { Some(ExtraData { port: match (value & 0x80) > 0 { false => None, - true => Some(buffer.get_u16()?), + true => Some(buffer.read()?), }, steam_id: match (value & 0x10) > 0 { false => None, - true => Some(buffer.get_u64()?), + true => Some(buffer.read()?), }, tv_port: match (value & 0x40) > 0 { false => None, - true => Some(buffer.get_u16()?), + true => Some(buffer.read()?), }, tv_name: match (value & 0x40) > 0 { false => None, - true => Some(buffer.get_string_utf8()?), + true => Some(buffer.read_string::(None)?), }, keywords: match (value & 0x20) > 0 { false => None, - true => Some(buffer.get_string_utf8()?), + true => Some(buffer.read_string::(None)?), }, game_id: match (value & 0x01) > 0 { false => None, true => { - let gid = buffer.get_u64()?; + let gid = buffer.read()?; appid = (gid & ((1 << 24) - 1)) as u32; Some(gid) @@ -357,25 +356,26 @@ impl ValveProtocol { /// Get the server player's. fn get_server_players(&mut self, engine: &Engine, protocol: u8) -> GDResult> { - let mut buffer = self.get_kind_request_data(engine, protocol, Request::Players)?; + let data = self.get_kind_request_data(engine, protocol, Request::Players)?; + let mut buffer = Buffer::::new(&data); - let count = buffer.get_u8()? as usize; + let count = buffer.read::()? as usize; let mut players: Vec = Vec::with_capacity(count); for _ in 0 .. count { - buffer.move_position_ahead(1); //skip the index byte + buffer.move_cursor(1)?; //skip the index byte players.push(ServerPlayer { - name: buffer.get_string_utf8()?, - score: buffer.get_u32()?, - duration: buffer.get_f32()?, + name: buffer.read_string::(None)?, + score: buffer.read()?, + duration: buffer.read()?, deaths: match *engine == SteamApp::TS.as_engine() { false => None, - true => Some(buffer.get_u32()?), + true => Some(buffer.read()?), }, money: match *engine == SteamApp::TS.as_engine() { false => None, - true => Some(buffer.get_u32()?), + true => Some(buffer.read()?), }, }); } @@ -385,14 +385,15 @@ impl ValveProtocol { /// Get the server's rules. fn get_server_rules(&mut self, engine: &Engine, protocol: u8) -> GDResult> { - let mut buffer = self.get_kind_request_data(engine, protocol, Request::Rules)?; + let data = self.get_kind_request_data(engine, protocol, Request::Rules)?; + let mut buffer = Buffer::::new(&data); - let count = buffer.get_u16()? as usize; + let count = buffer.read::()? as usize; let mut rules: HashMap = HashMap::with_capacity(count); for _ in 0 .. count { - let name = buffer.get_string_utf8()?; - let value = buffer.get_string_utf8()?; + let name = buffer.read_string::(None)?; + let value = buffer.read_string::(None)?; rules.insert(name, value); } diff --git a/src/protocols/valve/types.rs b/src/protocols/valve/types.rs index 608480b..ab31665 100644 --- a/src/protocols/valve/types.rs +++ b/src/protocols/valve/types.rs @@ -3,7 +3,8 @@ use std::collections::HashMap; use crate::protocols::types::{CommonPlayer, CommonResponse, GenericPlayer}; use crate::GDError::UnknownEnumCast; use crate::GDResult; -use crate::{bufferer::Bufferer, protocols::GenericResponse}; +use crate::{buffer::Buffer, protocols::GenericResponse}; +use byteorder::LittleEndian; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -209,11 +210,11 @@ impl Packet { } } - pub fn new_from_bufferer(buffer: &mut Bufferer) -> GDResult { + pub fn new_from_bufferer(buffer: &mut Buffer) -> GDResult { Ok(Self { - header: buffer.get_u32()?, - kind: buffer.get_u8()?, - payload: buffer.remaining_data().to_vec(), + header: buffer.read::()?, + kind: buffer.read::()?, + payload: buffer.remaining_bytes().to_vec(), }) } diff --git a/src/services/valve_master_server/service.rs b/src/services/valve_master_server/service.rs index 7beae2a..6ad6a36 100644 --- a/src/services/valve_master_server/service.rs +++ b/src/services/valve_master_server/service.rs @@ -1,9 +1,15 @@ -use crate::bufferer::{Bufferer, Endianess}; -use crate::socket::{Socket, UdpSocket}; -use crate::valve_master_server::{Region, SearchFilters}; -use crate::{GDError, GDResult}; +use crate::{ + buffer::Buffer, + socket::{Socket, UdpSocket}, + valve_master_server::{Region, SearchFilters}, + GDError, + GDResult, +}; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use byteorder::BigEndian; + /// The default master ip, which is the one for Source. pub fn default_master_address() -> SocketAddr { SocketAddr::new(IpAddr::V4(Ipv4Addr::new(208, 64, 201, 194)), 27011) // hl2master.steampowered.com @@ -63,21 +69,22 @@ impl ValveMasterServer { self.socket.send(&payload)?; let received_data = self.socket.receive(Some(1400))?; - let mut buf = Bufferer::new_with_data(Endianess::Big, &received_data); + let mut buf = Buffer::::new(&received_data); - if buf.get_u32()? != 4294967295 || buf.get_u16()? != 26122 { + if buf.read::()? != 4294967295 || buf.read::()? != 26122 { return Err(GDError::PacketBad); } let mut ips: Vec<(IpAddr, u16)> = Vec::new(); - while !buf.is_remaining_empty() { + + while !buf.remaining_length() == 0 { let ip = IpAddr::V4(Ipv4Addr::new( - buf.get_u8()?, - buf.get_u8()?, - buf.get_u8()?, - buf.get_u8()?, + buf.read::()?, + buf.read::()?, + buf.read::()?, + buf.read::()?, )); - let port = buf.get_u16()?; + let port = buf.read::()?; ips.push((ip, port)); }