Skip to content

Commit

Permalink
Turbofish operator can be used for try_parse
Browse files Browse the repository at this point in the history
That removes the following TODO:
// TODO: Find a way to use turbofish operator
  • Loading branch information
daladim committed Nov 14, 2022
1 parent 56fb61e commit 477cbfb
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 57 deletions.
1 change: 0 additions & 1 deletion examples/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use ferrisetw::parser::Parser;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::native::etw_types::EventRecord;
use ferrisetw::trace::UserTrace;
use ferrisetw::parser::TryParse;
use ferrisetw::schema::Schema;


Expand Down
4 changes: 2 additions & 2 deletions examples/kernel_trace.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ferrisetw::native::etw_types::EventRecord;
use ferrisetw::parser::{Parser, TryParse};
use ferrisetw::parser::Parser;
use ferrisetw::provider::*;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::trace::*;
Expand All @@ -17,7 +17,7 @@ fn main() {
println!("ProviderName: {}", name);
let parser = Parser::create(record, &schema);
// Fully Qualified Syntax for Disambiguation
match TryParse::<String>::try_parse(&parser, "FileName") {
match parser.try_parse::<String>("FileName") {
Ok(filename) => println!("FileName: {}", filename),
Err(err) => println!("Error: {:?} getting Filename", err),
};
Expand Down
2 changes: 1 addition & 1 deletion examples/multiple_providers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ferrisetw::native::etw_types::EventRecord;
use ferrisetw::parser::{Parser, Pointer, TryParse};
use ferrisetw::parser::{Parser, Pointer};
use ferrisetw::provider::*;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::trace::*;
Expand Down
2 changes: 1 addition & 1 deletion examples/user_trace.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ferrisetw::native::etw_types::EventRecord;
use ferrisetw::parser::{Parser, TryParse};
use ferrisetw::parser::Parser;
use ferrisetw::provider::*;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::trace::*;
Expand Down
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
//! use ferrisetw::native::etw_types::EventRecord;
//! use ferrisetw::schema_locator::SchemaLocator;
//! use ferrisetw::parser::Parser;
//! use ferrisetw::parser::TryParse;
//! use ferrisetw::provider::Provider;
//! use ferrisetw::trace::{UserTrace, TraceTrait};
//!
Expand All @@ -57,8 +56,7 @@
//! // Finally, properties for a given event can be retrieved using a Parser
//! let parser = Parser::create(record, &schema);
//!
//! // Type annotations or Fully Qualified Syntax are needed when calling TryParse
//! // Supported types implement the trait TryParse for Parser
//! // You'll need type inference to tell ferrisetw what type you want to parse into
//! // In actual code, be sure to correctly handle Err values!
//! let process_id: u32 = parser.try_parse("ProcessID").unwrap();
//! let image_name: String = parser.try_parse("ImageName").unwrap();
Expand Down
3 changes: 1 addition & 2 deletions src/native/tdh_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
//! event
//!
//! This is a bit extra but is basically a redefinition of the In an Out TDH types following the
//! rust naming convention, it can also come in handy when implementing the [TryParse] trait for a type
//! rust naming convention, it can also come in handy when implementing the `TryParse` trait for a type
//! to determine how to handle a [Property] based on this values
//!
//! [TryParse]: crate::parser::TryParse
//! [Property]: crate::native::tdh_types::Property
use num_traits::FromPrimitive;

Expand Down
113 changes: 71 additions & 42 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! ETW Types Parser
//!
//! This module act as a helper to parse the Buffer from an ETW Event

use crate::native::etw_types::EVENT_HEADER_FLAG_32_BIT_HEADER;
use crate::native::etw_types::EventRecord;
use crate::native::sddl;
Expand Down Expand Up @@ -68,22 +70,6 @@ impl From<std::array::TryFromSliceError> for ParserError {

type ParserResult<T> = Result<T, ParserError>;

/// Trait to try and parse a type
///
/// This trait has to be implemented in order to be able to parse a type we want to retrieve from
/// within an Event.
///
/// An implementation for most of the Primitive Types is created by using a Macro, any other needed type
/// requires this trait to be implemented
// TODO: Find a way to use turbofish operator
pub trait TryParse<T> {
/// Implement the `try_parse` function to provide a way to Parse `T` from an ETW event or
/// return an Error in case the type `T` can't be parsed
///
/// # Arguments
/// * `name` - Name of the property to be found in the Schema
fn try_parse(&self, name: &str) -> Result<T, ParserError>;
}

#[derive(Default)]
/// Cache of the properties we've extracted already
Expand All @@ -99,6 +85,27 @@ struct CachedSlices<'schema, 'record> {
///
/// This structure provides a way to parse an ETW event (= extract its properties).
/// Because properties may have variable length (e.g. strings), a `Parser` is only suited to a single [`EventRecord`]
///
/// # Example
/// ```
/// # use ferrisetw::native::etw_types::EventRecord;
/// # use ferrisetw::schema_locator::SchemaLocator;
/// # use ferrisetw::parser::Parser;
/// let my_callback = |record: &EventRecord, schema_locator: &SchemaLocator| {
/// let schema = schema_locator.event_schema(record).unwrap();
/// let parser = Parser::create(record, &schema);
///
/// // There are several ways to define the type requested for `try_parse`
/// // It is possible to use type inference...
/// let property1: Option<String> = parser.try_parse("PropertyName").ok();
///
/// // ...or to use the turbofish operator
/// match parser.try_parse::<u32>("OtherPropertyName") {
/// Ok(_) => println!("OtherPropertyName is a valid u32"),
/// Err(_) => println!("OtherPropertyName is invalid"),
/// }
/// };
/// ```
#[allow(dead_code)]
pub struct Parser<'schema, 'record> {
properties: &'schema [Property],
Expand All @@ -111,17 +118,6 @@ impl<'schema, 'record> Parser<'schema, 'record> {
///
/// # Arguments
/// * `schema` - The [Schema] from the ETW Event we want to parse
///
/// # Example
/// ```
/// # use ferrisetw::native::etw_types::EventRecord;
/// # use ferrisetw::schema_locator::SchemaLocator;
/// # use ferrisetw::parser::Parser;
/// let my_callback = |record: &EventRecord, schema_locator: &SchemaLocator| {
/// let schema = schema_locator.event_schema(record).unwrap();
/// let parser = Parser::create(record, &schema);
/// };
/// ```
pub fn create(event_record: &'record EventRecord, schema: &'schema Schema) -> Self {
Parser {
record: &event_record,
Expand Down Expand Up @@ -253,12 +249,45 @@ impl<'schema, 'record> Parser<'schema, 'record> {

Err(ParserError::NotFound)
}

/// Return a property from the event, or an error in case the parsing failed.
///
/// You must explicitly define `T`, the type you want to parse the property into.<br/>
/// In case this type is not compatible with the ETW type, [`ParserError::InvalidType`] is returned.
pub fn try_parse<T>(&self, name: &str) -> ParserResult<T>
where Parser<'schema, 'record>: private::TryParse<T>
{
use crate::parser::private::TryParse;
self.try_parse_impl(name)
}
}



mod private {
use super::*;

/// Trait to try and parse a type
///
/// This trait has to be implemented in order to be able to parse a type we want to retrieve from
/// within an Event.
///
/// An implementation for most of the Primitive Types is created by using a Macro, any other needed type
/// requires this trait to be implemented
pub trait TryParse<T> {
/// Implement the `try_parse` function to provide a way to Parse `T` from an ETW event or
/// return an Error in case the type `T` can't be parsed
///
/// # Arguments
/// * `name` - Name of the property to be found in the Schema
fn try_parse_impl(&self, name: &str) -> Result<T, ParserError>;
}
}

macro_rules! impl_try_parse_primitive {
($T:ident) => {
impl TryParse<$T> for Parser<'_, '_> {
fn try_parse(&self, name: &str) -> ParserResult<$T> {
impl private::TryParse<$T> for Parser<'_, '_> {
fn try_parse_impl(&self, name: &str) -> ParserResult<$T> {
let prop_slice = self.find_property(name)?;

// TODO: Check In and Out type and do a better type checking
Expand Down Expand Up @@ -298,7 +327,7 @@ impl_try_parse_primitive!(isize);
/// ```
/// # use ferrisetw::native::etw_types::EventRecord;
/// # use ferrisetw::schema_locator::SchemaLocator;
/// # use ferrisetw::parser::{Parser, TryParse};
/// # use ferrisetw::parser::Parser;
/// let my_callback = |record: &EventRecord, schema_locator: &SchemaLocator| {
/// let schema = schema_locator.event_schema(record).unwrap();
/// let parser = Parser::create(record, &schema);
Expand All @@ -307,8 +336,8 @@ impl_try_parse_primitive!(isize);
/// ```
///
/// [TdhInTypes]: TdhInType
impl TryParse<String> for Parser<'_, '_> {
fn try_parse(&self, name: &str) -> ParserResult<String> {
impl private::TryParse<String> for Parser<'_, '_> {
fn try_parse_impl(&self, name: &str) -> ParserResult<String> {
let prop_slice = self.find_property(name)?;

// TODO: Handle errors and type checking better
Expand All @@ -334,8 +363,8 @@ impl TryParse<String> for Parser<'_, '_> {
}
}

impl TryParse<GUID> for Parser<'_, '_> {
fn try_parse(&self, name: &str) -> Result<GUID, ParserError> {
impl private::TryParse<GUID> for Parser<'_, '_> {
fn try_parse_impl(&self, name: &str) -> Result<GUID, ParserError> {
let prop_slice = self.find_property(name)?;

let guid_string = utils::parse_utf16_guid(prop_slice.buffer);
Expand All @@ -348,8 +377,8 @@ impl TryParse<GUID> for Parser<'_, '_> {
}
}

impl TryParse<IpAddr> for Parser<'_, '_> {
fn try_parse(&self, name: &str) -> ParserResult<IpAddr> {
impl private::TryParse<IpAddr> for Parser<'_, '_> {
fn try_parse_impl(&self, name: &str) -> ParserResult<IpAddr> {
let prop_slice = self.find_property(name)?;

if prop_slice.property.out_type() != TdhOutType::OutTypeIpv4
Expand Down Expand Up @@ -416,23 +445,23 @@ impl std::fmt::Display for Pointer {
}
}

impl TryParse<Pointer> for Parser<'_, '_> {
fn try_parse(&self, name: &str) -> ParserResult<Pointer> {
impl private::TryParse<Pointer> for Parser<'_, '_> {
fn try_parse_impl(&self, name: &str) -> ParserResult<Pointer> {
let prop_slice = self.find_property(name)?;

let mut res = Pointer::default();
if prop_slice.buffer.len() == std::mem::size_of::<u32>() {
res.0 = TryParse::<u32>::try_parse(self, name)? as usize;
res.0 = private::TryParse::<u32>::try_parse_impl(self, name)? as usize;
} else {
res.0 = TryParse::<u64>::try_parse(self, name)? as usize;
res.0 = private::TryParse::<u64>::try_parse_impl(self, name)? as usize;
}

Ok(res)
}
}

impl TryParse<Vec<u8>> for Parser<'_, '_> {
fn try_parse(&self, name: &str) -> Result<Vec<u8>, ParserError> {
impl private::TryParse<Vec<u8>> for Parser<'_, '_> {
fn try_parse_impl(&self, name: &str) -> Result<Vec<u8>, ParserError> {
let prop_slice = self.find_property(name)?;
Ok(prop_slice.buffer.to_vec())
}
Expand Down
6 changes: 3 additions & 3 deletions tests/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use ferrisetw::native::etw_types::EventRecord;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::trace::UserTrace;
use ferrisetw::trace::TraceTrait;
use ferrisetw::parser::{Parser, TryParse};
use ferrisetw::parser::Parser;

mod utils;
use utils::{Status, TestKind};
Expand Down Expand Up @@ -123,12 +123,12 @@ fn check_a_few_cases(record: &EventRecord, parser: &Parser) {
// Parsing with a wrong type should properly error out
if record.event_id() == EVENT_ID_DNS_QUERY_INITIATED {
let _right_type: String = parser.try_parse("QueryName").unwrap();
let wrong_type: Result<u32, _> = parser.try_parse("QueryName");
let wrong_type = parser.try_parse::<u32>("QueryName");
assert!(wrong_type.is_err());
}

// Giving an unknown property should properly error out
let wrong_name: Result<u32, _> = parser.try_parse("NoSuchProperty");
let wrong_name = parser.try_parse::<u32>("NoSuchProperty");
assert!(wrong_name.is_err());
}

Expand Down
4 changes: 2 additions & 2 deletions tests/kernel_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::time::Duration;
use ferrisetw::provider::{Provider, EventFilter};
use ferrisetw::native::etw_types::EventRecord;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::parser::{Parser, TryParse};
use ferrisetw::parser::Parser;
use ferrisetw::trace::KernelTrace;
use ferrisetw::provider::kernel_providers;

Expand Down Expand Up @@ -88,7 +88,7 @@ fn generate_image_load_events() {

fn has_seen_dll_load(record: &EventRecord, parser: &Parser) -> bool {
if record.process_id() == std::process::id() {
let filename: Result<String, _> = parser.try_parse("FileName");
let filename = parser.try_parse::<String>("FileName");
println!(" this one's for us: {:?}", filename);
if let Ok(filename) = filename {
if filename.ends_with(TEST_LIBRARY_NAME) {
Expand Down

0 comments on commit 477cbfb

Please sign in to comment.