async_snmp/message/
community.rs1use crate::ber::{Decoder, EncodeBuf};
9use crate::error::{DecodeErrorKind, Error, Result};
10use crate::pdu::{GetBulkPdu, Pdu};
11use crate::version::Version;
12use bytes::Bytes;
13
14#[derive(Debug, Clone)]
19pub struct CommunityMessage {
20 pub version: Version,
22 pub community: Bytes,
24 pub pdu: Pdu,
26}
27
28impl CommunityMessage {
29 pub fn new(version: Version, community: impl Into<Bytes>, pdu: Pdu) -> Self {
34 assert!(
35 matches!(version, Version::V1 | Version::V2c),
36 "CommunityMessage only supports V1/V2c, not {:?}",
37 version
38 );
39 Self {
40 version,
41 community: community.into(),
42 pdu,
43 }
44 }
45
46 pub fn v2c(community: impl Into<Bytes>, pdu: Pdu) -> Self {
48 Self::new(Version::V2c, community, pdu)
49 }
50
51 pub fn v1(community: impl Into<Bytes>, pdu: Pdu) -> Self {
53 Self::new(Version::V1, community, pdu)
54 }
55
56 pub fn encode(&self) -> Bytes {
58 let mut buf = EncodeBuf::new();
59
60 buf.push_sequence(|buf| {
61 self.pdu.encode(buf);
62 buf.push_octet_string(&self.community);
63 buf.push_integer(self.version.as_i32());
64 });
65
66 buf.finish()
67 }
68
69 pub fn decode(data: Bytes) -> Result<Self> {
73 let mut decoder = Decoder::new(data);
74 Self::decode_from(&mut decoder)
75 }
76
77 pub(crate) fn decode_from(decoder: &mut Decoder) -> Result<Self> {
79 let mut seq = decoder.read_sequence()?;
80
81 let version_num = seq.read_integer()?;
82 let version = Version::from_i32(version_num).ok_or_else(|| {
83 Error::decode(seq.offset(), DecodeErrorKind::UnknownVersion(version_num))
84 })?;
85
86 if version == Version::V3 {
87 return Err(Error::decode(
88 seq.offset(),
89 DecodeErrorKind::UnknownVersion(3),
90 ));
91 }
92
93 let community = seq.read_octet_string()?;
94 let pdu = Pdu::decode(&mut seq)?;
95
96 Ok(CommunityMessage {
97 version,
98 community,
99 pdu,
100 })
101 }
102
103 pub fn into_pdu(self) -> Pdu {
105 self.pdu
106 }
107
108 pub fn encode_bulk(version: Version, community: impl Into<Bytes>, pdu: &GetBulkPdu) -> Bytes {
112 debug_assert!(version != Version::V1, "GETBULK not supported in SNMPv1");
113
114 let community = community.into();
115 let mut buf = EncodeBuf::new();
116
117 buf.push_sequence(|buf| {
118 pdu.encode(buf);
119 buf.push_octet_string(&community);
120 buf.push_integer(version.as_i32());
121 });
122
123 buf.finish()
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::oid;
131
132 #[test]
133 fn test_v1_roundtrip() {
134 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
135 let msg = CommunityMessage::v1(b"public".as_slice(), pdu);
136
137 let encoded = msg.encode();
138 let decoded = CommunityMessage::decode(encoded).unwrap();
139
140 assert_eq!(decoded.version, Version::V1);
141 assert_eq!(decoded.community.as_ref(), b"public");
142 assert_eq!(decoded.pdu.request_id, 42);
143 }
144
145 #[test]
146 fn test_v2c_roundtrip() {
147 let pdu = Pdu::get_request(123, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
148 let msg = CommunityMessage::v2c(b"private".as_slice(), pdu);
149
150 let encoded = msg.encode();
151 let decoded = CommunityMessage::decode(encoded).unwrap();
152
153 assert_eq!(decoded.version, Version::V2c);
154 assert_eq!(decoded.community.as_ref(), b"private");
155 assert_eq!(decoded.pdu.request_id, 123);
156 }
157
158 #[test]
159 fn test_version_preserved() {
160 for version in [Version::V1, Version::V2c] {
161 let pdu = Pdu::get_request(1, &[oid!(1, 3, 6, 1)]);
162 let msg = CommunityMessage::new(version, b"test".as_slice(), pdu);
163
164 let encoded = msg.encode();
165 let decoded = CommunityMessage::decode(encoded).unwrap();
166
167 assert_eq!(decoded.version, version);
168 }
169 }
170}