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 Self::decode_from_sequence(&mut seq, version)
87 }
88
89 pub(crate) fn decode_from_sequence(seq: &mut Decoder, version: Version) -> Result<Self> {
91 if version == Version::V3 {
92 return Err(Error::decode(
93 seq.offset(),
94 DecodeErrorKind::UnknownVersion(3),
95 ));
96 }
97
98 let community = seq.read_octet_string()?;
99 let pdu = Pdu::decode(seq)?;
100
101 Ok(CommunityMessage {
102 version,
103 community,
104 pdu,
105 })
106 }
107
108 pub fn into_pdu(self) -> Pdu {
110 self.pdu
111 }
112
113 pub fn encode_bulk(version: Version, community: impl Into<Bytes>, pdu: &GetBulkPdu) -> Bytes {
117 debug_assert!(version != Version::V1, "GETBULK not supported in SNMPv1");
118
119 let community = community.into();
120 let mut buf = EncodeBuf::new();
121
122 buf.push_sequence(|buf| {
123 pdu.encode(buf);
124 buf.push_octet_string(&community);
125 buf.push_integer(version.as_i32());
126 });
127
128 buf.finish()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::oid;
136
137 #[test]
138 fn test_v1_roundtrip() {
139 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
140 let msg = CommunityMessage::v1(b"public".as_slice(), pdu);
141
142 let encoded = msg.encode();
143 let decoded = CommunityMessage::decode(encoded).unwrap();
144
145 assert_eq!(decoded.version, Version::V1);
146 assert_eq!(decoded.community.as_ref(), b"public");
147 assert_eq!(decoded.pdu.request_id, 42);
148 }
149
150 #[test]
151 fn test_v2c_roundtrip() {
152 let pdu = Pdu::get_request(123, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
153 let msg = CommunityMessage::v2c(b"private".as_slice(), pdu);
154
155 let encoded = msg.encode();
156 let decoded = CommunityMessage::decode(encoded).unwrap();
157
158 assert_eq!(decoded.version, Version::V2c);
159 assert_eq!(decoded.community.as_ref(), b"private");
160 assert_eq!(decoded.pdu.request_id, 123);
161 }
162
163 #[test]
164 fn test_version_preserved() {
165 for version in [Version::V1, Version::V2c] {
166 let pdu = Pdu::get_request(1, &[oid!(1, 3, 6, 1)]);
167 let msg = CommunityMessage::new(version, b"test".as_slice(), pdu);
168
169 let encoded = msg.encode();
170 let decoded = CommunityMessage::decode(encoded).unwrap();
171
172 assert_eq!(decoded.version, version);
173 }
174 }
175}