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;
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    /// For V3 messages with encrypted data, this will panic.
41    /// Use `try_pdu()` for a fallible version.
42    pub fn pdu(&self) -> &Pdu {
43        match self {
44            Message::Community(m) => &m.pdu,
45            Message::V3(m) => m.pdu().expect("V3 message is encrypted; use try_pdu()"),
46        }
47    }
48
49    /// Try to get a reference to the PDU.
50    ///
51    /// Returns `None` for encrypted V3 messages.
52    pub fn try_pdu(&self) -> Option<&Pdu> {
53        match self {
54            Message::Community(m) => Some(&m.pdu),
55            Message::V3(m) => m.pdu(),
56        }
57    }
58
59    /// Consume and return the PDU.
60    ///
61    /// For V3 messages with encrypted data, this will panic.
62    /// Use `try_into_pdu()` for a fallible version.
63    pub fn into_pdu(self) -> Pdu {
64        match self {
65            Message::Community(m) => m.into_pdu(),
66            Message::V3(m) => m.into_pdu().expect("V3 message is encrypted"),
67        }
68    }
69
70    /// Try to consume and return the PDU.
71    ///
72    /// Returns `None` for encrypted V3 messages.
73    pub fn try_into_pdu(self) -> Option<Pdu> {
74        match self {
75            Message::Community(m) => Some(m.into_pdu()),
76            Message::V3(m) => m.into_pdu(),
77        }
78    }
79
80    /// Get the SNMP version.
81    pub fn version(&self) -> Version {
82        match self {
83            Message::Community(m) => m.version,
84            Message::V3(_) => Version::V3,
85        }
86    }
87
88    /// Decode a message from bytes.
89    ///
90    /// Automatically detects the SNMP version and parses accordingly.
91    pub fn decode(data: Bytes) -> Result<Self> {
92        let mut decoder = Decoder::new(data);
93        let mut seq = decoder.read_sequence()?;
94
95        // Read version to determine message type
96        let version_num = seq.read_integer()?;
97        let version = Version::from_i32(version_num).ok_or_else(|| {
98            tracing::debug!(target: "async_snmp::ber", { offset = seq.offset(), kind = %DecodeErrorKind::UnknownVersion(version_num) }, "decode error");
99            Error::MalformedResponse {
100                target: UNKNOWN_TARGET,
101            }
102            .boxed()
103        })?;
104
105        // Decode remainder using version-specific handler
106        match version {
107            Version::V1 | Version::V2c => {
108                let msg = CommunityMessage::decode_from_sequence(&mut seq, version)?;
109                Ok(Message::Community(msg))
110            }
111            Version::V3 => {
112                let msg = V3Message::decode_from_sequence(&mut seq)?;
113                Ok(Message::V3(msg))
114            }
115        }
116    }
117}
118
119// Convenience conversions
120impl From<CommunityMessage> for Message {
121    fn from(msg: CommunityMessage) -> Self {
122        Message::Community(msg)
123    }
124}
125
126impl From<V3Message> for Message {
127    fn from(msg: V3Message) -> Self {
128        Message::V3(msg)
129    }
130}