Skip to content

Commit

Permalink
Add Codec enum, update rasn_snmp::v3::Message
Browse files Browse the repository at this point in the history
fixes #153
  • Loading branch information
XAMPPRocky committed Aug 26, 2023
1 parent 3e4f0ac commit 4bff831
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 74 deletions.
36 changes: 36 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,42 @@ pub mod de;
pub mod enc;
pub mod types;

use alloc::boxed::Box;

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum Codec {
Ber,
Cer,
Der,
Uper,
Aper,
}

impl Codec {
pub fn encode<E: Encode>(
&self,
value: &E,
) -> Result<alloc::vec::Vec<u8>, Box<dyn core::fmt::Display>> {
match self {
Self::Ber => ber::encode(value).map_err(|error| Box::new(error) as Box<_>),
Self::Cer => cer::encode(value).map_err(|error| Box::new(error) as Box<_>),
Self::Der => der::encode(value).map_err(|error| Box::new(error) as Box<_>),
Self::Uper => uper::encode(value).map_err(|error| Box::new(error) as Box<_>),
Self::Aper => aper::encode(value).map_err(|error| Box::new(error) as Box<_>),
}
}

pub fn decode<D: Decode>(&self, input: &[u8]) -> Result<D, Box<dyn core::fmt::Display>> {
match self {
Self::Ber => ber::decode(input).map_err(|error| Box::new(error) as Box<_>),
Self::Cer => cer::decode(input).map_err(|error| Box::new(error) as Box<_>),
Self::Der => der::decode(input).map_err(|error| Box::new(error) as Box<_>),
Self::Uper => uper::decode(input).map_err(|error| Box::new(error) as Box<_>),
Self::Aper => aper::decode(input).map_err(|error| Box::new(error) as Box<_>),
}
}
}

#[cfg(test)]
macro_rules! round_trip {
($codec:ident, $typ:ty, $value:expr, $expected:expr) => {{
Expand Down
101 changes: 27 additions & 74 deletions standards/snmp/src/v3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
//! - [RFC 3416](https://datatracker.ietf.org/doc/html/rfc3416): Version 2 of the Protocol
//! Operations for the Simple Network Management Protocol (SNMP)

use alloc::boxed::Box;

use rasn::{
prelude::*,
types::{Integer, OctetString},
Expand All @@ -23,7 +25,7 @@ pub use crate::v2::{
/// The SNMPv3 message format, corresponding to the SNMP version 3 Message Processing Model.
///
/// [RFC 3412 § 6](https://datatracker.ietf.org/doc/html/rfc3412#section-6)
#[derive(AsnType, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[derive(AsnType, Debug, Decode, Encode, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Message {
/// Identifies the layout of the SNMPv3 Message.
///
Expand All @@ -36,93 +38,44 @@ pub struct Message {
pub global_data: HeaderData,
/// Security model specific parameters.
///
/// The contents of this field is defined by the Security Model, however only the User-based
/// Security Model ([RFC 3414](https://datatracker.ietf.org/doc/html/rfc3414)) is defined here.
/// The contents of this field is defined by the Security Model.
///
/// [RFC 3412 § 6.6](https://datatracker.ietf.org/doc/html/rfc3412#section-6.6)
pub security_parameters: USMSecurityParameters,
pub security_parameters: OctetString,
/// Message data.
pub scoped_data: ScopedPduData,
}

impl Decode for Message {
fn decode_with_tag_and_constraints<D: rasn::Decoder>(
decoder: &mut D,
tag: rasn::Tag,
constraints: Constraints,
) -> Result<Self, D::Error> {
NestedMessage::decode_with_tag_and_constraints(decoder, tag, constraints).map(Self::from)
}
}

impl Encode for Message {
fn encode_with_tag_and_constraints<E: rasn::Encoder>(
impl Message {
pub fn decode_security_parameters<T: SecurityParameters>(
&self,
encoder: &mut E,
tag: rasn::Tag,
constraints: Constraints,
) -> Result<(), E::Error> {
NestedMessage::from(self.clone()).encode_with_tag_and_constraints(encoder, tag, constraints)
}
}

impl From<NestedMessage> for Message {
fn from(m: NestedMessage) -> Self {
Self {
version: m.version,
global_data: m.global_data,
security_parameters: m.security_parameters.0,
scoped_data: m.scoped_data,
codec: rasn::Codec,
) -> Result<T, alloc::boxed::Box<dyn core::fmt::Display>> {
if self.global_data.security_model != T::ID.into() {
return Err(Box::new(alloc::format!(
"`{}` doesn't match security model `{}`",
self.global_data.security_model,
T::ID
)) as Box<_>);
}
}
}

/// A helper type to allow a BER-encoded message to be nested in an OCTET STRING field.
#[derive(AsnType, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(tag(universal, 4))]
struct Nested<T>(T);

impl<T: Decode> Decode for Nested<T> {
fn decode_with_tag_and_constraints<D: Decoder>(
decoder: &mut D,
tag: Tag,
_: Constraints,
) -> Result<Self, D::Error> {
decoder.decode_explicit_prefix(tag).map(Self)
codec.decode::<T>(&self.security_parameters)
}
}

impl<T: Encode> Encode for Nested<T> {
fn encode_with_tag_and_constraints<E: Encoder>(
&self,
encoder: &mut E,
tag: Tag,
_: Constraints,
) -> Result<(), E::Error> {
encoder.encode_explicit_prefix(tag, &self.0).map(drop)
}
}
pub fn encode_security_parameters<T: SecurityParameters>(
&mut self,
codec: rasn::Codec,
value: T,
) -> Result<(), alloc::boxed::Box<dyn core::fmt::Display>> {
self.global_data.security_model = T::ID.into();

/// The actual encoding for `Message`, with `security_parameters` nested in an OCTET STRING field.
/// The `Encode` and `Decode` impls for `Message` are defined in terms of converting to and from
/// `NestedMessage` prior to encode/decode.
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
struct NestedMessage {
version: Integer,
global_data: HeaderData,
security_parameters: Nested<USMSecurityParameters>,
scoped_data: ScopedPduData,
self.security_parameters = codec.encode::<T>(&value)?.into();
Ok(())
}
}

impl From<Message> for NestedMessage {
fn from(m: Message) -> Self {
Self {
version: m.version,
global_data: m.global_data,
security_parameters: Nested(m.security_parameters),
scoped_data: m.scoped_data,
}
}
pub trait SecurityParameters: Decode + Encode {
const ID: u32;
}

/// Administrative data about a `Message`.
Expand Down
29 changes: 29 additions & 0 deletions standards/snmp/tests/issue153.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#[test]
fn test_snmp() {
let bytes: Vec<i8> = vec![
48, -127, -125, 2, 1, 3, 48, 16, 2, 3, 0, -15, -36, 2, 3, 0, -1, -1, 4, 1, 5, 2, 1, 3, 4,
55, 48, 53, 4, 17, -128, 0, 31, -120, -128, 101, 74, 97, 51, 95, 18, -59, 100, 0, 0, 0, 0,
2, 1, 11, 2, 2, 106, 24, 4, 9, 98, 111, 111, 116, 115, 116, 114, 97, 112, 4, 12, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 48, 51, 4, 17, -128, 0, 31, -120, -128, 101, 74, 97, 51,
95, 18, -59, 100, 0, 0, 0, 0, 4, 0, -96, 28, 2, 4, 65, 85, -7, -101, 2, 1, 0, 2, 1, 0, 48,
14, 48, 12, 6, 8, 43, 6, 1, 2, 1, 1, 5, 0, 5, 0,
];
let bytes: Vec<u8> = unsafe { std::mem::transmute(bytes) };
println!(
"{}",
bytes
.iter()
.map(|byte| format!("{:02X}", byte))
.collect::<String>()
);
let msg: rasn_snmp::v3::Message = rasn::der::decode(&bytes).unwrap();
let bytes2 = rasn::der::encode(&msg).unwrap();
println!(
"{}",
bytes2
.iter()
.map(|byte| format!("{:02X}", byte))
.collect::<String>()
);
pretty_assertions::assert_eq!(bytes, bytes2);
}

0 comments on commit 4bff831

Please sign in to comment.