diff options
Diffstat (limited to 'rust/vendor/sawp/src')
-rw-r--r-- | rust/vendor/sawp/src/error.rs | 153 | ||||
-rw-r--r-- | rust/vendor/sawp/src/ffi.rs | 86 | ||||
-rw-r--r-- | rust/vendor/sawp/src/lib.rs | 44 | ||||
-rw-r--r-- | rust/vendor/sawp/src/parser.rs | 39 | ||||
-rw-r--r-- | rust/vendor/sawp/src/probe.rs | 31 | ||||
-rw-r--r-- | rust/vendor/sawp/src/protocol.rs | 8 |
6 files changed, 361 insertions, 0 deletions
diff --git a/rust/vendor/sawp/src/error.rs b/rust/vendor/sawp/src/error.rs new file mode 100644 index 0000000..d27717b --- /dev/null +++ b/rust/vendor/sawp/src/error.rs @@ -0,0 +1,153 @@ +#[cfg(feature = "ffi")] +use sawp_ffi::GenerateFFI; + +use std::num::NonZeroUsize; + +// Re-export types used for ErrorKind +use nom::error::ErrorKind as NomErrorKind; +use nom::Needed as NomNeeded; + +/// Helper that uses this module's error type +pub type Result<T> = std::result::Result<T, Error>; + +/// Helper for nom's default error type +pub type NomError<I> = nom::error::Error<I>; + +/// Common protocol or parsing error +/// +/// This error type is meant to return the errors that +/// are common across parsers and other sub packages. +/// Sub packages may choose to implement their own error +/// types if they wish to avoid adding extra dependencies +/// to the base crate. +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "ffi", derive(GenerateFFI))] +#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp"))] +pub struct Error { + pub kind: ErrorKind, +} + +impl Error { + pub fn new(kind: ErrorKind) -> Self { + Self { kind } + } + + /// Helper for creating an error with a `ErrorKind::Incomplete` and a needed size. + pub fn incomplete_needed(size: usize) -> Self { + Error::new(ErrorKind::Incomplete( + NonZeroUsize::new(size) + .map(Needed::Size) + .unwrap_or(Needed::Unknown), + )) + } + + /// Helper for creating an error with a `ErrorKind::Incomplete` and an unknown size. + pub fn incomplete() -> Self { + Error::new(ErrorKind::Incomplete(Needed::Unknown)) + } + + /// Helper for creating a parse error. + #[cfg(verbose)] + pub fn parse(msg: Option<String>) -> Self { + Error::new(ErrorKind::ParseError(msg)) + } + + /// Helper for creating a parse error. + #[cfg(not(verbose))] + pub fn parse(_msg: Option<String>) -> Self { + Error::new(ErrorKind::ParseError(None)) + } +} + +impl From<ErrorKind> for Error { + fn from(kind: ErrorKind) -> Self { + Self::new(kind) + } +} + +/// Number of bytes needed for the next parsing attempt. +/// +/// Used in `ErrorKind::Incomplete` to tell the caller how many bytes to wait +/// for before calling the parser with more data. +#[derive(Debug, PartialEq)] +pub enum Needed { + Unknown, + Size(NonZeroUsize), +} + +/// Kinds of common errors used by the parsers +#[derive(Debug, PartialEq)] +#[non_exhaustive] +#[cfg_attr(feature = "ffi", derive(GenerateFFI))] +#[cfg_attr(feature = "ffi", sawp_ffi(type_only, prefix = "sawp"))] +pub enum ErrorKind { + /// Feature is not yet implemented. + Unimplemented, + /// Parser could not advance based on the data provided. + /// + /// Usually indicates the provided input bytes cannot be parsed + /// for the protocol. + // + // Developer note: + // + // This error should only be used as a last resort. Consider + // returning Ok and adding validation error flags to the + // parser's `Message` instead. + InvalidData, + /// Generic parsing error with optional message. + ParseError(Option<String>), + /// Parser did not advance because more data is required to + /// make a decision. + /// + /// The caller should gather more data and try again. + Incomplete(Needed), +} + +impl From<NomErrorKind> for ErrorKind { + #[cfg(verbose)] + fn from(kind: NomErrorKind) -> Self { + Self::ParseError(Some(format!("{:?}", kind))) + } + + #[cfg(not(verbose))] + fn from(_kind: NomErrorKind) -> Self { + Self::ParseError(None) + } +} + +impl<I: std::fmt::Debug> From<nom::Err<NomError<I>>> for Error { + fn from(nom_err: nom::Err<NomError<I>>) -> Self { + match nom_err { + nom::Err::Error(err) | nom::Err::Failure(err) => Error::new(err.code.into()), + nom::Err::Incomplete(needed) => match needed { + NomNeeded::Unknown => Error::incomplete(), + NomNeeded::Size(size) => Error::incomplete_needed(size.into()), + }, + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { + match &self.kind { + ErrorKind::Unimplemented => write!(f, "Unimplemented feature"), + ErrorKind::InvalidData => write!(f, "Encountered invalid data"), + ErrorKind::ParseError(err) if err.is_some() => { + write!(f, "Parsing error: {}", err.clone().unwrap()) + } + ErrorKind::ParseError(_) => write!(f, "Parsing error"), + ErrorKind::Incomplete(Needed::Unknown) => write!(f, "More bytes required to parse"), + ErrorKind::Incomplete(Needed::Size(n)) => { + write!(f, "{} more bytes required to parse", n) + } + } + } +} + +impl std::error::Error for Error {} + +impl<I: std::fmt::Debug> From<NomError<I>> for Error { + fn from(nom_err: NomError<I>) -> Self { + Error::new(nom_err.code.into()) + } +} diff --git a/rust/vendor/sawp/src/ffi.rs b/rust/vendor/sawp/src/ffi.rs new file mode 100644 index 0000000..aeed1cc --- /dev/null +++ b/rust/vendor/sawp/src/ffi.rs @@ -0,0 +1,86 @@ +use sawp_ffi::deref; + +/// Note this function only works for Vec<u8> +/// for other types, use the field_ptr accessor +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_vector_get_data(vec: *const Vec<u8>) -> *const u8 { + deref!(vec).as_ptr() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_vector_get_size(vec: *const Vec<u8>) -> usize { + deref!(vec).len() +} + +/// Note: Returned string is not null terminated +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_string_get_ptr(s: *const String) -> *const u8 { + deref!(s).as_ptr() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_string_get_size(s: *const String) -> usize { + deref!(s).len() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipv4addr_get_data(ip: *const std::net::Ipv4Addr) -> u32 { + u32::from_be_bytes(deref!(ip).octets()) +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipv6addr_get_data(ip: *const std::net::Ipv6Addr) -> *const u8 { + deref!(ip).octets().as_slice().as_ptr() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_is_v4(ip: *const std::net::IpAddr) -> bool { + deref!(ip).is_ipv4() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_is_v6(ip: *const std::net::IpAddr) -> bool { + deref!(ip).is_ipv6() +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_as_v4( + ip: *const std::net::IpAddr, +) -> *const std::net::Ipv4Addr { + if let std::net::IpAddr::V4(addr) = deref!(ip) { + addr + } else { + std::ptr::null() + } +} + +/// # Safety +/// function will panic if called with null +#[no_mangle] +pub unsafe extern "C" fn sawp_ipaddr_as_v6( + ip: *const std::net::IpAddr, +) -> *const std::net::Ipv6Addr { + if let std::net::IpAddr::V6(addr) = deref!(ip) { + addr + } else { + std::ptr::null() + } +} diff --git a/rust/vendor/sawp/src/lib.rs b/rust/vendor/sawp/src/lib.rs new file mode 100644 index 0000000..7c5940b --- /dev/null +++ b/rust/vendor/sawp/src/lib.rs @@ -0,0 +1,44 @@ +/*! +# SAWP: Security Aware Wire Protocol parsing library + +This library contains parsers for various wire protocols +and is intended to be used in network security sensors. + +The base library contains all of the common types and traits +used by the parsers. + +Usage documentation can be found in the [README](https://github.com/CybercentreCanada/sawp/blob/main/README.md). + +## Protocols + +Each protocol, along with certain features, are implemented +in a separate package inside this workspace. This reduces the +number dependencies needed for using various protocols or +features. A practical use of this library can be found by +referring to the protocol parser you wish to use: +- [Diameter](/sawp-diameter) +- [Json](/sawp-json) +- [Modbus](/sawp-modbus) + +## Utility + +The following utility packages also exist: +- [File](/sawp-file) Serializes API calls for debugging +*/ + +#![allow(clippy::unneeded_field_pattern)] + +/// Return common errors +pub mod error; + +/// Parse Messages +pub mod parser; + +/// Probe Bytes +pub mod probe; + +/// Describe a Protocol +pub mod protocol; + +#[cfg(feature = "ffi")] +pub mod ffi; diff --git a/rust/vendor/sawp/src/parser.rs b/rust/vendor/sawp/src/parser.rs new file mode 100644 index 0000000..6909305 --- /dev/null +++ b/rust/vendor/sawp/src/parser.rs @@ -0,0 +1,39 @@ +use crate::error::Result; +use crate::protocol::Protocol; + +/// Destination of the input byte stream. +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum Direction { + /// Message is destined to the client + ToClient, + /// Message is destined to the server + ToServer, + /// Direction is not known + Unknown, +} + +/// Trait for parsing message from an input byte stream. +pub trait Parse<'a>: Protocol<'a> { + /// Returns a tuple containing the remaining unparsed data and the parsed `Message`. + /// + /// A return value of `Result::Ok` indicates that the parser has *made progress* + /// and should only be used when the remaining unparsed data is less than the input. + /// + /// A return value of `Result::Err` indicates that *no progress* was made + /// and the user may call the parse function again with the same input in + /// some scenarios: + /// - `ErrorKind::Incomplete`: call `parse` once more input data is available. + /// + /// Consequently, `Result::Ok(None)` is used to indicate the parser made + /// progress but needs more data to return a complete `Message`. Internal + /// buffering may occur depending on the implementation. + /// + /// `Result::Err(ErrorKind::Incomplete(_))` must be used instead of `Result::Ok(None)` + /// when no progress was made parsing the input. + fn parse( + &self, + input: &'a [u8], + direction: Direction, + ) -> Result<(&'a [u8], Option<Self::Message>)>; +} diff --git a/rust/vendor/sawp/src/probe.rs b/rust/vendor/sawp/src/probe.rs new file mode 100644 index 0000000..3aa976d --- /dev/null +++ b/rust/vendor/sawp/src/probe.rs @@ -0,0 +1,31 @@ +use crate::error::{Error, ErrorKind}; +use crate::parser::{Direction, Parse}; +use crate::protocol::Protocol; + +/// Result of probing the underlying bytes. +#[derive(Debug, PartialEq)] +pub enum Status { + /// Data matches this protocol + Recognized, + /// Data does not match this protocol + Unrecognized, + /// More data is needed to make a decision + Incomplete, +} + +pub trait Probe<'a>: Protocol<'a> + Parse<'a> { + /// Probes the input to recognize if the underlying bytes likely match this + /// protocol. + /// + /// Returns a probe status. Probe again once more data is available when the + /// status is `Status::Incomplete`. + fn probe(&self, input: &'a [u8], direction: Direction) -> Status { + match self.parse(input, direction) { + Ok((_, _)) => Status::Recognized, + Err(Error { + kind: ErrorKind::Incomplete(_), + }) => Status::Incomplete, + Err(_) => Status::Unrecognized, + } + } +} diff --git a/rust/vendor/sawp/src/protocol.rs b/rust/vendor/sawp/src/protocol.rs new file mode 100644 index 0000000..a24d7e2 --- /dev/null +++ b/rust/vendor/sawp/src/protocol.rs @@ -0,0 +1,8 @@ +/// Represents the basic elements of a protocol +pub trait Protocol<'a> { + /// Type of message returned when parsing + type Message: 'a; + + /// Protocol name string + fn name() -> &'static str; +} |