async_snmp/
varbind.rs

1//! Variable binding (VarBind) type.
2//!
3//! A VarBind pairs an OID with a value.
4
5use crate::ber::{Decoder, EncodeBuf};
6use crate::error::Result;
7use crate::oid::Oid;
8use crate::value::Value;
9
10/// Variable binding - an OID-value pair.
11#[derive(Debug, Clone, PartialEq)]
12pub struct VarBind {
13    /// The object identifier.
14    pub oid: Oid,
15    /// The value.
16    pub value: Value,
17}
18
19impl VarBind {
20    /// Create a new VarBind.
21    pub fn new(oid: Oid, value: Value) -> Self {
22        Self { oid, value }
23    }
24
25    /// Create a VarBind with a NULL value (for GET requests).
26    pub fn null(oid: Oid) -> Self {
27        Self {
28            oid,
29            value: Value::Null,
30        }
31    }
32
33    /// Encode to BER.
34    pub fn encode(&self, buf: &mut EncodeBuf) {
35        buf.push_sequence(|buf| {
36            self.value.encode(buf);
37            buf.push_oid(&self.oid);
38        });
39    }
40
41    /// Returns the exact encoded size of this VarBind in bytes.
42    ///
43    /// This encodes the VarBind to a temporary buffer to determine the exact size.
44    /// Useful for response size estimation in GETBULK processing.
45    pub fn encoded_size(&self) -> usize {
46        let mut buf = EncodeBuf::new();
47        self.encode(&mut buf);
48        buf.len()
49    }
50
51    /// Decode from BER.
52    pub fn decode(decoder: &mut Decoder) -> Result<Self> {
53        let mut seq = decoder.read_sequence()?;
54        let oid = seq.read_oid()?;
55        let value = Value::decode(&mut seq)?;
56        Ok(VarBind { oid, value })
57    }
58}
59
60impl std::fmt::Display for VarBind {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        write!(f, "{} = {}", self.oid, self.value)
63    }
64}
65
66/// Encodes a list of VarBinds to BER format.
67///
68/// Writes the VarBinds as a SEQUENCE of SEQUENCE elements, where each inner
69/// SEQUENCE contains an OID and its associated value.
70pub fn encode_varbind_list(buf: &mut EncodeBuf, varbinds: &[VarBind]) {
71    buf.push_sequence(|buf| {
72        // Encode in reverse order since we're using reverse buffer
73        for vb in varbinds.iter().rev() {
74            vb.encode(buf);
75        }
76    });
77}
78
79/// Decodes a BER-encoded VarBind list into a vector of VarBinds.
80///
81/// Expects a SEQUENCE containing zero or more VarBind SEQUENCE elements.
82pub fn decode_varbind_list(decoder: &mut Decoder) -> Result<Vec<VarBind>> {
83    let mut seq = decoder.read_sequence()?;
84    let mut varbinds = Vec::new();
85
86    while !seq.is_empty() {
87        varbinds.push(VarBind::decode(&mut seq)?);
88    }
89
90    Ok(varbinds)
91}
92
93/// Encodes OIDs with NULL values for GET requests.
94///
95/// Creates a VarBind list where each OID is paired with a NULL value,
96/// as required by SNMP GET, GETNEXT, and GETBULK request PDUs.
97pub fn encode_null_varbinds(buf: &mut EncodeBuf, oids: &[Oid]) {
98    buf.push_sequence(|buf| {
99        for oid in oids.iter().rev() {
100            buf.push_sequence(|buf| {
101                buf.push_null();
102                buf.push_oid(oid);
103            });
104        }
105    });
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use crate::oid;
112    use bytes::Bytes;
113
114    #[test]
115    fn test_varbind_roundtrip() {
116        let vb = VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42));
117
118        let mut buf = EncodeBuf::new();
119        vb.encode(&mut buf);
120        let bytes = buf.finish();
121
122        let mut decoder = Decoder::new(bytes);
123        let decoded = VarBind::decode(&mut decoder).unwrap();
124
125        assert_eq!(vb, decoded);
126    }
127
128    #[test]
129    fn test_varbind_list_roundtrip() {
130        let varbinds = vec![
131            VarBind::new(oid!(1, 3, 6, 1), Value::Integer(1)),
132            VarBind::new(oid!(1, 3, 6, 2), Value::Integer(2)),
133        ];
134
135        let mut buf = EncodeBuf::new();
136        encode_varbind_list(&mut buf, &varbinds);
137        let bytes = buf.finish();
138
139        let mut decoder = Decoder::new(bytes);
140        let decoded = decode_varbind_list(&mut decoder).unwrap();
141
142        assert_eq!(varbinds, decoded);
143    }
144
145    // ========================================================================
146    // Exception Value VarBind Tests
147    // ========================================================================
148
149    #[test]
150    fn test_varbind_no_such_object() {
151        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject);
152
153        let mut buf = EncodeBuf::new();
154        vb.encode(&mut buf);
155        let bytes = buf.finish();
156
157        let mut decoder = Decoder::new(bytes);
158        let decoded = VarBind::decode(&mut decoder).unwrap();
159
160        assert_eq!(vb, decoded);
161        assert!(decoded.value.is_exception());
162    }
163
164    #[test]
165    fn test_varbind_no_such_instance() {
166        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchInstance);
167
168        let mut buf = EncodeBuf::new();
169        vb.encode(&mut buf);
170        let bytes = buf.finish();
171
172        let mut decoder = Decoder::new(bytes);
173        let decoded = VarBind::decode(&mut decoder).unwrap();
174
175        assert_eq!(vb, decoded);
176        assert!(decoded.value.is_exception());
177    }
178
179    #[test]
180    fn test_varbind_end_of_mib_view() {
181        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::EndOfMibView);
182
183        let mut buf = EncodeBuf::new();
184        vb.encode(&mut buf);
185        let bytes = buf.finish();
186
187        let mut decoder = Decoder::new(bytes);
188        let decoded = VarBind::decode(&mut decoder).unwrap();
189
190        assert_eq!(vb, decoded);
191        assert!(decoded.value.is_exception());
192    }
193
194    // ========================================================================
195    // VarBind List Edge Cases
196    // ========================================================================
197
198    #[test]
199    fn test_varbind_list_empty() {
200        let varbinds: Vec<VarBind> = vec![];
201
202        let mut buf = EncodeBuf::new();
203        encode_varbind_list(&mut buf, &varbinds);
204        let bytes = buf.finish();
205
206        let mut decoder = Decoder::new(bytes);
207        let decoded = decode_varbind_list(&mut decoder).unwrap();
208
209        assert!(decoded.is_empty());
210    }
211
212    #[test]
213    fn test_varbind_list_single() {
214        let varbinds = vec![VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42))];
215
216        let mut buf = EncodeBuf::new();
217        encode_varbind_list(&mut buf, &varbinds);
218        let bytes = buf.finish();
219
220        let mut decoder = Decoder::new(bytes);
221        let decoded = decode_varbind_list(&mut decoder).unwrap();
222
223        assert_eq!(varbinds, decoded);
224    }
225
226    #[test]
227    fn test_varbind_list_with_exceptions() {
228        let varbinds = vec![
229            VarBind::new(
230                oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
231                Value::OctetString(Bytes::from_static(b"Linux router")),
232            ),
233            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 99, 0), Value::NoSuchObject),
234            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::TimeTicks(123456)),
235            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 100, 0), Value::NoSuchInstance),
236        ];
237
238        let mut buf = EncodeBuf::new();
239        encode_varbind_list(&mut buf, &varbinds);
240        let bytes = buf.finish();
241
242        let mut decoder = Decoder::new(bytes);
243        let decoded = decode_varbind_list(&mut decoder).unwrap();
244
245        assert_eq!(varbinds, decoded);
246        assert!(!decoded[0].value.is_exception());
247        assert!(decoded[1].value.is_exception());
248        assert!(!decoded[2].value.is_exception());
249        assert!(decoded[3].value.is_exception());
250    }
251
252    #[test]
253    fn test_varbind_list_all_exceptions() {
254        let varbinds = vec![
255            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject),
256            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::NoSuchInstance),
257            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::EndOfMibView),
258        ];
259
260        let mut buf = EncodeBuf::new();
261        encode_varbind_list(&mut buf, &varbinds);
262        let bytes = buf.finish();
263
264        let mut decoder = Decoder::new(bytes);
265        let decoded = decode_varbind_list(&mut decoder).unwrap();
266
267        assert_eq!(varbinds, decoded);
268        assert!(decoded.iter().all(|vb| vb.value.is_exception()));
269    }
270
271    #[test]
272    fn test_varbind_list_mixed_value_types() {
273        let varbinds = vec![
274            VarBind::new(
275                oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
276                Value::OctetString(Bytes::from_static(b"test")),
277            ),
278            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::Integer(42)),
279            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::Counter32(1000)),
280            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 4, 0), Value::Gauge32(500)),
281            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 5, 0), Value::TimeTicks(99999)),
282            VarBind::new(
283                oid!(1, 3, 6, 1, 2, 1, 1, 6, 0),
284                Value::IpAddress([192, 168, 1, 1]),
285            ),
286            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 7, 0), Value::Counter64(u64::MAX)),
287            VarBind::new(
288                oid!(1, 3, 6, 1, 2, 1, 1, 8, 0),
289                Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4)),
290            ),
291            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 9, 0), Value::Null),
292        ];
293
294        let mut buf = EncodeBuf::new();
295        encode_varbind_list(&mut buf, &varbinds);
296        let bytes = buf.finish();
297
298        let mut decoder = Decoder::new(bytes);
299        let decoded = decode_varbind_list(&mut decoder).unwrap();
300
301        assert_eq!(varbinds, decoded);
302    }
303
304    #[test]
305    fn test_null_varbinds_encoding() {
306        let oids = vec![
307            oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
308            oid!(1, 3, 6, 1, 2, 1, 1, 3, 0),
309            oid!(1, 3, 6, 1, 2, 1, 1, 5, 0),
310        ];
311
312        let mut buf = EncodeBuf::new();
313        encode_null_varbinds(&mut buf, &oids);
314        let bytes = buf.finish();
315
316        let mut decoder = Decoder::new(bytes);
317        let decoded = decode_varbind_list(&mut decoder).unwrap();
318
319        assert_eq!(decoded.len(), 3);
320        for (i, vb) in decoded.iter().enumerate() {
321            assert_eq!(vb.oid, oids[i]);
322            assert_eq!(vb.value, Value::Null);
323        }
324    }
325
326    #[test]
327    fn test_null_varbinds_empty() {
328        let oids: Vec<Oid> = vec![];
329
330        let mut buf = EncodeBuf::new();
331        encode_null_varbinds(&mut buf, &oids);
332        let bytes = buf.finish();
333
334        let mut decoder = Decoder::new(bytes);
335        let decoded = decode_varbind_list(&mut decoder).unwrap();
336
337        assert!(decoded.is_empty());
338    }
339
340    // ========================================================================
341    // VarBind Display Tests
342    // ========================================================================
343
344    #[test]
345    fn test_varbind_display() {
346        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::Integer(42));
347        let display = format!("{}", vb);
348        assert!(display.contains("1.3.6.1.2.1.1.1.0"));
349        assert!(display.contains("42"));
350    }
351
352    #[test]
353    fn test_varbind_display_exception() {
354        let vb = VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject);
355        let display = format!("{}", vb);
356        assert!(display.contains("noSuchObject"));
357    }
358
359    // ========================================================================
360    // VarBind::null() Constructor Test
361    // ========================================================================
362
363    #[test]
364    fn test_varbind_null_constructor() {
365        let vb = VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
366        assert_eq!(vb.oid, oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
367        assert_eq!(vb.value, Value::Null);
368    }
369}