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/// Encode a list of VarBinds.
67pub fn encode_varbind_list(buf: &mut EncodeBuf, varbinds: &[VarBind]) {
68    buf.push_sequence(|buf| {
69        // Encode in reverse order since we're using reverse buffer
70        for vb in varbinds.iter().rev() {
71            vb.encode(buf);
72        }
73    });
74}
75
76/// Decode a list of VarBinds.
77pub fn decode_varbind_list(decoder: &mut Decoder) -> Result<Vec<VarBind>> {
78    let mut seq = decoder.read_sequence()?;
79    let mut varbinds = Vec::new();
80
81    while !seq.is_empty() {
82        varbinds.push(VarBind::decode(&mut seq)?);
83    }
84
85    Ok(varbinds)
86}
87
88/// Encode a list of OIDs as VarBinds with NULL values (for GET requests).
89pub fn encode_null_varbinds(buf: &mut EncodeBuf, oids: &[Oid]) {
90    buf.push_sequence(|buf| {
91        for oid in oids.iter().rev() {
92            buf.push_sequence(|buf| {
93                buf.push_null();
94                buf.push_oid(oid);
95            });
96        }
97    });
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use crate::oid;
104    use bytes::Bytes;
105
106    #[test]
107    fn test_varbind_roundtrip() {
108        let vb = VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42));
109
110        let mut buf = EncodeBuf::new();
111        vb.encode(&mut buf);
112        let bytes = buf.finish();
113
114        let mut decoder = Decoder::new(bytes);
115        let decoded = VarBind::decode(&mut decoder).unwrap();
116
117        assert_eq!(vb, decoded);
118    }
119
120    #[test]
121    fn test_varbind_list_roundtrip() {
122        let varbinds = vec![
123            VarBind::new(oid!(1, 3, 6, 1), Value::Integer(1)),
124            VarBind::new(oid!(1, 3, 6, 2), Value::Integer(2)),
125        ];
126
127        let mut buf = EncodeBuf::new();
128        encode_varbind_list(&mut buf, &varbinds);
129        let bytes = buf.finish();
130
131        let mut decoder = Decoder::new(bytes);
132        let decoded = decode_varbind_list(&mut decoder).unwrap();
133
134        assert_eq!(varbinds, decoded);
135    }
136
137    // ========================================================================
138    // Exception Value VarBind Tests
139    // ========================================================================
140
141    #[test]
142    fn test_varbind_no_such_object() {
143        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject);
144
145        let mut buf = EncodeBuf::new();
146        vb.encode(&mut buf);
147        let bytes = buf.finish();
148
149        let mut decoder = Decoder::new(bytes);
150        let decoded = VarBind::decode(&mut decoder).unwrap();
151
152        assert_eq!(vb, decoded);
153        assert!(decoded.value.is_exception());
154    }
155
156    #[test]
157    fn test_varbind_no_such_instance() {
158        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchInstance);
159
160        let mut buf = EncodeBuf::new();
161        vb.encode(&mut buf);
162        let bytes = buf.finish();
163
164        let mut decoder = Decoder::new(bytes);
165        let decoded = VarBind::decode(&mut decoder).unwrap();
166
167        assert_eq!(vb, decoded);
168        assert!(decoded.value.is_exception());
169    }
170
171    #[test]
172    fn test_varbind_end_of_mib_view() {
173        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::EndOfMibView);
174
175        let mut buf = EncodeBuf::new();
176        vb.encode(&mut buf);
177        let bytes = buf.finish();
178
179        let mut decoder = Decoder::new(bytes);
180        let decoded = VarBind::decode(&mut decoder).unwrap();
181
182        assert_eq!(vb, decoded);
183        assert!(decoded.value.is_exception());
184    }
185
186    // ========================================================================
187    // VarBind List Edge Cases
188    // ========================================================================
189
190    #[test]
191    fn test_varbind_list_empty() {
192        let varbinds: Vec<VarBind> = vec![];
193
194        let mut buf = EncodeBuf::new();
195        encode_varbind_list(&mut buf, &varbinds);
196        let bytes = buf.finish();
197
198        let mut decoder = Decoder::new(bytes);
199        let decoded = decode_varbind_list(&mut decoder).unwrap();
200
201        assert!(decoded.is_empty());
202    }
203
204    #[test]
205    fn test_varbind_list_single() {
206        let varbinds = vec![VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42))];
207
208        let mut buf = EncodeBuf::new();
209        encode_varbind_list(&mut buf, &varbinds);
210        let bytes = buf.finish();
211
212        let mut decoder = Decoder::new(bytes);
213        let decoded = decode_varbind_list(&mut decoder).unwrap();
214
215        assert_eq!(varbinds, decoded);
216    }
217
218    #[test]
219    fn test_varbind_list_with_exceptions() {
220        let varbinds = vec![
221            VarBind::new(
222                oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
223                Value::OctetString(Bytes::from_static(b"Linux router")),
224            ),
225            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 99, 0), Value::NoSuchObject),
226            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::TimeTicks(123456)),
227            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 100, 0), Value::NoSuchInstance),
228        ];
229
230        let mut buf = EncodeBuf::new();
231        encode_varbind_list(&mut buf, &varbinds);
232        let bytes = buf.finish();
233
234        let mut decoder = Decoder::new(bytes);
235        let decoded = decode_varbind_list(&mut decoder).unwrap();
236
237        assert_eq!(varbinds, decoded);
238        assert!(!decoded[0].value.is_exception());
239        assert!(decoded[1].value.is_exception());
240        assert!(!decoded[2].value.is_exception());
241        assert!(decoded[3].value.is_exception());
242    }
243
244    #[test]
245    fn test_varbind_list_all_exceptions() {
246        let varbinds = vec![
247            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject),
248            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::NoSuchInstance),
249            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::EndOfMibView),
250        ];
251
252        let mut buf = EncodeBuf::new();
253        encode_varbind_list(&mut buf, &varbinds);
254        let bytes = buf.finish();
255
256        let mut decoder = Decoder::new(bytes);
257        let decoded = decode_varbind_list(&mut decoder).unwrap();
258
259        assert_eq!(varbinds, decoded);
260        assert!(decoded.iter().all(|vb| vb.value.is_exception()));
261    }
262
263    #[test]
264    fn test_varbind_list_mixed_value_types() {
265        let varbinds = vec![
266            VarBind::new(
267                oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
268                Value::OctetString(Bytes::from_static(b"test")),
269            ),
270            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::Integer(42)),
271            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::Counter32(1000)),
272            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 4, 0), Value::Gauge32(500)),
273            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 5, 0), Value::TimeTicks(99999)),
274            VarBind::new(
275                oid!(1, 3, 6, 1, 2, 1, 1, 6, 0),
276                Value::IpAddress([192, 168, 1, 1]),
277            ),
278            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 7, 0), Value::Counter64(u64::MAX)),
279            VarBind::new(
280                oid!(1, 3, 6, 1, 2, 1, 1, 8, 0),
281                Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4)),
282            ),
283            VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 9, 0), Value::Null),
284        ];
285
286        let mut buf = EncodeBuf::new();
287        encode_varbind_list(&mut buf, &varbinds);
288        let bytes = buf.finish();
289
290        let mut decoder = Decoder::new(bytes);
291        let decoded = decode_varbind_list(&mut decoder).unwrap();
292
293        assert_eq!(varbinds, decoded);
294    }
295
296    #[test]
297    fn test_null_varbinds_encoding() {
298        let oids = vec![
299            oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
300            oid!(1, 3, 6, 1, 2, 1, 1, 3, 0),
301            oid!(1, 3, 6, 1, 2, 1, 1, 5, 0),
302        ];
303
304        let mut buf = EncodeBuf::new();
305        encode_null_varbinds(&mut buf, &oids);
306        let bytes = buf.finish();
307
308        let mut decoder = Decoder::new(bytes);
309        let decoded = decode_varbind_list(&mut decoder).unwrap();
310
311        assert_eq!(decoded.len(), 3);
312        for (i, vb) in decoded.iter().enumerate() {
313            assert_eq!(vb.oid, oids[i]);
314            assert_eq!(vb.value, Value::Null);
315        }
316    }
317
318    #[test]
319    fn test_null_varbinds_empty() {
320        let oids: Vec<Oid> = vec![];
321
322        let mut buf = EncodeBuf::new();
323        encode_null_varbinds(&mut buf, &oids);
324        let bytes = buf.finish();
325
326        let mut decoder = Decoder::new(bytes);
327        let decoded = decode_varbind_list(&mut decoder).unwrap();
328
329        assert!(decoded.is_empty());
330    }
331
332    // ========================================================================
333    // VarBind Display Tests
334    // ========================================================================
335
336    #[test]
337    fn test_varbind_display() {
338        let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::Integer(42));
339        let display = format!("{}", vb);
340        assert!(display.contains("1.3.6.1.2.1.1.1.0"));
341        assert!(display.contains("42"));
342    }
343
344    #[test]
345    fn test_varbind_display_exception() {
346        let vb = VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject);
347        let display = format!("{}", vb);
348        assert!(display.contains("noSuchObject"));
349    }
350
351    // ========================================================================
352    // VarBind::null() Constructor Test
353    // ========================================================================
354
355    #[test]
356    fn test_varbind_null_constructor() {
357        let vb = VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
358        assert_eq!(vb.oid, oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
359        assert_eq!(vb.value, Value::Null);
360    }
361}