From 304b8340d2fa4f29bebd88ba5f9245b2ee11c01b Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Thu, 24 Nov 2022 23:38:51 +0200 Subject: [PATCH] Better, faster and stronger errors. --- src/errors.rs | 31 ++++++++-------- src/protocols/minecraft/protocol/java.rs | 18 +++++----- .../minecraft/protocol/legacy_bv1_8.rs | 15 ++++---- .../minecraft/protocol/legacy_v1_4.rs | 15 ++++---- .../minecraft/protocol/legacy_v1_6.rs | 19 +++++----- src/protocols/valve/protocol.rs | 10 +++--- src/utils.rs | 36 ++++++++++++------- 7 files changed, 75 insertions(+), 69 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 6ed3bf9..23711a9 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -11,33 +11,35 @@ pub type GDResult = Result; #[derive(Debug, Clone)] pub enum GDError { /// The received packet was bigger than the buffer size. - PacketOverflow(String), + PacketOverflow(&'static str), /// The received packet was shorter than the expected one. - PacketUnderflow(String), + PacketUnderflow(&'static str), /// The received packet was badly formatted. - PacketBad(String), + PacketBad(&'static str), /// Couldn't send the packet. - PacketSend(String), + PacketSend(&'static str), /// Couldn't send the receive. - PacketReceive(String), + PacketReceive(&'static str), /// Couldn't decompress data. - Decompress(String), + Decompress(&'static str), /// Unknown cast while translating a value to an enum. UnknownEnumCast, /// The server queried is not from the queried game. - BadGame(String), + BadGame(&'static str), /// Problems occurred while dns resolving. - DnsResolve(String), + DnsResolve(&'static str), /// Couldn't bind a socket. - SocketBind(String), + SocketBind(&'static str), /// Invalid input. - InvalidInput(String), + InvalidInput(&'static str), /// Couldn't create a socket connection. - SocketConnect(String), + SocketConnect(&'static str), /// Couldn't parse a json string. - JsonParse(String), - /// Couldn't parse a json string. - AutoQuery(String), + JsonParse(&'static str), + /// Couldn't automatically query. + AutoQuery(&'static str), + /// A protocol-defined expected format was not met. + ProtocolRule(&'static str), } impl fmt::Display for GDError { @@ -57,6 +59,7 @@ impl fmt::Display for GDError { GDError::SocketConnect(details) => write!(f, "Socket connect: {details}"), GDError::JsonParse(details) => write!(f, "Json parse: {details}"), GDError::AutoQuery(details) => write!(f, "Auto query: {details}"), + GDError::ProtocolRule(details) => write!(f, "Protocol rule: {details}"), } } } diff --git a/src/protocols/minecraft/protocol/java.rs b/src/protocols/minecraft/protocol/java.rs index 06b87f6..3e491c1 100644 --- a/src/protocols/minecraft/protocol/java.rs +++ b/src/protocols/minecraft/protocol/java.rs @@ -73,33 +73,33 @@ impl Java { let mut pos = 0; if get_varint(&buf, &mut pos)? != 0 { //first var int is the packet id - return Err(GDError::PacketBad("Bad receive packet id.".to_string())); + return Err(GDError::PacketBad("Bad receive packet id.")); } let json_response = get_string(&buf, &mut pos)?; let value_response: Value = serde_json::from_str(&json_response) - .map_err(|e| GDError::JsonParse(e.to_string()))?; + .map_err(|e| GDError::JsonParse(e.to_string().as_str()))?; let version_name = value_response["version"]["name"].as_str() - .ok_or(GDError::PacketBad("Couldn't get expected string.".to_string()))?.to_string(); + .ok_or(GDError::PacketBad("Couldn't get expected string."))?.to_string(); let version_protocol = value_response["version"]["protocol"].as_i64() - .ok_or(GDError::PacketBad("Couldn't get expected number.".to_string()))? as i32; + .ok_or(GDError::PacketBad("Couldn't get expected number."))? as i32; let max_players = value_response["players"]["max"].as_u64() - .ok_or(GDError::PacketBad("Couldn't get expected number.".to_string()))? as u32; + .ok_or(GDError::PacketBad("Couldn't get expected number."))? as u32; let online_players = value_response["players"]["online"].as_u64() - .ok_or(GDError::PacketBad("Couldn't get expected number.".to_string()))? as u32; + .ok_or(GDError::PacketBad("Couldn't get expected number."))? as u32; let sample_players: Option> = match value_response["players"]["sample"].is_null() { true => None, false => Some({ let players_values = value_response["players"]["sample"].as_array() - .ok_or(GDError::PacketBad("Couldn't get expected array.".to_string()))?; + .ok_or(GDError::PacketBad("Couldn't get expected array."))?; let mut players = Vec::with_capacity(players_values.len()); for player in players_values { players.push(Player { - name: player["name"].as_str().ok_or(GDError::PacketBad("Couldn't get expected string.".to_string()))?.to_string(), - id: player["id"].as_str().ok_or(GDError::PacketBad("Couldn't get expected string.".to_string()))?.to_string() + name: player["name"].as_str().ok_or(GDError::PacketBad("Couldn't get expected string."))?.to_string(), + id: player["id"].as_str().ok_or(GDError::PacketBad("Couldn't get expected string."))?.to_string() }) } diff --git a/src/protocols/minecraft/protocol/legacy_bv1_8.rs b/src/protocols/minecraft/protocol/legacy_bv1_8.rs index abfac32..b935c9a 100644 --- a/src/protocols/minecraft/protocol/legacy_bv1_8.rs +++ b/src/protocols/minecraft/protocol/legacy_bv1_8.rs @@ -4,6 +4,7 @@ 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 { socket: TcpSocket @@ -30,26 +31,22 @@ impl LegacyBV1_8 { let mut pos = 0; if get_u8(&buf, &mut pos)? != 0xFF { - return Err(GDError::PacketBad("Expected 0xFF".to_string())); + return Err(GDError::ProtocolRule("Expected 0xFF at the begin of the packet.")); } let length = get_u16_be(&buf, &mut pos)? * 2; - if buf.len() != (length + 3) as usize { //+ 3 because of the first byte and the u16 - return Err(GDError::PacketBad("Not right size".to_string())); - } + error_by_expected_size((length + 3) as usize, buf.len())?; let packet_string = get_string_utf16_be(&buf, &mut pos)?; let split: Vec<&str> = packet_string.split("§").collect(); - if split.len() != 3 { - return Err(GDError::PacketBad("Not right size".to_string())); - } + error_by_expected_size(3, split.len())?; let description = split[0].to_string(); let online_players = split[1].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; let max_players = split[2].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; Ok(Response { version_name: "Beta 1.8+".to_string(), diff --git a/src/protocols/minecraft/protocol/legacy_v1_4.rs b/src/protocols/minecraft/protocol/legacy_v1_4.rs index 853a24a..29815be 100644 --- a/src/protocols/minecraft/protocol/legacy_v1_4.rs +++ b/src/protocols/minecraft/protocol/legacy_v1_4.rs @@ -5,6 +5,7 @@ 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 { socket: TcpSocket @@ -31,13 +32,11 @@ impl LegacyV1_4 { let mut pos = 0; if get_u8(&buf, &mut pos)? != 0xFF { - return Err(GDError::PacketBad("Expected 0xFF".to_string())); + return Err(GDError::ProtocolRule("Expected 0xFF at the begin of the packet.")); } let length = get_u16_be(&buf, &mut pos)? * 2; - if buf.len() != (length + 3) as usize { //+ 3 because of the first byte and the u16 - return Err(GDError::PacketBad("Not right size".to_string())); - } + error_by_expected_size((length + 3) as usize, buf.len())?; if LegacyV1_6::is_protocol(&buf, &mut pos)? { return LegacyV1_6::get_response(&buf, &mut pos); @@ -46,15 +45,13 @@ impl LegacyV1_4 { let packet_string = get_string_utf16_be(&buf, &mut pos)?; let split: Vec<&str> = packet_string.split("§").collect(); - if split.len() != 3 { - return Err(GDError::PacketBad("Not right size".to_string())); - } + error_by_expected_size(3, split.len())?; let description = split[0].to_string(); let online_players = split[1].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; let max_players = split[2].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; Ok(Response { version_name: "1.4+".to_string(), diff --git a/src/protocols/minecraft/protocol/legacy_v1_6.rs b/src/protocols/minecraft/protocol/legacy_v1_6.rs index edbaa73..0659261 100644 --- a/src/protocols/minecraft/protocol/legacy_v1_6.rs +++ b/src/protocols/minecraft/protocol/legacy_v1_6.rs @@ -3,6 +3,7 @@ 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 { socket: TcpSocket @@ -48,18 +49,16 @@ impl LegacyV1_6 { let packet_string = get_string_utf16_be(&buf, pos)?; let split: Vec<&str> = packet_string.split("\x00").collect(); - if split.len() != 5 { - return Err(GDError::PacketBad("Not right split size".to_string())); - } + error_by_expected_size(5, split.len())?; let version_protocol = split[0].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; let version_name = split[1].to_string(); let description = split[2].to_string(); let max_players = split[3].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; let online_players = split[4].parse() - .map_err(|_| GDError::PacketBad("Expected int".to_string()))?; + .map_err(|_| GDError::PacketBad("Failed to parse to expected int."))?; Ok(Response { version_name, @@ -82,16 +81,14 @@ impl LegacyV1_6 { let mut pos = 0; if get_u8(&buf, &mut pos)? != 0xFF { - return Err(GDError::PacketBad("Expected 0xFF".to_string())); + return Err(GDError::ProtocolRule("Expected a certain byte (0xFF) at the begin of the packet.")); } let length = get_u16_be(&buf, &mut pos)? * 2; - if buf.len() != (length + 3) as usize { //+ 3 because of the first byte and the u16 - return Err(GDError::PacketBad("Not right size".to_string())); - } + error_by_expected_size((length + 3) as usize, buf.len())?; if !LegacyV1_6::is_protocol(&buf, &mut pos)? { - return Err(GDError::PacketBad("Not good".to_string())); + return Err(GDError::ProtocolRule("Expected certain bytes at the beginning of the packet.")); } LegacyV1_6::get_response(&buf, &mut pos) diff --git a/src/protocols/valve/protocol.rs b/src/protocols/valve/protocol.rs index 780ce81..cbdcac6 100644 --- a/src/protocols/valve/protocol.rs +++ b/src/protocols/valve/protocol.rs @@ -117,18 +117,18 @@ impl SplitPacket { fn get_payload(&self) -> GDResult> { if self.compressed { let mut decoder = Decoder::new(); - decoder.write(&self.payload).map_err(|e| GDError::Decompress(e.to_string()))?; + decoder.write(&self.payload).map_err(|e| GDError::Decompress(e.to_string().as_str()))?; let decompressed_size = self.decompressed_size.unwrap() as usize; let mut decompressed_payload = Vec::with_capacity(decompressed_size); - decoder.read(&mut decompressed_payload).map_err(|e| GDError::Decompress(e.to_string()))?; + decoder.read(&mut decompressed_payload).map_err(|e| GDError::Decompress(e.to_string().as_str()))?; if decompressed_payload.len() != decompressed_size { - Err(GDError::Decompress("Valve Protocol: The decompressed payload size doesn't match the expected one.".to_string())) + Err(GDError::Decompress("The decompressed payload size doesn't match the expected one.")) } else if crc32fast::hash(&decompressed_payload) != self.uncompressed_crc32.unwrap() { - Err(GDError::Decompress("Valve Protocol: The decompressed crc32 hash does not match the expected one.".to_string())) + Err(GDError::Decompress("The decompressed crc32 hash does not match the expected one.")) } else { Ok(decompressed_payload) @@ -420,7 +420,7 @@ fn get_response(address: &str, port: u16, app: App, gather_settings: GatheringSe if let App::Source(x) = &app { if let Some(appid) = x { if *appid != info.appid { - return Err(GDError::BadGame(format!("Expected {}, found {} instead!", *appid, info.appid))); + return Err(GDError::BadGame(format!("Expected {}, found {} instead!", *appid, info.appid).as_str())); } } } diff --git a/src/utils.rs b/src/utils.rs index a5c9772..6b334a9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,12 +4,24 @@ use crate::{GDResult, GDError}; fn resolve_dns(address: &str) -> GDResult { let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()) - .map_err(|e| GDError::DnsResolve(e.to_string()))?; + .map_err(|e| GDError::DnsResolve(e.to_string().as_str()))?; let response = resolver.lookup_ip(address) - .map_err(|e| GDError::DnsResolve(e.to_string()))?; + .map_err(|e| GDError::DnsResolve(e.to_string().as_str()))?; - Ok(response.iter().next().ok_or(GDError::DnsResolve("Couldn't resolve the DNS address.".to_string()))?.to_string()) + Ok(response.iter().next().ok_or(GDError::DnsResolve("Couldn't resolve the DNS address."))?.to_string()) +} + +pub fn error_by_expected_size(expected: usize, size: usize) -> GDResult<()> { + if size < expected { + Err(GDError::PacketUnderflow("Unexpectedly short packet.")) + } + else if size > expected { + Err(GDError::PacketOverflow("Unexpectedly long packet.")) + } + else { + Ok(()) + } } pub fn complete_address(address: &str, port: u16) -> GDResult { @@ -25,7 +37,7 @@ pub mod buffer { pub fn get_u8(buf: &[u8], pos: &mut usize) -> GDResult { if buf.len() <= *pos { - return Err(GDError::PacketUnderflow("Unexpectedly short packet.".to_string())); + return Err(GDError::PacketUnderflow("Unexpectedly short packet.")); } let value = buf[*pos]; @@ -35,7 +47,7 @@ pub mod buffer { pub fn get_u16_le(buf: &[u8], pos: &mut usize) -> GDResult { if buf.len() <= *pos + 1 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet.".to_string())); + return Err(GDError::PacketUnderflow("Unexpectedly short packet.")); } let value = u16::from_le_bytes([buf[*pos], buf[*pos + 1]]); @@ -45,7 +57,7 @@ pub mod buffer { pub fn get_u16_be(buf: &[u8], pos: &mut usize) -> GDResult { if buf.len() <= *pos + 1 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet.".to_string())); + return Err(GDError::PacketUnderflow("Unexpectedly short packet.")); } let value = u16::from_be_bytes([buf[*pos], buf[*pos + 1]]); @@ -55,7 +67,7 @@ pub mod buffer { pub fn get_u32_le(buf: &[u8], pos: &mut usize) -> GDResult { if buf.len() <= *pos + 3 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet.".to_string())); + return Err(GDError::PacketUnderflow("Unexpectedly short packet.")); } let value = u32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]); @@ -65,7 +77,7 @@ pub mod buffer { pub fn get_f32_le(buf: &[u8], pos: &mut usize) -> GDResult { if buf.len() <= *pos + 3 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet.".to_string())); + return Err(GDError::PacketUnderflow("Unexpectedly short packet.")); } let value = f32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]); @@ -75,7 +87,7 @@ pub mod buffer { pub fn get_u64_le(buf: &[u8], pos: &mut usize) -> GDResult { if buf.len() <= *pos + 7 { - return Err(GDError::PacketUnderflow("Unexpectedly short packet.".to_string())); + return Err(GDError::PacketUnderflow("Unexpectedly short packet.")); } 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]]); @@ -86,9 +98,9 @@ pub mod buffer { pub fn get_string_utf8_le(buf: &[u8], pos: &mut usize) -> GDResult { let sub_buf = &buf[*pos..]; let first_null_position = sub_buf.iter().position(|&x| x == 0) - .ok_or(GDError::PacketBad("Unexpectedly formatted packet.".to_string()))?; + .ok_or(GDError::PacketBad("Unexpectedly formatted packet."))?; let value = std::str::from_utf8(&sub_buf[..first_null_position]) - .map_err(|_| GDError::PacketBad("Badly formatted string.".to_string()))?.to_string(); + .map_err(|_| GDError::PacketBad("Badly formatted string."))?.to_string(); *pos += value.len() + 1; Ok(value) @@ -100,7 +112,7 @@ pub mod buffer { .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 string.".to_string()))?.to_string(); + .map_err(|_| GDError::PacketBad("Badly formatted string."))?.to_string(); *pos += value.len() + 1; Ok(value)