From b09fa4ada5cf044f54d55ae8a7d70e7e948953a6 Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Thu, 29 Dec 2022 16:30:24 +0200 Subject: [PATCH] Change buffer reading implementation (#8) * Add new implementation an valve protocol refactor * Refactor minecraft protocol with new bufferer --- src/bufferer.rs | 304 ++++++++++++++++++ src/lib.rs | 1 + src/protocols/minecraft/protocol/bedrock.rs | 22 +- src/protocols/minecraft/protocol/java.rs | 17 +- .../minecraft/protocol/legacy_bv1_8.rs | 13 +- .../minecraft/protocol/legacy_v1_4.rs | 17 +- .../minecraft/protocol/legacy_v1_6.rs | 25 +- src/protocols/minecraft/types.rs | 12 +- src/protocols/valve/protocol.rs | 181 +++++------ src/utils.rs | 216 +------------ 10 files changed, 455 insertions(+), 353 deletions(-) create mode 100644 src/bufferer.rs diff --git a/src/bufferer.rs b/src/bufferer.rs new file mode 100644 index 0000000..20feb0d --- /dev/null +++ b/src/bufferer.rs @@ -0,0 +1,304 @@ +use crate::{GDError, GDResult}; + +pub enum Endianess { + Little, Big +} + +pub struct Bufferer { + data: Vec, + endianess: Endianess, + position: usize +} + +impl Bufferer { + pub fn new(endianess: Endianess) -> Self { + Bufferer::new_with_data(endianess, &[]) + } + + 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(GDError::PacketUnderflow("Unexpectedly short packet for getting an u8.".to_string())); + } + + let value = self.data[self.position]; + self.position += 1; + Ok(value) + } + + pub fn get_u16(&mut self) -> GDResult { + if self.check_size(2) { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u16.".to_string())); + } + + let source_data: [u8; 2] = (&self.data[self.position..self.position + 2]).try_into().unwrap(); + + let value = match self.endianess { + Endianess::Little => u16::from_le_bytes(source_data), + Endianess::Big => u16::from_be_bytes(source_data) + }; + + self.position += 2; + Ok(value) + } + + pub fn get_u32(&mut self) -> GDResult { + if self.check_size(4) { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u32.".to_string())); + } + + let source_data: [u8; 4] = (&self.data[self.position..self.position + 4]).try_into().unwrap(); + + let value = match self.endianess { + Endianess::Little => u32::from_le_bytes(source_data), + Endianess::Big => u32::from_be_bytes(source_data) + }; + + self.position += 4; + Ok(value) + } + + pub fn get_f32(&mut self) -> GDResult { + if self.check_size(4) { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an f32.".to_string())); + } + + let source_data: [u8; 4] = (&self.data[self.position..self.position + 4]).try_into().unwrap(); + + let value = match self.endianess { + Endianess::Little => f32::from_le_bytes(source_data), + Endianess::Big => f32::from_be_bytes(source_data) + }; + + self.position += 4; + Ok(value) + } + + pub fn get_u64(&mut self) -> GDResult { + if self.check_size(8) { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u64.".to_string())); + } + + let source_data: [u8; 8] = (&self.data[self.position..self.position + 8]).try_into().unwrap(); + + let value = match self.endianess { + Endianess::Little => u64::from_le_bytes(source_data), + Endianess::Big => u64::from_be_bytes(source_data) + }; + + self.position += 8; + Ok(value) + } + + pub fn get_string_utf8(&mut self) -> GDResult { + let sub_buf = &self.data[self.position..]; + if sub_buf.len() == 0 { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf8 string.".to_string())); + } + + let first_null_position = sub_buf.iter().position(|&x| x == 0) + .ok_or(GDError::PacketBad("Unexpectedly formatted packet for getting an utf8 string.".to_string()))?; + let value = std::str::from_utf8(&sub_buf[..first_null_position]) + .map_err(|_| GDError::PacketBad("Badly formatted utf8 string.".to_string()))?.to_string(); + + self.position += value.len() + 1; + Ok(value) + } + + pub fn get_string_utf8_unended(&mut self) -> GDResult { + let sub_buf = &self.data[self.position..]; + if sub_buf.len() == 0 { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf8 unended string.".to_string())); + } + + let value = std::str::from_utf8(&sub_buf) + .map_err(|_| GDError::PacketBad("Badly formatted utf8 unended string.".to_string()))?.to_string(); + + self.position += value.len(); + Ok(value) + } + + pub fn get_string_utf16(&mut self) -> GDResult { + let sub_buf = &self.data[self.position..]; + if sub_buf.len() == 0 { + return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf16 string.".to_string())); + } + + let paired_buf: Vec = sub_buf.chunks_exact(2) + .into_iter().map(|a| match self.endianess { + Endianess::Little => u16::from_le_bytes([a[0], a[1]]), + Endianess::Big => u16::from_be_bytes([a[0], a[1]]) + }).collect(); + + let value = String::from_utf16(&paired_buf) + .map_err(|_| GDError::PacketBad("Badly formatted utf16 string.".to_string()))?.to_string(); + + self.position += 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 get_data_in_front_of_position(&self) -> Vec { + self.data[self.position..].to_vec() + } + + pub fn data_length(&self) -> usize { + self.data.len() + } + + pub fn remaining_data(&self) -> &[u8] { + &self.data[self.position..] + } + + pub fn remaining_length(&self) -> usize { + self.data.len() - self.position + } + + 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/lib.rs b/src/lib.rs index f2590ab..b892c3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub mod protocols; pub mod games; mod utils; mod socket; +mod bufferer; pub use errors::*; pub use games::*; diff --git a/src/protocols/minecraft/protocol/bedrock.rs b/src/protocols/minecraft/protocol/bedrock.rs index f406f4f..50ff0ca 100644 --- a/src/protocols/minecraft/protocol/bedrock.rs +++ b/src/protocols/minecraft/protocol/bedrock.rs @@ -5,10 +5,10 @@ https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.j */ use crate::{GDError, GDResult}; +use crate::bufferer::{Bufferer, Endianess}; use crate::protocols::minecraft::{BedrockResponse, GameMode, Server}; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; -use crate::utils::buffer::{get_string_utf8_le_unended, get_u16_be, get_u64_le, get_u8}; use crate::utils::error_by_expected_size; pub struct Bedrock { @@ -42,34 +42,34 @@ impl Bedrock { fn get_info(&mut self) -> GDResult { self.send_status_request()?; - let buf = self.socket.receive(None)?; - let mut pos = 0; + let mut buffer = Bufferer::new_with_data(Endianess::Little, &self.socket.receive(None)?); - if get_u8(&buf, &mut pos)? != 0x1c { + if buffer.get_u8()? != 0x1c { return Err(GDError::PacketBad("Invalid message id.".to_string())); } // Checking for our nonce directly from a u64 (as the nonce is 8 bytes). - if get_u64_le(&buf, &mut pos)? != 9833440827789222417 { + if buffer.get_u64()? != 9833440827789222417 { return Err(GDError::PacketBad("Invalid nonce.".to_string())); } // These 8 bytes are identical to the serverId string we receive in decimal below - pos += 8; + buffer.move_position_ahead(8); // Verifying the magic value (as we need 16 bytes, cast to two u64 values) - if get_u64_le(&buf, &mut pos)? != 18374403896610127616 { + if buffer.get_u64()? != 18374403896610127616 { return Err(GDError::PacketBad("Invalid magic (part 1).".to_string())); } - if get_u64_le(&buf, &mut pos)? != 8671175388723805693 { + if buffer.get_u64()? != 8671175388723805693 { return Err(GDError::PacketBad("Invalid magic (part 2).".to_string())); } - let remaining_length = get_u16_be(&buf, &mut pos)? as usize; - error_by_expected_size(remaining_length, buf.len() - pos)?; + let remaining_length = buffer.as_endianess(Endianess::Big).get_u16()? as usize; + buffer.move_position_ahead(2); + error_by_expected_size(remaining_length, buffer.remaining_length())?; - let binding = get_string_utf8_le_unended(&buf, &mut pos)?; + let binding = buffer.get_string_utf8_unended()?; 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 06b87f6..c042241 100644 --- a/src/protocols/minecraft/protocol/java.rs +++ b/src/protocols/minecraft/protocol/java.rs @@ -1,5 +1,6 @@ use serde_json::Value; use crate::{GDError, GDResult}; +use crate::bufferer::{Bufferer, Endianess}; use crate::protocols::minecraft::{as_varint, get_string, get_varint, Player, Response, Server}; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, TcpSocket}; @@ -22,14 +23,13 @@ impl Java { self.socket.send(&[as_varint(data.len() as i32), data].concat()) } - fn receive(&mut self) -> GDResult> { - let buf = self.socket.receive(None)?; - let mut pos = 0; + fn receive(&mut self) -> GDResult { + let mut buffer = Bufferer::new_with_data(Endianess::Little, &self.socket.receive(None)?); - let _packet_length = get_varint(&buf, &mut pos)? as usize; + 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(buf[pos..].to_vec()) + Ok(buffer) } fn send_handshake(&mut self) -> GDResult<()> { @@ -69,14 +69,13 @@ impl Java { self.send_status_request()?; self.send_ping_request()?; - let buf = self.receive()?; - let mut pos = 0; + let mut buffer = self.receive()?; - if get_varint(&buf, &mut pos)? != 0 { //first var int is the packet id + if get_varint(&mut buffer)? != 0 { //first var int is the packet id return Err(GDError::PacketBad("Bad receive packet id.".to_string())); } - let json_response = get_string(&buf, &mut pos)?; + let json_response = get_string(&mut buffer)?; let value_response: Value = serde_json::from_str(&json_response) .map_err(|e| GDError::JsonParse(e.to_string()))?; diff --git a/src/protocols/minecraft/protocol/legacy_bv1_8.rs b/src/protocols/minecraft/protocol/legacy_bv1_8.rs index 430f38e..0da7d6e 100644 --- a/src/protocols/minecraft/protocol/legacy_bv1_8.rs +++ b/src/protocols/minecraft/protocol/legacy_bv1_8.rs @@ -1,9 +1,9 @@ use crate::{GDError, GDResult}; +use crate::bufferer::{Bufferer, Endianess}; use crate::protocols::minecraft::{LegacyGroup, Response, Server}; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, TcpSocket}; -use crate::utils::buffer::{get_string_utf16_be, get_u16_be, get_u8}; use crate::utils::error_by_expected_size; pub struct LegacyBV1_8 { @@ -27,17 +27,16 @@ impl LegacyBV1_8 { fn get_info(&mut self) -> GDResult { self.send_initial_request()?; - let buf = self.socket.receive(None)?; - let mut pos = 0; + let mut buffer = Bufferer::new_with_data(Endianess::Big, &self.socket.receive(None)?); - if get_u8(&buf, &mut pos)? != 0xFF { + if buffer.get_u8()? != 0xFF { return Err(GDError::ProtocolFormat("Expected 0xFF at the begin of the packet.".to_string())); } - let length = get_u16_be(&buf, &mut pos)? * 2; - error_by_expected_size((length + 3) as usize, buf.len())?; + let length = buffer.get_u16()? * 2; + error_by_expected_size((length + 3) as usize, buffer.data_length())?; - let packet_string = get_string_utf16_be(&buf, &mut pos)?; + let packet_string = buffer.get_string_utf16()?; 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 4c2ab08..108b53d 100644 --- a/src/protocols/minecraft/protocol/legacy_v1_4.rs +++ b/src/protocols/minecraft/protocol/legacy_v1_4.rs @@ -1,10 +1,10 @@ use crate::{GDError, GDResult}; +use crate::bufferer::{Bufferer, Endianess}; use crate::protocols::minecraft::{LegacyGroup, Response, Server}; use crate::protocols::minecraft::protocol::legacy_v1_6::LegacyV1_6; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, TcpSocket}; -use crate::utils::buffer::{get_string_utf16_be, get_u16_be, get_u8}; use crate::utils::error_by_expected_size; pub struct LegacyV1_4 { @@ -28,21 +28,20 @@ impl LegacyV1_4 { fn get_info(&mut self) -> GDResult { self.send_initial_request()?; - let buf = self.socket.receive(None)?; - let mut pos = 0; + let mut buffer = Bufferer::new_with_data(Endianess::Big, &self.socket.receive(None)?); - if get_u8(&buf, &mut pos)? != 0xFF { + if buffer.get_u8()? != 0xFF { return Err(GDError::ProtocolFormat("Expected 0xFF at the begin of the packet.".to_string())); } - let length = get_u16_be(&buf, &mut pos)? * 2; - error_by_expected_size((length + 3) as usize, buf.len())?; + let length = buffer.get_u16()? * 2; + error_by_expected_size((length + 3) as usize, buffer.data_length())?; - if LegacyV1_6::is_protocol(&buf, &mut pos)? { - return LegacyV1_6::get_response(&buf, &mut pos); + if LegacyV1_6::is_protocol(&mut buffer)? { + return LegacyV1_6::get_response(&mut buffer); } - let packet_string = get_string_utf16_be(&buf, &mut pos)?; + let packet_string = buffer.get_string_utf16()?; 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 0034a39..9bd14a4 100644 --- a/src/protocols/minecraft/protocol/legacy_v1_6.rs +++ b/src/protocols/minecraft/protocol/legacy_v1_6.rs @@ -1,8 +1,8 @@ use crate::{GDError, GDResult}; +use crate::bufferer::{Bufferer, Endianess}; use crate::protocols::minecraft::{LegacyGroup, Response, Server}; use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, TcpSocket}; -use crate::utils::buffer::{get_string_utf16_be, get_u16_be, get_u8}; use crate::utils::error_by_expected_size; pub struct LegacyV1_6 { @@ -35,18 +35,18 @@ impl LegacyV1_6 { Ok(()) } - pub fn is_protocol(buf: &[u8], pos: &mut usize) -> GDResult { - let state = buf[*pos..].starts_with(&[0x00, 0xA7, 0x00, 0x31, 0x00, 0x00]); + pub fn is_protocol(buffer: &mut Bufferer) -> GDResult { + let state = buffer.remaining_data().starts_with(&[0x00, 0xA7, 0x00, 0x31, 0x00, 0x00]); if state { - *pos += 6; + buffer.move_position_ahead(6); } Ok(state) } - pub fn get_response(buf: &[u8], pos: &mut usize) -> GDResult { - let packet_string = get_string_utf16_be(&buf, pos)?; + 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())?; @@ -77,21 +77,20 @@ impl LegacyV1_6 { fn get_info(&mut self) -> GDResult { self.send_initial_request()?; - let buf = self.socket.receive(None)?; - let mut pos = 0; + let mut buffer = Bufferer::new_with_data(Endianess::Big, &self.socket.receive(None)?); - if get_u8(&buf, &mut pos)? != 0xFF { + if buffer.get_u8()? != 0xFF { return Err(GDError::ProtocolFormat("Expected a certain byte (0xFF) at the begin of the packet.".to_string())); } - let length = get_u16_be(&buf, &mut pos)? * 2; - error_by_expected_size((length + 3) as usize, buf.len())?; + let length = buffer.get_u16()? * 2; + error_by_expected_size((length + 3) as usize, buffer.data_length())?; - if !LegacyV1_6::is_protocol(&buf, &mut pos)? { + if !LegacyV1_6::is_protocol(&mut buffer)? { return Err(GDError::ProtocolFormat("Expected certain bytes at the beginning of the packet.".to_string())); } - LegacyV1_6::get_response(&buf, &mut pos) + LegacyV1_6::get_response(&mut buffer) } pub fn query(address: &str, port: u16, timeout_settings: Option) -> GDResult { diff --git a/src/protocols/minecraft/types.rs b/src/protocols/minecraft/types.rs index 05999a4..74976c3 100644 --- a/src/protocols/minecraft/types.rs +++ b/src/protocols/minecraft/types.rs @@ -6,7 +6,7 @@ https://github.com/thisjaiden/golden_apple/blob/master/src/lib.rs */ use crate::{GDError, GDResult}; -use crate::utils::buffer::get_u8; +use crate::bufferer::Bufferer; /// The type of Minecraft Server you want to query. #[derive(Debug)] @@ -123,14 +123,14 @@ impl GameMode { } } -pub fn get_varint(buf: &[u8], pos: &mut usize) -> GDResult { +pub fn get_varint(buffer: &mut Bufferer) -> GDResult { let mut result = 0; let msb: u8 = 0b10000000; let mask: u8 = !msb; for i in 0..5 { - let current_byte = get_u8(buf, pos)?; + let current_byte = buffer.get_u8()?; result |= ((current_byte & mask) as i32) << (7 * i); @@ -171,12 +171,12 @@ pub fn as_varint(value: i32) -> Vec { bytes } -pub fn get_string(buf: &[u8], pos: &mut usize) -> GDResult { - let length = get_varint(buf, pos)? as usize; +pub fn get_string(buffer: &mut Bufferer) -> GDResult { + let length = get_varint(buffer)? as usize; let mut text = vec![0; length]; for i in 0..length { - text[i] = get_u8(buf, pos)?; + text[i] = buffer.get_u8()?; } Ok(String::from_utf8(text) diff --git a/src/protocols/valve/protocol.rs b/src/protocols/valve/protocol.rs index c1985ba..e4fc464 100644 --- a/src/protocols/valve/protocol.rs +++ b/src/protocols/valve/protocol.rs @@ -1,10 +1,11 @@ use bzip2_rs::decoder::Decoder; use crate::{GDError, GDResult}; +use crate::bufferer::{Bufferer, Endianess}; use crate::protocols::types::TimeoutSettings; use crate::protocols::valve::{App, ModData, SteamID}; use crate::protocols::valve::types::{Environment, ExtraData, GatheringSettings, Request, Response, Server, ServerInfo, ServerPlayer, ServerRule, TheShip}; use crate::socket::{Socket, UdpSocket}; -use crate::utils::{buffer, u8_lower_upper}; +use crate::utils::u8_lower_upper; #[derive(Debug, Clone)] struct Packet { @@ -14,12 +15,11 @@ struct Packet { } impl Packet { - fn new(buf: &[u8]) -> GDResult { - let mut pos = 0; + fn new(buffer: &mut Bufferer) -> GDResult { Ok(Self { - header: buffer::get_u32_le(&buf, &mut pos)?, - kind: buffer::get_u8(&buf, &mut pos)?, - payload: buf[pos..].to_vec() + header: buffer.get_u32()?, + kind: buffer.get_u8()?, + payload: buffer.get_data_in_front_of_position() }) } @@ -75,27 +75,25 @@ struct SplitPacket { } impl SplitPacket { - fn new(app: &App, protocol: u8, buf: &[u8]) -> GDResult { - let mut pos = 0; - - let header = buffer::get_u32_le(&buf, &mut pos)?; - let id = buffer::get_u32_le(&buf, &mut pos)?; + fn new(app: &App, protocol: u8, buffer: &mut Bufferer) -> GDResult { + let header = buffer.get_u32()?; + let id = buffer.get_u32()?; let (total, number, size, compressed, decompressed_size, uncompressed_crc32) = match app { App::GoldSrc(_) => { - let (lower, upper) = u8_lower_upper(buffer::get_u8(&buf, &mut pos)?); + let (lower, upper) = u8_lower_upper(buffer.get_u8()?); (lower, upper, 0, false, None, None) } App::Source(_) => { - let total = buffer::get_u8(&buf, &mut pos)?; - let number = buffer::get_u8(&buf, &mut pos)?; + let total = buffer.get_u8()?; + let number = buffer.get_u8()?; let size = match protocol == 7 && (*app == SteamID::CSS.as_app()) { //certain apps with protocol = 7 doesnt have this field - false => buffer::get_u16_le(&buf, &mut pos)?, + false => buffer.get_u16()?, true => 1248 }; let compressed = ((id >> 31) & 1) == 1; let (decompressed_size, uncompressed_crc32) = match compressed { false => (None, None), - true => (Some(buffer::get_u32_le(&buf, &mut pos)?), Some(buffer::get_u32_le(&buf, &mut pos)?)) + true => (Some(buffer.get_u32()?), Some(buffer.get_u32()?)) }; (total, number, size, compressed, decompressed_size, uncompressed_crc32) } @@ -110,7 +108,7 @@ impl SplitPacket { compressed, decompressed_size, uncompressed_crc32, - payload: buf[pos..].to_vec() + payload: buffer.get_data_in_front_of_position() }) } @@ -156,21 +154,26 @@ impl ValveProtocol { } fn receive(&mut self, app: &App, protocol: u8, buffer_size: usize) -> GDResult { - let mut buf = self.socket.receive(Some(buffer_size))?; + let data = self.socket.receive(Some(buffer_size))?; + let mut buffer = Bufferer::new_with_data(Endianess::Little, &data); - if buf[0] == 0xFE { //the packet is split - let mut main_packet = SplitPacket::new(&app, protocol, &buf)?; + let header = buffer.get_u8()?; + buffer.move_position_backward(1); + if header == 0xFE { //the packet is split + let mut main_packet = SplitPacket::new(&app, protocol, &mut buffer)?; for _ in 1..main_packet.total { - buf = self.socket.receive(Some(buffer_size))?; - let chunk_packet = SplitPacket::new(&app, protocol, &buf)?; + let new_data = self.socket.receive(Some(buffer_size))?; + buffer = Bufferer::new_with_data(Endianess::Little, &new_data); + let chunk_packet = SplitPacket::new(&app, protocol, &mut buffer)?; main_packet.payload.extend(chunk_packet.payload); } - Ok(Packet::new(&main_packet.get_payload()?)?) + let mut new_packet_buffer = Bufferer::new_with_data(Endianess::Little, &main_packet.get_payload()?); + Ok(Packet::new(&mut new_packet_buffer)?) } else { - Packet::new(&buf) + Packet::new(&mut buffer) } } @@ -192,44 +195,42 @@ impl ValveProtocol { Ok(self.receive(app, protocol, PACKET_SIZE)?.payload) } - fn get_goldsrc_server_info(buf: &[u8]) -> GDResult { - let mut pos = 0; - - buffer::get_u8(&buf, &mut pos)?; //get the header (useless info) - buffer::get_string_utf8_le(&buf, &mut pos)?; //get the server address (useless info) - let name = buffer::get_string_utf8_le(&buf, &mut pos)?; - let map = buffer::get_string_utf8_le(&buf, &mut pos)?; - let folder = buffer::get_string_utf8_le(&buf, &mut pos)?; - let game = buffer::get_string_utf8_le(&buf, &mut pos)?; - let players = buffer::get_u8(&buf, &mut pos)?; - let max_players = buffer::get_u8(&buf, &mut pos)?; - let protocol = buffer::get_u8(&buf, &mut pos)?; - let server_type = match buffer::get_u8(&buf, &mut pos)? { + 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()? { 68 => Server::Dedicated, //'D' 76 => Server::NonDedicated, //'L' 80 => Server::TV, //'P' _ => Err(GDError::UnknownEnumCast)? }; - let environment_type = match buffer::get_u8(&buf, &mut pos)? { + let environment_type = match buffer.get_u8()? { 76 => Environment::Linux, //'L' 87 => Environment::Windows, //'W' _ => Err(GDError::UnknownEnumCast)? }; - let has_password = buffer::get_u8(&buf, &mut pos)? == 1; - let is_mod = buffer::get_u8(&buf, &mut pos)? == 1; + let has_password = buffer.get_u8()? == 1; + let is_mod = buffer.get_u8()? == 1; let mod_data = match is_mod { false => None, true => Some(ModData { - link: buffer::get_string_utf8_le(&buf, &mut pos)?, - download_link: buffer::get_string_utf8_le(&buf, &mut pos)?, - version: buffer::get_u32_le(&buf, &mut pos)?, - size: buffer::get_u32_le(&buf, &mut pos)?, - multiplayer_only: buffer::get_u8(&buf, &mut pos)? == 1, - has_own_dll: buffer::get_u8(&buf, &mut pos)? == 1 + 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 }) }; - let vac_secured = buffer::get_u8(&buf, &mut pos)? == 1; - let bots = buffer::get_u8(&buf, &mut pos)?; + let vac_secured = buffer.get_u8()? == 1; + let bots = buffer.get_u8()?; Ok(ServerInfo { protocol, @@ -255,74 +256,74 @@ impl ValveProtocol { /// Get the server information's. fn get_server_info(&mut self, app: &App) -> GDResult { - let buf = self.get_request_data(&app, 0, Request::INFO)?; + let data = self.get_request_data(&app, 0, Request::INFO)?; + let mut buffer = Bufferer::new_with_data(Endianess::Little, &data); + if let App::GoldSrc(force) = app { if *force { - return ValveProtocol::get_goldsrc_server_info(&buf); + return ValveProtocol::get_goldsrc_server_info(&mut buffer); } } - let mut pos = 0; - - let protocol = buffer::get_u8(&buf, &mut pos)?; - let name = buffer::get_string_utf8_le(&buf, &mut pos)?; - let map = buffer::get_string_utf8_le(&buf, &mut pos)?; - let folder = buffer::get_string_utf8_le(&buf, &mut pos)?; - let game = buffer::get_string_utf8_le(&buf, &mut pos)?; - let mut appid = buffer::get_u16_le(&buf, &mut pos)? as u32; - let players = buffer::get_u8(&buf, &mut pos)?; - let max_players = buffer::get_u8(&buf, &mut pos)?; - let bots = buffer::get_u8(&buf, &mut pos)?; - let server_type = match buffer::get_u8(&buf, &mut pos)? { + 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 = match buffer.get_u8()? { 100 => Server::Dedicated, //'d' 108 => Server::NonDedicated, //'l' 112 => Server::TV, //'p' _ => Err(GDError::UnknownEnumCast)? }; - let environment_type = match buffer::get_u8(&buf, &mut pos)? { + let environment_type = match buffer.get_u8()? { 108 => Environment::Linux, //'l' 119 => Environment::Windows, //'w' 109 | 111 => Environment::Mac, //'m' or 'o' _ => Err(GDError::UnknownEnumCast)? }; - let has_password = buffer::get_u8(&buf, &mut pos)? == 1; - let vac_secured = buffer::get_u8(&buf, &mut pos)? == 1; + let has_password = buffer.get_u8()? == 1; + let vac_secured = buffer.get_u8()? == 1; let the_ship = match *app == SteamID::TS.as_app() { false => None, true => Some(TheShip { - mode: buffer::get_u8(&buf, &mut pos)?, - witnesses: buffer::get_u8(&buf, &mut pos)?, - duration: buffer::get_u8(&buf, &mut pos)? + mode: buffer.get_u8()?, + witnesses: buffer.get_u8()?, + duration: buffer.get_u8()? }) }; - let version = buffer::get_string_utf8_le(&buf, &mut pos)?; - let extra_data = match buffer::get_u8(&buf, &mut pos) { + let version = buffer.get_string_utf8()?; + let extra_data = match buffer.get_u8() { Err(_) => None, Ok(value) => Some(ExtraData { port: match (value & 0x80) > 0 { false => None, - true => Some(buffer::get_u16_le(&buf, &mut pos)?) + true => Some(buffer.get_u16()?) }, steam_id: match (value & 0x10) > 0 { false => None, - true => Some(buffer::get_u64_le(&buf, &mut pos)?) + true => Some(buffer.get_u64()?) }, tv_port: match (value & 0x40) > 0 { false => None, - true => Some(buffer::get_u16_le(&buf, &mut pos)?) + true => Some(buffer.get_u16()?) }, tv_name: match (value & 0x40) > 0 { false => None, - true => Some(buffer::get_string_utf8_le(&buf, &mut pos)?) + true => Some(buffer.get_string_utf8()?) }, keywords: match (value & 0x20) > 0 { false => None, - true => Some(buffer::get_string_utf8_le(&buf, &mut pos)?) + true => Some(buffer.get_string_utf8()?) }, game_id: match (value & 0x01) > 0 { false => None, true => { - let gid = buffer::get_u64_le(&buf, &mut pos)?; + let gid = buffer.get_u64()?; appid = (gid & ((1 << 24) - 1)) as u32; Some(gid) @@ -355,25 +356,25 @@ impl ValveProtocol { /// Get the server player's. fn get_server_players(&mut self, app: &App, protocol: u8) -> GDResult> { - let buf = self.get_request_data(&app, protocol, Request::PLAYERS)?; - let mut pos = 0; + let data = self.get_request_data(&app, protocol, Request::PLAYERS)?; + let mut buffer = Bufferer::new_with_data(Endianess::Little, &data); - let count = buffer::get_u8(&buf, &mut pos)? as usize; + let count = buffer.get_u8()? as usize; let mut players: Vec = Vec::with_capacity(count); for _ in 0..count { - pos += 1; //skip the index byte + buffer.move_position_ahead(1); //skip the index byte players.push(ServerPlayer { - name: buffer::get_string_utf8_le(&buf, &mut pos)?, - score: buffer::get_u32_le(&buf, &mut pos)?, - duration: buffer::get_f32_le(&buf, &mut pos)?, + name: buffer.get_string_utf8()?, + score: buffer.get_u32()?, + duration: buffer.get_f32()?, deaths: match *app == SteamID::TS.as_app() { false => None, - true => Some(buffer::get_u32_le(&buf, &mut pos)?) + true => Some(buffer.get_u32()?) }, money: match *app == SteamID::TS.as_app() { false => None, - true => Some(buffer::get_u32_le(&buf, &mut pos)?) + true => Some(buffer.get_u32()?) } }); } @@ -383,16 +384,16 @@ impl ValveProtocol { /// Get the server rules's. fn get_server_rules(&mut self, app: &App, protocol: u8) -> GDResult>> { - let buf = self.get_request_data(&app, protocol, Request::RULES)?; - let mut pos = 0; + let data = self.get_request_data(&app, protocol, Request::RULES)?; + let mut buffer = Bufferer::new_with_data(Endianess::Little, &data); - let count = buffer::get_u16_le(&buf, &mut pos)? as usize; + let count = buffer.get_u16()? as usize; let mut rules: Vec = Vec::with_capacity(count); for _ in 0..count { rules.push(ServerRule { - name: buffer::get_string_utf8_le(&buf, &mut pos)?, - value: buffer::get_string_utf8_le(&buf, &mut pos)? + name: buffer.get_string_utf8()?, + value: buffer.get_string_utf8()? }) } diff --git a/src/utils.rs b/src/utils.rs index 8f4c7f5..0043843 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,222 +20,22 @@ pub fn u8_lower_upper(n: u8) -> (u8, u8) { (n & 15, n >> 4) } -pub mod buffer { - use super::*; - - pub fn get_u8(buf: &[u8], pos: &mut usize) -> GDResult { - if buf.len() <= *pos { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u8.".to_string())); - } - - let value = buf[*pos]; - *pos += 1; - Ok(value) - } - - pub fn get_u16_le(buf: &[u8], pos: &mut usize) -> GDResult { - if buf.len() <= *pos + 1 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u16.".to_string())); - } - - let value = u16::from_le_bytes([buf[*pos], buf[*pos + 1]]); - *pos += 2; - Ok(value) - } - - pub fn get_u16_be(buf: &[u8], pos: &mut usize) -> GDResult { - if buf.len() <= *pos + 1 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u16.".to_string())); - } - - let value = u16::from_be_bytes([buf[*pos], buf[*pos + 1]]); - *pos += 2; - Ok(value) - } - - pub fn get_u32_le(buf: &[u8], pos: &mut usize) -> GDResult { - if buf.len() <= *pos + 3 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u32.".to_string())); - } - - let value = u32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]); - *pos += 4; - Ok(value) - } - - pub fn get_f32_le(buf: &[u8], pos: &mut usize) -> GDResult { - if buf.len() <= *pos + 3 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an f32.".to_string())); - } - - let value = f32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]); - *pos += 4; - Ok(value) - } - - pub fn get_u64_le(buf: &[u8], pos: &mut usize) -> GDResult { - if buf.len() <= *pos + 7 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an u64.".to_string())); - } - - let value = u64::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3], buf[*pos + 4], buf[*pos + 5], buf[*pos + 6], buf[*pos + 7]]); - *pos += 8; - Ok(value) - } - - pub fn get_string_utf8_le(buf: &[u8], pos: &mut usize) -> GDResult { - let sub_buf = &buf[*pos..]; - if sub_buf.len() == 0 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf8 LE string.".to_string())); - } - - let first_null_position = sub_buf.iter().position(|&x| x == 0) - .ok_or(GDError::PacketBad("Unexpectedly formatted packet for getting a utf8 LE string.".to_string()))?; - let value = std::str::from_utf8(&sub_buf[..first_null_position]) - .map_err(|_| GDError::PacketBad("Badly formatted utf8 LE string.".to_string()))?.to_string(); - - *pos += value.len() + 1; - Ok(value) - } - - pub fn get_string_utf8_le_unended(buf: &[u8], pos: &mut usize) -> GDResult { - let sub_buf = &buf[*pos..]; - if sub_buf.len() == 0 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf8 LE string.".to_string())); - } - - let value = std::str::from_utf8(&sub_buf) - .map_err(|_| GDError::PacketBad("Badly formatted utf8 LE string.".to_string()))?.to_string(); - - *pos += value.len(); - Ok(value) - } - - pub fn get_string_utf16_be(buf: &[u8], pos: &mut usize) -> GDResult { - let sub_buf = &buf[*pos..]; - if sub_buf.len() == 0 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf16 BE string.".to_string())); - } - - let paired_buf: Vec = sub_buf.chunks_exact(2) - .into_iter().map(|a| u16::from_be_bytes([a[0], a[1]])).collect(); - - let value = String::from_utf16(&paired_buf) - .map_err(|_| GDError::PacketBad("Badly formatted utf16 BE string.".to_string()))?.to_string(); - - *pos += value.len() * 2; - Ok(value) - } -} - #[cfg(test)] mod tests { - use super::*; - #[test] - fn address_and_port_as_string_test() { - assert_eq!(address_and_port_as_string("192.168.0.1", 27015), "192.168.0.1:27015"); + fn address_and_port_as_string() { + assert_eq!(super::address_and_port_as_string("192.168.0.1", 27015), "192.168.0.1:27015"); } #[test] - fn u8_lower_upper_test() { - assert_eq!(u8_lower_upper(171), (11, 10)); + fn u8_lower_upper() { + assert_eq!(super::u8_lower_upper(171), (11, 10)); } #[test] - fn get_u8_test() { - let data = [72]; - let mut pos = 0; - assert_eq!(buffer::get_u8(&data, &mut pos).unwrap(), 72); - assert_eq!(pos, 1); - assert!(buffer::get_u8(&data, &mut pos).is_err()); - assert_eq!(pos, 1); - } - - #[test] - fn get_u16_le_test() { - let data = [72, 29]; - let mut pos = 0; - assert_eq!(buffer::get_u16_le(&data, &mut pos).unwrap(), 7496); - assert_eq!(pos, 2); - assert!(buffer::get_u16_le(&data, &mut pos).is_err()); - assert_eq!(pos, 2); - } - - #[test] - fn get_u16_be_test() { - let data = [29, 72]; - let mut pos = 0; - assert_eq!(buffer::get_u16_be(&data, &mut pos).unwrap(), 7496); - assert_eq!(pos, 2); - assert!(buffer::get_u16_be(&data, &mut pos).is_err()); - assert_eq!(pos, 2); - } - - #[test] - fn get_u32_le_test() { - let data = [72, 29, 128, 100]; - let mut pos = 0; - assert_eq!(buffer::get_u32_le(&data, &mut pos).unwrap(), 1686117704); - assert_eq!(pos, 4); - assert!(buffer::get_u32_le(&data, &mut pos).is_err()); - assert_eq!(pos, 4); - } - - #[test] - fn get_f32_le_test() { - let data = [72, 29, 128, 100]; - let mut pos = 0; - assert_eq!(buffer::get_f32_le(&data, &mut pos).unwrap(), 1.8906345e22); - assert_eq!(pos, 4); - assert!(buffer::get_f32_le(&data, &mut pos).is_err()); - assert_eq!(pos, 4); - } - - #[test] - fn get_u64_le_test() { - let data = [72, 29, 128, 99, 69, 4, 2, 0]; - let mut pos = 0; - assert_eq!(buffer::get_u64_le(&data, &mut pos).unwrap(), 567646022016328); - assert_eq!(pos, 8); - assert!(buffer::get_u64_le(&data, &mut pos).is_err()); - assert_eq!(pos, 8); - } - - #[test] - fn get_string_utf8_le_test() { - let data = [72, 101, 108, 108, 111, 0, 72]; - let mut pos = 0; - assert_eq!(buffer::get_string_utf8_le(&data, &mut pos).unwrap(), "Hello"); - assert_eq!(pos, 6); - assert!(buffer::get_string_utf8_le(&data, &mut pos).is_err()); - assert_eq!(pos, 6); - } - - #[test] - fn get_string_utf8_le_unended_test() { - let data = [72, 101, 108, 108, 111]; - let mut pos = 0; - assert_eq!(buffer::get_string_utf8_le_unended(&data, &mut pos).unwrap(), "Hello"); - assert_eq!(pos, 5); - assert!(buffer::get_string_utf8_le_unended(&data, &mut pos).is_err()); - assert_eq!(pos, 5); - } - - #[test] - fn get_string_utf16_be_test() { - let data = [0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f]; - let mut pos = 0; - assert_eq!(buffer::get_string_utf16_be(&data, &mut pos).unwrap(), "Hello"); - assert_eq!(pos, 10); - assert!(buffer::get_string_utf16_be(&data, &mut pos).is_err()); - assert_eq!(pos, 10); - } - - #[test] - fn error_by_expected_size_test() { - assert!(error_by_expected_size(69, 69).is_ok()); - assert!(error_by_expected_size(69, 68).is_err()); - assert!(error_by_expected_size(69, 70).is_err()); + fn error_by_expected_size() { + assert!(super::error_by_expected_size(69, 69).is_ok()); + assert!(super::error_by_expected_size(69, 68).is_err()); + assert!(super::error_by_expected_size(69, 70).is_err()); } }