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::{DecodeErrorKind, Error, Result};
20use crate::pdu::Pdu;
21use crate::version::Version;
22use bytes::Bytes;
23
24/// Decoded SNMP message (any version).
25///
26/// This enum provides a unified interface for working with SNMP messages
27/// regardless of version. Use [`Message::decode`] to parse incoming data.
28#[derive(Debug)]
29pub enum Message {
30    /// SNMPv1 or SNMPv2c message with community string
31    Community(CommunityMessage),
32    /// SNMPv3 message with USM security
33    V3(V3Message),
34}
35
36impl Message {
37    /// Get a reference to the PDU.
38    ///
39    /// For V3 messages with encrypted data, this will panic.
40    /// Use `try_pdu()` for a fallible version.
41    pub fn pdu(&self) -> &Pdu {
42        match self {
43            Message::Community(m) => &m.pdu,
44            Message::V3(m) => m.pdu().expect("V3 message is encrypted; use try_pdu()"),
45        }
46    }
47
48    /// Try to get a reference to the PDU.
49    ///
50    /// Returns `None` for encrypted V3 messages.
51    pub fn try_pdu(&self) -> Option<&Pdu> {
52        match self {
53            Message::Community(m) => Some(&m.pdu),
54            Message::V3(m) => m.pdu(),
55        }
56    }
57
58    /// Consume and return the PDU.
59    ///
60    /// For V3 messages with encrypted data, this will panic.
61    /// Use `try_into_pdu()` for a fallible version.
62    pub fn into_pdu(self) -> Pdu {
63        match self {
64            Message::Community(m) => m.into_pdu(),
65            Message::V3(m) => m.into_pdu().expect("V3 message is encrypted"),
66        }
67    }
68
69    /// Try to consume and return the PDU.
70    ///
71    /// Returns `None` for encrypted V3 messages.
72    pub fn try_into_pdu(self) -> Option<Pdu> {
73        match self {
74            Message::Community(m) => Some(m.into_pdu()),
75            Message::V3(m) => m.into_pdu(),
76        }
77    }
78
79    /// Get the SNMP version.
80    pub fn version(&self) -> Version {
81        match self {
82            Message::Community(m) => m.version,
83            Message::V3(_) => Version::V3,
84        }
85    }
86
87    /// Decode a message from bytes.
88    ///
89    /// Automatically detects the SNMP version and parses accordingly.
90    pub fn decode(data: Bytes) -> Result<Self> {
91        let mut decoder = Decoder::new(data);
92        let mut seq = decoder.read_sequence()?;
93
94        // Read version to determine message type
95        let version_num = seq.read_integer()?;
96        let version = Version::from_i32(version_num).ok_or_else(|| {
97            Error::decode(seq.offset(), DecodeErrorKind::UnknownVersion(version_num))
98        })?;
99
100        // Decode remainder using version-specific handler
101        match version {
102            Version::V1 | Version::V2c => {
103                let msg = CommunityMessage::decode_from_sequence(&mut seq, version)?;
104                Ok(Message::Community(msg))
105            }
106            Version::V3 => {
107                let msg = V3Message::decode_from_sequence(&mut seq)?;
108                Ok(Message::V3(msg))
109            }
110        }
111    }
112}
113
114// Convenience conversions
115impl From<CommunityMessage> for Message {
116    fn from(msg: CommunityMessage) -> Self {
117        Message::Community(msg)
118    }
119}
120
121impl From<V3Message> for Message {
122    fn from(msg: V3Message) -> Self {
123        Message::V3(msg)
124    }
125}