use std::borrow::Borrow;
use std::convert::TryInto;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use windows::core::GUID;
use super::etw_types::EVENT_HEADER_FLAG_32_BIT_HEADER;
use super::property::{PropertyInfo, PropertyIter};
use super::schema::TypedEvent;
use super::tdh_types::{Property, PropertyDesc, PropertyLength, TdhInType, TdhOutType};
use super::{tdh, utils};
#[derive(Debug, Clone, Copy)]
pub enum Address {
Address64(u64),
Address32(u32),
}
impl Address {
pub fn as_u64(&self) -> u64 {
match self {
Address::Address64(a) => *a,
Address::Address32(a) => *a as u64,
}
}
}
#[derive(Debug)]
pub enum ParserError {
InvalidType,
ParseError,
LengthMismatch,
PropertyError(String),
Utf8Error(std::string::FromUtf8Error),
SliceError(std::array::TryFromSliceError),
TdhNativeError(tdh::TdhNativeError),
}
impl From<tdh::TdhNativeError> for ParserError {
fn from(err: tdh::TdhNativeError) -> Self {
ParserError::TdhNativeError(err)
}
}
impl From<std::string::FromUtf8Error> for ParserError {
fn from(err: std::string::FromUtf8Error) -> Self {
ParserError::Utf8Error(err)
}
}
impl From<std::array::TryFromSliceError> for ParserError {
fn from(err: std::array::TryFromSliceError) -> Self {
ParserError::SliceError(err)
}
}
type ParserResult<T> = Result<T, ParserError>;
pub trait TryParse<T> {
fn try_parse(&mut self, name: &str) -> Result<T, ParserError>;
fn parse(&mut self, name: &str) -> T {
self.try_parse(name)
.unwrap_or_else(|e| panic!("{:?} name {} {:?}", e, std::any::type_name::<T>(), name))
}
}
#[allow(dead_code)]
pub struct Parser<'a> {
event: &'a TypedEvent<'a>,
properties: &'a PropertyIter,
pub buffer: &'a [u8],
last_property: u32,
offset: usize,
cache: Vec<PropertyInfo<'a>>,
}
impl<'a> Parser<'a> {
pub fn create(event: &'a TypedEvent) -> Self {
Parser {
event,
buffer: event.user_buffer(),
properties: event.schema.properties(),
last_property: 0,
offset: 0,
cache: Vec::new(), }
}
fn find_property_size(&self, property: &Property) -> ParserResult<usize> {
match property.length {
PropertyLength::Index(_) => {
Ok(tdh::property_size(self.event.record(), &property.name).unwrap() as usize)
}
PropertyLength::Length(length) => {
if property.flags.is_empty() && length > 0 && property.count == 1 {
return Ok(length as usize);
}
if property.count == 1 {
if let PropertyDesc::Primitive(desc) = &property.desc {
match desc.in_type {
TdhInType::InTypeBoolean => return Ok(4),
TdhInType::InTypeInt32
| TdhInType::InTypeUInt32
| TdhInType::InTypeHexInt32 => return Ok(4),
TdhInType::InTypeInt64
| TdhInType::InTypeUInt64
| TdhInType::InTypeHexInt64 => return Ok(8),
TdhInType::InTypeInt8 | TdhInType::InTypeUInt8 => return Ok(1),
TdhInType::InTypeInt16 | TdhInType::InTypeUInt16 => return Ok(2),
TdhInType::InTypePointer => {
return Ok(
if (self.event.event_flags() & EVENT_HEADER_FLAG_32_BIT_HEADER)
!= 0
{
4
} else {
8
},
)
}
TdhInType::InTypeGuid => return Ok(std::mem::size_of::<GUID>()),
TdhInType::InTypeUnicodeString => {
return Ok(utils::parse_unk_size_null_unicode_size(self.buffer))
}
TdhInType::InTypeAnsiString => {
return Ok(utils::parse_unk_size_null_ansi_size(self.buffer));
}
_ => {}
}
}
}
Ok(tdh::property_size(self.event.record(), &property.name).unwrap() as usize)
}
}
}
pub fn find_property(&mut self, name: &str) -> ParserResult<usize> {
let indx = *self
.properties
.name_to_indx
.get(name)
.ok_or_else(|| ParserError::PropertyError(format!("Unknown property: {}", name)))?;
if indx < self.cache.len() {
return Ok(indx);
}
for i in self.cache.len()..=indx {
let curr_prop = self.properties.property(i).unwrap();
let prop_size = self.find_property_size(curr_prop)?;
if self.buffer.len() < prop_size {
return Err(ParserError::PropertyError(format!(
"Property of {} bytes out of buffer bounds ({})",
prop_size,
self.buffer.len()
)));
}
let (prop_buffer, remaining) = self.buffer.split_at(prop_size);
self.buffer = remaining;
self.cache
.push(PropertyInfo::create(curr_prop, self.offset, prop_buffer));
self.offset += prop_size;
}
Ok(indx)
}
}
macro_rules! impl_try_parse_primitive {
($T:ident, $ty:ident) => {
impl TryParse<$T> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<$T> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
let prop_info: &PropertyInfo = prop_info.borrow();
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.in_type != $ty {
return Err(ParserError::InvalidType);
}
if std::mem::size_of::<$T>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok($T::from_ne_bytes(prop_info.buffer.try_into()?));
};
Err(ParserError::InvalidType)
}
}
};
}
impl_try_parse_primitive!(u8, InTypeUInt8);
impl_try_parse_primitive!(i8, InTypeInt8);
impl_try_parse_primitive!(u16, InTypeUInt16);
impl_try_parse_primitive!(i16, InTypeInt16);
impl_try_parse_primitive!(u32, InTypeUInt32);
impl TryParse<u64> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<u64> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.in_type == InTypeUInt64 {
if std::mem::size_of::<u64>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(u64::from_ne_bytes(prop_info.buffer.try_into()?));
}
if desc.in_type == InTypePointer || desc.in_type == InTypeSizeT {
if (self.event.event_flags() & EVENT_HEADER_FLAG_32_BIT_HEADER) != 0 {
if std::mem::size_of::<u32>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(u32::from_ne_bytes(prop_info.buffer.try_into()?) as u64);
}
if std::mem::size_of::<u64>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(u64::from_ne_bytes(prop_info.buffer.try_into()?));
}
}
Err(ParserError::InvalidType)
}
}
impl TryParse<i64> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<i64> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.in_type == InTypeInt64 || desc.in_type == InTypeHexInt64 {
if std::mem::size_of::<i64>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(i64::from_ne_bytes(prop_info.buffer.try_into()?));
}
}
Err(ParserError::InvalidType)
}
}
impl TryParse<i32> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<i32> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.in_type == InTypeInt32 || desc.in_type == InTypeHexInt32 {
if std::mem::size_of::<i32>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(i32::from_ne_bytes(prop_info.buffer.try_into()?));
}
}
Err(ParserError::InvalidType)
}
}
impl TryParse<Address> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<Address> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if self.event.is_64bit() {
if desc.in_type == InTypeUInt64
|| desc.in_type == InTypePointer
|| desc.in_type == InTypeHexInt64
{
if std::mem::size_of::<u64>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(Address::Address64(u64::from_ne_bytes(
prop_info.buffer.try_into()?,
)));
}
} else if desc.in_type == InTypeUInt32
|| desc.in_type == InTypePointer
|| desc.in_type == InTypeHexInt32
{
if std::mem::size_of::<u32>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(Address::Address32(u32::from_ne_bytes(
prop_info.buffer.try_into()?,
)));
}
}
Err(ParserError::InvalidType)
}
}
impl TryParse<bool> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<bool> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.in_type != InTypeBoolean {
return Err(ParserError::InvalidType);
}
if prop_info.buffer.len() != 4 {
return Err(ParserError::LengthMismatch);
}
return match u32::from_ne_bytes(prop_info.buffer.try_into()?) {
1 => Ok(true),
0 => Ok(false),
_ => Err(ParserError::InvalidType),
};
};
Err(ParserError::InvalidType)
}
}
impl TryParse<f32> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<f32> {
use TdhInType::*;
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.in_type == InTypeFloat {
if std::mem::size_of::<f32>() != prop_info.buffer.len() {
return Err(ParserError::LengthMismatch);
}
return Ok(f32::from_ne_bytes(prop_info.buffer.try_into()?));
}
}
Err(ParserError::InvalidType)
}
}
impl TryParse<String> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<String> {
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
let res = match desc.in_type {
TdhInType::InTypeUnicodeString => utils::parse_null_utf16_string(prop_info.buffer),
TdhInType::InTypeAnsiString => String::from_utf8(prop_info.buffer.to_vec())?
.trim_matches(char::default())
.to_string(),
TdhInType::InTypeSid => {
panic!()
}
TdhInType::InTypeCountedString => unimplemented!(),
_ => return Err(ParserError::InvalidType),
};
return Ok(res);
}
Err(ParserError::InvalidType)
}
}
impl TryParse<GUID> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> Result<GUID, ParserError> {
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
match desc.in_type {
TdhInType::InTypeUnicodeString => {
let guid_string = utils::parse_utf16_guid(prop_info.buffer);
if guid_string.len() != 36 {
return Err(ParserError::LengthMismatch);
}
return GUID::try_from(guid_string.as_str()).map_err(|_| {
ParserError::PropertyError(format!("Error parsing GUID {guid_string}"))
});
}
TdhInType::InTypeGuid => {
return Ok(GUID::from_values(
u32::from_ne_bytes((&prop_info.buffer[0..4]).try_into()?),
u16::from_ne_bytes((&prop_info.buffer[4..6]).try_into()?),
u16::from_ne_bytes((&prop_info.buffer[6..8]).try_into()?),
[
prop_info.buffer[8],
prop_info.buffer[9],
prop_info.buffer[10],
prop_info.buffer[11],
prop_info.buffer[12],
prop_info.buffer[13],
prop_info.buffer[14],
prop_info.buffer[15],
],
))
}
_ => return Err(ParserError::InvalidType),
}
};
Err(ParserError::InvalidType)
}
}
impl TryParse<IpAddr> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<IpAddr> {
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
if let PropertyDesc::Primitive(desc) = &prop_info.property.desc {
if desc.out_type != TdhOutType::OutTypeIpv4 && desc.out_type != TdhOutType::OutTypeIpv6
{
return Err(ParserError::InvalidType);
}
let res = match prop_info.property.length {
PropertyLength::Length(16) => {
let tmp: [u8; 16] = prop_info.buffer.try_into()?;
IpAddr::V6(Ipv6Addr::from(tmp))
}
PropertyLength::Length(4) => {
let tmp: [u8; 4] = prop_info.buffer.try_into()?;
IpAddr::V4(Ipv4Addr::from(tmp))
}
_ => return Err(ParserError::LengthMismatch),
};
return Ok(res);
}
Err(ParserError::InvalidType)
}
}
#[derive(Clone, Default, Debug)]
pub struct Pointer(usize);
impl std::ops::Deref for Pointer {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for Pointer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl std::fmt::LowerHex for Pointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let val = self.0;
std::fmt::LowerHex::fmt(&val, f) }
}
impl std::fmt::UpperHex for Pointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let val = self.0;
std::fmt::UpperHex::fmt(&val, f) }
}
impl std::fmt::Display for Pointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let val = self.0;
std::fmt::Display::fmt(&val, f) }
}
impl TryParse<Pointer> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> ParserResult<Pointer> {
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
let mut res = Pointer::default();
if prop_info.buffer.len() == std::mem::size_of::<u32>() {
res.0 = TryParse::<u32>::try_parse(self, name)? as usize;
} else {
res.0 = TryParse::<u64>::try_parse(self, name)? as usize;
}
Ok(res)
}
}
impl TryParse<Vec<u8>> for Parser<'_> {
fn try_parse(&mut self, name: &str) -> Result<Vec<u8>, ParserError> {
let indx = self.find_property(name)?;
let prop_info = &self.cache[indx];
Ok(prop_info.buffer.to_vec())
}
}