Decompression support

This commit is contained in:
cosminperram 2022-10-22 02:27:11 +03:00
parent e621a9aedd
commit 3b4dd9d9e4
4 changed files with 61 additions and 24 deletions

View file

@ -15,3 +15,5 @@ keywords = ["server", "verify", "game", "check", "status"]
msrv = "1.58.1"
[dependencies]
crc32fast = "1.3.2"
bzip2-rs = "0.1.2"

View file

@ -1,8 +1,8 @@
# Supported protocols:
| Name | Documentation reference | Used by |
|----------------|---------------------------------------------------------------------------|---------------------|
| Valve Protocol | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | TF2, CSGO, The_ship |
| Name | Documentation reference | Used by | Notes |
|----------------|---------------------------------------------------------------------------|------------------------------------------------|----------------------------------------|
| Valve Protocol | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | TF2, CSGO, TS, CSS, DODS, GM, HL2DM, L4D, L4D2 | Multi-packet decompression not tested. |
## Planned to add support:
Minecraft protocol

View file

@ -17,7 +17,9 @@ pub enum GDError {
PacketSend(String),
/// Couldn't send the receive.
PacketReceive(String),
/// Unknown cast while translating a value to an enum
/// Couldn't decompress data.
Decompress(String),
/// Unknown cast while translating a value to an enum.
UnknownEnumCast,
/// The server queried is not from the queried game.
BadGame(String)
@ -31,6 +33,7 @@ impl fmt::Display for GDError {
GDError::PacketBad(details) => write!(f, "Packet bad: {details}"),
GDError::PacketSend(details) => write!(f, "Couldn't send a packet: {details}"),
GDError::PacketReceive(details) => write!(f, "Couldn't receive a packet: {details}"),
GDError::Decompress(details) => write!(f, "Couldn't decompress data: {details}"),
GDError::UnknownEnumCast => write!(f, "Unknown enum cast encountered."),
GDError::BadGame(details) => write!(f, "Queried another game that the supposed one: {details}"),
}

View file

@ -1,4 +1,5 @@
use std::net::UdpSocket;
use bzip2_rs::decoder::Decoder;
use crate::{GDError, GDResult};
use crate::utils::{buffer, complete_address};
@ -233,16 +234,19 @@ impl Packet {
#[derive(Debug)]
#[allow(dead_code)] //remove this later on
struct SplitPacketInfo {
struct SplitPacket {
pub header: u32,
pub id: u32,
pub total: u8,
pub number: u8,
pub size: u16,
pub payload: Vec<u8>
pub compressed: bool,
pub decompressed_size: Option<u32>,
pub uncompressed_crc32: Option<u32>,
payload: Vec<u8>
}
impl SplitPacketInfo {
impl SplitPacket {
fn new(_app: &App, buf: &[u8]) -> GDResult<Self> {
let mut pos = 0;
@ -251,16 +255,10 @@ impl SplitPacketInfo {
let total = buffer::get_u8(&buf, &mut pos)?;
let number = buffer::get_u8(&buf, &mut pos)?;
let size = buffer::get_u16_le(&buf, &mut pos)?;
let payload = match ((id >> 31) & 1) == 1 {
false => buf[pos..].to_vec(),
true => {
let _decompressed_size = buffer::get_u32_le(&buf, &mut pos)?;
let _uncompressed_crc32 = buffer::get_u32_le(&buf, &mut pos)?;
//decompress...
vec![]
}
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)?))
};
Ok(Self {
@ -269,9 +267,44 @@ impl SplitPacketInfo {
total,
number,
size,
payload
compressed,
decompressed_size,
uncompressed_crc32,
payload: buf[pos..].to_vec()
})
}
fn decompress(&self) -> GDResult<Vec<u8>> {
if !self.compressed {
let mut decoder = Decoder::new();
decoder.write(&self.payload).map_err(|e| GDError::Decompress(e.to_string()))?;
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()))?;
if decompressed_payload.len() != decompressed_size {
Err(GDError::Decompress("Valve Protocol: The decompressed payload size doesn't match the expected one.".to_string()))
}
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()))
}
else {
Ok(decompressed_payload)
}
} else { //already decompressed
Ok(self.payload.clone())
}
}
fn get_payload(&self) -> GDResult<Vec<u8>> {
if self.compressed {
Ok(self.decompress()?)
} else {
Ok(self.payload.clone())
}
}
}
impl ValveProtocol {
@ -302,16 +335,15 @@ impl ValveProtocol {
let mut buf = self.receive_raw(buffer_size)?;
if buf[0] == 0xFE { //the packet is split
let initial_split_packet_info = SplitPacketInfo::new(app, &buf)?;
let mut final_packet = Packet::new(&initial_split_packet_info.payload)?;
let mut main_packet = SplitPacket::new(app, &buf)?;
for _ in 1..initial_split_packet_info.total {
for _ in 1..main_packet.total {
buf = self.receive_raw(buffer_size)?;
let split_packet_info = SplitPacketInfo::new(app, &buf)?;
final_packet.payload.extend(split_packet_info.payload);
let chunk_packet = SplitPacket::new(app, &buf)?;
main_packet.payload.extend(chunk_packet.payload);
}
Ok(final_packet)
Ok(Packet::new(&main_packet.get_payload()?)?)
}
else {
Packet::new(&buf)