Better, faster and stronger errors.

This commit is contained in:
CosminPerRam 2022-11-24 23:38:51 +02:00
parent b988b51cff
commit 304b8340d2
7 changed files with 75 additions and 69 deletions

View file

@ -11,33 +11,35 @@ pub type GDResult<T> = Result<T, GDError>;
#[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}"),
}
}
}

View file

@ -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<Vec<Player>> = 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()
})
}

View file

@ -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(),

View file

@ -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(),

View file

@ -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)

View file

@ -117,18 +117,18 @@ impl SplitPacket {
fn get_payload(&self) -> GDResult<Vec<u8>> {
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()));
}
}
}

View file

@ -4,12 +4,24 @@ use crate::{GDResult, GDError};
fn resolve_dns(address: &str) -> GDResult<String> {
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<String> {
@ -25,7 +37,7 @@ pub mod buffer {
pub fn get_u8(buf: &[u8], pos: &mut usize) -> GDResult<u8> {
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<u16> {
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<u16> {
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<u32> {
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<f32> {
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<u64> {
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<String> {
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)