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