Skip to content

Commit

Permalink
Add multiline strings for packets, small fixes, add better packet err…
Browse files Browse the repository at this point in the history
…or strings, small tools to make parsing tests easier
  • Loading branch information
jon-zu committed Mar 7, 2024
1 parent ab8c96f commit e587d3e
Show file tree
Hide file tree
Showing 12 changed files with 396 additions and 72 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ members = [
"crates/shroom-net",
"crates/shroom-pkt",
"crates/shroom-pkt-derive",
"crates/shroom-srv",
"crates/shroom-crypto", "crates/shroom-wz",
"crates/shroom-crypto",
"crates/shroom-wz",
]
2 changes: 1 addition & 1 deletion crates/shroom-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ thiserror = "1"
rand = "0.8"
futures = "0.3"
num_enum = "0.7"
tokio = { version = "1", features = ["rt", "macros", "io-util"] }
tokio = { version = "1", features = ["rt", "macros", "io-util", "net"] }
tokio-util = { version = "0.7", features = ["codec"] }
shroom-pkt = { version = "0.1", path = "../shroom-pkt" }
shroom-crypto = { version = "0.1.0", path = "../shroom-crypto" }
6 changes: 6 additions & 0 deletions crates/shroom-pkt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ name = "shroom-pkt"
version = "0.1.1"
edition = "2021"

[features]
default = ["eof_ext"]
# provides extra data, for packet parsing errors
eof_ext = []

[[test]]
name = "tests"
path = "tests/progress.rs"
Expand Down Expand Up @@ -30,4 +35,5 @@ pretty-hex = "0.4.0"
thiserror = "1.0.48"
shroom-pkt-derive = { path = "../shroom-pkt-derive" }
nt-time = { version = "0.6.5", features = ["chrono", "std"] }
hexlit = "0.5.5"

139 changes: 117 additions & 22 deletions crates/shroom-pkt/src/analyzer.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,135 @@
use std::fmt::Display;
use std::{fmt::Display, ops::Range};

use bytes::Bytes;
use pretty_hex::PrettyHex;
use crate::error::EOFErrorData;

/// Data analytics to get some more info about an error during reading a packet
#[derive(Debug)]
pub struct PacketDataAnalytics {
data: Bytes,
pos: usize,
read_len: usize,
context: usize,
pub struct PacketAnalyzer<'a> {
eof: &'a EOFErrorData,
data: &'a [u8],
}

impl PacketDataAnalytics {
/// Create analytics data by copying the byte slice
pub fn from_data(data: &[u8], pos: usize, read_len: usize, context: usize) -> Self {
Self {
data: Bytes::from(data.to_vec()),
pos,
read_len,
context,
pub struct HexString<'a, const SPACE: bool>(&'a [u8]);

impl<'a, const SPACE: bool> std::fmt::Display for HexString<'a, SPACE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut first = true;
for byte in self.0 {
if !first && SPACE {
write!(f, " ")?;
}
write!(f, "{:02x}", byte)?;
first = false;
}
Ok(())
}
}

impl<'a, const SPACE: bool> HexString<'a, SPACE> {
const fn size_per_byte() -> usize {
if SPACE {
3
} else {
2
}
}

pub fn map_index(&self, ix: usize) -> usize {
ix * Self::size_per_byte()
}

pub fn map_range(&self, range: Range<usize>) -> Range<usize> {
let start = range.start;
let l = self.map_index(start);
let end = self.map_index(start + range.count()).saturating_sub(1);
l..end
}

pub fn str_len(&self) -> usize {
self.map_index(self.0.len())
}
}

impl<'a> PacketAnalyzer<'a> {
pub fn new(eof: &'a EOFErrorData, data: &'a [u8]) -> Self {
Self { eof, data }
}
/// Get the relevant data with the surrounding context bytes
pub fn get_relevant_data(&self) -> &[u8] {
let left = self.pos.saturating_sub(self.context);
let right = (self.pos + self.read_len + self.context).min(self.data.len());
pub fn get_relevant_data(&'a self) -> &'a [u8] {
let ctx = self.eof.read_len() * 2;
let left = self.eof.pos.saturating_sub(ctx);
let right = (self.eof.pos + ctx).min(self.data.len());

&self.data[left..right]
}

/// Get the eof marker range
pub fn eof_range(&self) -> Range<usize> {
let p = self.eof.pos;
let right = (p + self.eof.read_len()).min(self.data.len());
p..right
}

/// Write the hex string
pub fn hex_string(&self) -> HexString<'a, true> {
HexString(self.data)
}

/// Write the hex string
pub fn relevant_data_hex_string(&'a self) -> HexString<'a, true> {
HexString(self.get_relevant_data())
}

/// Eof hex string
pub fn eof_hex_string(&self) -> HexString<'a, true> {
HexString(&self.data[self.eof_range()])
}

/// Marker
pub fn eof_marker(&self, mut f: impl std::fmt::Write) -> std::fmt::Result {
let eof_range = self.hex_string().map_range(self.eof_range());
for _ in 0..eof_range.start {
write!(f, " ")?;
}
for _ in eof_range {
write!(f, "^")?;
}
writeln!(
f,
" (read_len={}, type={})",
self.eof.read_len(),
self.eof.type_name()
)?;
Ok(())
}
}

impl Display for PacketDataAnalytics {
impl<'a> Display for PacketAnalyzer<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let hex = self.get_relevant_data().hex_dump();
write!(f, "{hex}")
// Write the hex string
let hx = self.hex_string();
writeln!(f, "{}", hx)?;
// Write the eof marker and type
if cfg!(feature = "eof_ext") {
self.eof_marker(f)?;
}

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
#[cfg(feature = "eof_ext")]
fn eof() {
let data = [1, 2, 3, 4];
let eof = EOFErrorData::from_type::<u8>(2, 1);
let a = PacketAnalyzer::new(&eof, &data);
let txt = a.to_string();
assert_eq!(a.eof_range(), 2..3);
assert_eq!(txt, "01 02 03 04\n ^^ (read_len=1, type=u8)\n");
}
}
96 changes: 78 additions & 18 deletions crates/shroom-pkt/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,91 @@ use std::{fmt::Display, str::Utf8Error};

use nt_time::error::FileTimeRangeError;
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};

use crate::analyzer::PacketDataAnalytics;
use thiserror::Error;

use crate::analyzer::PacketAnalyzer;

#[derive(Debug)]
#[cfg(feature = "eof_ext")]
pub struct EOFExtraData {
type_name: &'static str,
read_len: usize,
}

#[derive(Debug)]
#[cfg(not(feature = "eof_ext"))]
pub struct EOFExtraData;

impl EOFExtraData {
#[cfg(feature = "eof_ext")]
pub fn from_type<T>(read_len: usize) -> Self {
let type_name = std::any::type_name::<T>();

EOFExtraData {
type_name,
read_len,
}
}

#[cfg(not(feature = "eof_ext"))]
pub fn from_type<T>(_read_len: usize) -> Self {
EOFExtraData
}
}

#[derive(Debug)]
pub struct EOFErrorData {
pub analytics: PacketDataAnalytics,
pub type_name: &'static str,
pub pos: usize,
#[cfg(feature = "eof_ext")]
pub extra: Box<EOFExtraData>,
}

impl EOFErrorData {
pub fn from_type<T>(pos: usize, read_len: usize) -> Self {
let extra = EOFExtraData::from_type::<T>(read_len);
EOFErrorData {
pos,
extra: Box::new(extra),
}
}

pub fn analytics<'a>(&'a self, data: &'a [u8]) -> PacketAnalyzer<'a> {
PacketAnalyzer::new(self, data)
}

#[cfg(not(feature = "eof_ext"))]
pub fn read_len(&self) -> usize {
// Holy number for context
4
}

#[cfg(feature = "eof_ext")]
pub fn read_len(&self) -> usize {
self.extra.read_len
}

#[cfg(not(feature = "eof_ext"))]
pub fn type_name(&self) -> &str {
"unknown"
}

#[cfg(feature = "eof_ext")]
pub fn type_name(&self) -> &str {
self.extra.type_name
}
}

impl Display for EOFErrorData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "eof packet(type={}): {}", self.type_name, self.analytics)
if cfg!(feature = "eof_ext") {
write!(
f,
"eof packet(type={}): {}",
self.extra.type_name, self.extra.read_len
)
} else {
write!(f, "eof packet: {}", self.pos)
}
}
}

Expand All @@ -23,7 +95,7 @@ pub enum Error {
#[error("string utf8 error")]
StringUtf8(#[from] Utf8Error),
#[error("EOF error: {0}")]
EOF(Box<EOFErrorData>),
EOF(EOFErrorData),
#[error("String limit {0} exceeed")]
StringLimit(usize),
#[error("Invalid enum discriminant {0}")]
Expand All @@ -44,18 +116,6 @@ pub enum Error {
InvalidAllBits,
}

impl Error {
//TODO disable diagnostic for release builds
pub fn eof<T>(data: &[u8], read_len: usize) -> Self {
let type_name = std::any::type_name::<T>();
let pos = data.len().saturating_sub(read_len);
Self::EOF(Box::new(EOFErrorData {
analytics: PacketDataAnalytics::from_data(data, pos, read_len, read_len * 5),
type_name,
}))
}
}

impl<E> From<TryFromPrimitiveError<E>> for Error
where
E: TryFromPrimitive,
Expand Down
22 changes: 22 additions & 0 deletions crates/shroom-pkt/src/proto/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,28 @@ impl<D: EncodePacket> EncodePacket for Option<D> {
}
}


pub struct ShroomMultiLineStr<'a>(pub &'a str);
impl<'a> EncodePacket for ShroomMultiLineStr<'a> {
const SIZE_HINT: SizeHint = SizeHint::NONE;

fn encode<B: BufMut>(&self, pw: &mut PacketWriter<B>) -> PacketResult<()> {
pw.write_multi_line_str(self.0)?;
Ok(())
}

fn encode_len(&self) -> usize {
//TODO figure this out
todo!("ShroomMultiLineStr::encode_len");
}
}

impl<'a> DecodePacket<'a> for ShroomMultiLineStr<'a> {
fn decode(pr: &mut PacketReader<'a>) -> PacketResult<Self> {
Ok(Self(pr.read_string()?))
}
}

#[cfg(test)]
mod tests {
use crate::test_util::test_enc_dec_all;
Expand Down
Loading

0 comments on commit e587d3e

Please sign in to comment.