try to fix everything

This commit is contained in:
nora 2022-02-13 23:11:20 +01:00
parent ac2acbae1f
commit 5dc33f0dab
10 changed files with 1576 additions and 1525 deletions

File diff suppressed because it is too large Load diff

View file

@ -41,9 +41,15 @@ pub fn parse_method(payload: &[u8]) -> Result<generated::Class, TransError> {
match nom_result {
Ok(([], class)) => Ok(class),
Ok((_, _)) => Err(ProtocolError::ConException(ConException::SyntaxError).into()),
Ok((_, _)) => Err(ProtocolError::ConException(ConException::SyntaxError(vec![
"could not consume all input".to_string(),
]))
.into()),
Err(nom::Err::Incomplete(_)) => {
Err(ProtocolError::ConException(ConException::SyntaxError).into())
Err(ProtocolError::ConException(ConException::SyntaxError(vec![
"there was not enough data".to_string(),
]))
.into())
}
Err(nom::Err::Failure(err) | nom::Err::Error(err)) => Err(err),
}

View file

@ -9,11 +9,15 @@ use nom::error::ErrorKind;
use nom::multi::count;
use nom::number::complete::{f32, f64, i16, i32, i64, i8, u16, u32, u64, u8};
use nom::number::Endianness::Big;
use nom::Err;
use std::collections::HashMap;
// todo: remove the debug machinery or change it in a way that actually does what it should lmao
// I'm probably misusing nom hard
impl<T> nom::error::ParseError<T> for TransError {
fn from_error_kind(_input: T, _kind: ErrorKind) -> Self {
ProtocolError::ConException(ConException::SyntaxError).into()
ProtocolError::ConException(ConException::SyntaxError(vec![])).into()
}
fn append(_input: T, _kind: ErrorKind, other: Self) -> Self {
@ -21,12 +25,52 @@ impl<T> nom::error::ParseError<T> for TransError {
}
}
pub fn err<S: Into<String>>(msg: S) -> impl FnOnce(Err<TransError>) -> Err<TransError> {
move |err| {
let error_level = if matches!(err, nom::Err::Failure(_)) {
Err::Failure
} else {
Err::Error
};
let msg = msg.into();
let stack = match err {
Err::Error(e) | Err::Failure(e) => match e {
TransError::Invalid(ProtocolError::ConException(ConException::SyntaxError(
mut stack,
))) => {
stack.push(msg);
stack
}
_ => vec![msg],
},
_ => vec![msg],
};
error_level(ProtocolError::ConException(ConException::SyntaxError(stack)).into())
}
}
pub fn err_other<E, S: Into<String>>(msg: S) -> impl FnOnce(E) -> Err<TransError> {
move |_| {
Err::Error(ProtocolError::ConException(ConException::SyntaxError(vec![msg.into()])).into())
}
}
pub fn failure<E>(err: Err<E>) -> Err<E> {
match err {
Err::Incomplete(needed) => Err::Incomplete(needed),
Err::Error(e) => Err::Failure(e),
Err::Failure(e) => Err::Failure(e),
}
}
#[macro_export]
macro_rules! fail {
() => {
($cause:expr) => {
return Err(nom::Err::Failure(
crate::error::ProtocolError::ConException(crate::error::ConException::SyntaxError)
.into(),
crate::error::ProtocolError::ConException(crate::error::ConException::SyntaxError(
vec![String::from($cause)],
))
.into(),
))
};
}
@ -79,9 +123,7 @@ pub fn bit(input: &[u8], amount: usize) -> IResult<Vec<Bit>> {
pub fn shortstr(input: &[u8]) -> IResult<Shortstr> {
let (input, len) = u8(input)?;
let (input, str_data) = take(usize::from(len))(input)?;
let data = String::from_utf8(str_data.into()).map_err(|_| {
nom::Err::Failure(ProtocolError::ConException(ConException::SyntaxError).into())
})?;
let data = String::from_utf8(str_data.into()).map_err(err_other("shortstr"))?;
Ok((input, data))
}
@ -106,7 +148,7 @@ pub fn table(input: &[u8]) -> IResult<Table> {
fn table_value_pair(input: &[u8]) -> IResult<(TableFieldName, FieldValue)> {
let (input, field_name) = shortstr(input)?;
let (input, field_value) = field_value(input)?;
let (input, field_value) = field_value(input).map_err(err(format!("field {field_name}")))?;
Ok((input, (field_name, field_value)))
}
@ -119,7 +161,7 @@ fn field_value(input: &[u8]) -> IResult<FieldValue> {
match bool_byte {
0 => Ok((input, FieldValue::Boolean(false))),
1 => Ok((input, FieldValue::Boolean(true))),
_ => fail!(),
value => fail!(format!("invalid bool value {value}")),
}
}

View file

@ -11,8 +11,9 @@ pub(crate) trait RandomMethod<R: Rng> {
}
impl<R: Rng> RandomMethod<R> for String {
fn random(_rng: &mut R) -> Self {
"randomstring".to_string()
fn random(rng: &mut R) -> Self {
let n = rng.gen_range(0_u16..9999);
format!("string{n}")
}
}
@ -47,7 +48,7 @@ impl<R: Rng> RandomMethod<R> for HashMap<String, FieldValue> {
impl<R: Rng> RandomMethod<R> for FieldValue {
fn random(rng: &mut R) -> Self {
let index = rand::thread_rng().gen_range(0_u32..17);
let index = rng.gen_range(0_u32..17);
match index {
0 => FieldValue::Boolean(RandomMethod::random(rng)),
1 => FieldValue::ShortShortInt(RandomMethod::random(rng)),
@ -73,7 +74,6 @@ impl<R: Rng> RandomMethod<R> for FieldValue {
#[test]
fn pack_few_bits() {
rand::thread_rng().gen_range(0..5);
let bits = [true, false, true];
let mut buffer = [0u8; 2];
@ -97,18 +97,20 @@ fn pack_many_bits() {
assert_eq!(bits.as_slice(), parsed_bits.as_slice());
}
#[ignore]
#[test]
fn random_ser_de() {
const ITERATIONS: usize = 1000;
let mut rng = rand::rngs::StdRng::from_seed([0; 32]);
for _ in 0..ITERATIONS {
println!("iter");
let class = Class::random(&mut rng);
let mut bytes = Vec::new();
if let Err(err) = super::write::write_method(class.clone(), &mut bytes) {
eprintln!("{class:?}");
eprintln!("{err}");
eprintln!("{class:#?}");
eprintln!("{err:?}");
panic!("Failed to serialize");
}
@ -117,11 +119,31 @@ fn random_ser_de() {
assert_eq!(class, parsed);
}
Err(err) => {
eprintln!("{class:?}");
eprintln!("{class:#?}");
eprintln!("{bytes:?}");
eprintln!("{err}");
eprintln!("{err:?}");
panic!("Failed to deserialize");
}
}
}
}
#[test]
fn nested_table() {
let table = HashMap::from([(
"A".to_string(),
FieldValue::FieldTable(HashMap::from([(
"B".to_string(),
FieldValue::Boolean(true),
)])),
)]);
let mut bytes = Vec::new();
crate::classes::write_helper::table(table.clone(), &mut bytes).unwrap();
eprintln!("{bytes:?}");
let (rest, parsed_table) = crate::classes::parse_helper::table(&bytes).unwrap();
assert!(rest.is_empty());
assert_eq!(table, parsed_table);
}

View file

@ -154,8 +154,9 @@ fn field_value<W: Write>(value: FieldValue, writer: &mut W) -> Result<(), TransE
writer.write_all(b"T")?;
writer.write_all(&time.to_be_bytes())?;
}
FieldValue::FieldTable(_) => {
FieldValue::FieldTable(value) => {
writer.write_all(b"F")?;
table(value, writer)?;
}
FieldValue::Void => {
writer.write_all(b"V")?;

View file

@ -125,7 +125,7 @@ fn server_properties(host: SocketAddr) -> classes::Table {
}
let host_str = host.ip().to_string();
let host_value = if host_str.len() < 256 {
let _host_value = if host_str.len() < 256 {
FieldValue::ShortString(host_str)
} else {
FieldValue::LongString(host_str.into())

View file

@ -35,7 +35,8 @@ pub enum ConException {
#[error("503 Command invalid")]
CommandInvalid,
#[error("503 Syntax error")]
SyntaxError,
/// A method was received but there was a syntax error. The string stores where it occured.
SyntaxError(Vec<String>),
#[error("504 Channel error")]
ChannelError,
#[error("xxx Not decided yet")]

View file

@ -1,4 +1,4 @@
use crate::error::{ConException, ProtocolError, Result, TransError};
use crate::error::{ConException, ProtocolError, Result};
use anyhow::Context;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tracing::debug;