Skip to main content

async_snmp/message/
mod.rs

1//! SNMP message wrappers.
2//!
3//! Messages encapsulate PDUs with version and authentication information.
4//!
5//! # Message Types
6//!
7//! - [`CommunityMessage`] - V1/V2c messages with community string auth
8//! - [`V3Message`] - V3 messages with USM security
9
10mod community;
11mod v3;
12
13pub use community::{CommunityMessage, CommunityPdu};
14pub use v3::{
15    MsgFlags, MsgGlobalData, ScopedPdu, SecurityLevel, SecurityModel, V3Message, V3MessageData,
16};
17
18use crate::ber::Decoder;
19use crate::error::internal::DecodeErrorKind;
20use crate::error::{Error, Result, UNKNOWN_TARGET};
21use crate::pdu::Pdu;
22use crate::version::Version;
23use bytes::Bytes;
24
25/// Decoded SNMP message (any version).
26///
27/// This enum provides a unified interface for working with SNMP messages
28/// regardless of version. Use [`Message::decode`] to parse incoming data.
29#[derive(Debug)]
30pub enum Message {
31    /// `SNMPv1` or `SNMPv2c` message with community string
32    Community(CommunityMessage),
33    /// `SNMPv3` message with USM security
34    V3(V3Message),
35}
36
37impl Message {
38    /// Get a reference to the PDU.
39    ///
40    /// Returns `None` for encrypted V3 messages or `SNMPv1` Trap messages.
41    pub fn pdu(&self) -> Option<&Pdu> {
42        match self {
43            Message::Community(m) => m.pdu.standard(),
44            Message::V3(m) => m.pdu(),
45        }
46    }
47
48    /// Consume and return the PDU.
49    ///
50    /// Returns `None` for encrypted V3 messages or `SNMPv1` Trap messages.
51    pub fn into_pdu(self) -> Option<Pdu> {
52        match self {
53            Message::Community(m) => match m.pdu {
54                CommunityPdu::Standard(p) => Some(p),
55                CommunityPdu::TrapV1(_) => None,
56            },
57            Message::V3(m) => m.into_pdu(),
58        }
59    }
60
61    /// Get the SNMP version.
62    pub fn version(&self) -> Version {
63        match self {
64            Message::Community(m) => m.version,
65            Message::V3(_) => Version::V3,
66        }
67    }
68
69    /// Decode a message from bytes.
70    ///
71    /// Automatically detects the SNMP version and parses accordingly.
72    pub fn decode(data: Bytes) -> Result<Self> {
73        let mut decoder = Decoder::new(data);
74        let mut seq = decoder.read_sequence()?;
75
76        // Read version to determine message type
77        let version_num = seq.read_integer()?;
78        let version = Version::from_i32(version_num).ok_or_else(|| {
79            tracing::debug!(target: "async_snmp::ber", { offset = seq.offset(), kind = %DecodeErrorKind::UnknownVersion(version_num) }, "decode error");
80            Error::MalformedResponse {
81                target: UNKNOWN_TARGET,
82            }
83            .boxed()
84        })?;
85
86        // Decode remainder using version-specific handler
87        match version {
88            Version::V1 | Version::V2c => {
89                let msg = CommunityMessage::decode_from_sequence(&mut seq, version)?;
90                Ok(Message::Community(msg))
91            }
92            Version::V3 => {
93                let msg = V3Message::decode_from_sequence(&mut seq)?;
94                Ok(Message::V3(msg))
95            }
96        }
97    }
98}
99
100// Convenience conversions
101impl From<CommunityMessage> for Message {
102    fn from(msg: CommunityMessage) -> Self {
103        Message::Community(msg)
104    }
105}
106
107impl From<V3Message> for Message {
108    fn from(msg: V3Message) -> Self {
109        Message::V3(msg)
110    }
111}