summaryrefslogtreecommitdiffstats
path: root/rust/vendor/sawp/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/vendor/sawp/src')
-rw-r--r--rust/vendor/sawp/src/error.rs153
-rw-r--r--rust/vendor/sawp/src/ffi.rs86
-rw-r--r--rust/vendor/sawp/src/lib.rs44
-rw-r--r--rust/vendor/sawp/src/parser.rs39
-rw-r--r--rust/vendor/sawp/src/probe.rs31
-rw-r--r--rust/vendor/sawp/src/protocol.rs8
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;
+}