1use crate::ber::{Decoder, EncodeBuf};
6use crate::error::Result;
7use crate::oid::Oid;
8use crate::value::Value;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub struct VarBind {
13 pub oid: Oid,
15 pub value: Value,
17}
18
19impl VarBind {
20 pub fn new(oid: Oid, value: Value) -> Self {
22 Self { oid, value }
23 }
24
25 pub fn null(oid: Oid) -> Self {
27 Self {
28 oid,
29 value: Value::Null,
30 }
31 }
32
33 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 pub fn encoded_size(&self) -> usize {
46 use crate::ber::length_encoded_len;
47
48 let oid_len = self.oid.ber_encoded_len();
50 let value_len = self.value.ber_encoded_len();
51 let content_len = oid_len + value_len;
52
53 1 + length_encoded_len(content_len) + content_len
55 }
56
57 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
59 let mut seq = decoder.read_sequence()?;
60 let oid = seq.read_oid()?;
61 let value = Value::decode(&mut seq)?;
62 Ok(VarBind { oid, value })
63 }
64}
65
66impl std::fmt::Display for VarBind {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(f, "{} = {}", self.oid, self.value)
69 }
70}
71
72pub fn encode_varbind_list(buf: &mut EncodeBuf, varbinds: &[VarBind]) {
77 buf.push_sequence(|buf| {
78 for vb in varbinds.iter().rev() {
80 vb.encode(buf);
81 }
82 });
83}
84
85pub fn decode_varbind_list(decoder: &mut Decoder) -> Result<Vec<VarBind>> {
89 let mut seq = decoder.read_sequence()?;
90
91 let estimated_capacity = (seq.remaining() / 16).max(1);
94 let mut varbinds = Vec::with_capacity(estimated_capacity);
95
96 while !seq.is_empty() {
97 varbinds.push(VarBind::decode(&mut seq)?);
98 }
99
100 Ok(varbinds)
101}
102
103pub fn encode_null_varbinds(buf: &mut EncodeBuf, oids: &[Oid]) {
108 buf.push_sequence(|buf| {
109 for oid in oids.iter().rev() {
110 buf.push_sequence(|buf| {
111 buf.push_null();
112 buf.push_oid(oid);
113 });
114 }
115 });
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::oid;
122 use bytes::Bytes;
123
124 #[test]
125 fn test_varbind_roundtrip() {
126 let vb = VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42));
127
128 let mut buf = EncodeBuf::new();
129 vb.encode(&mut buf);
130 let bytes = buf.finish();
131
132 let mut decoder = Decoder::new(bytes);
133 let decoded = VarBind::decode(&mut decoder).unwrap();
134
135 assert_eq!(vb, decoded);
136 }
137
138 #[test]
139 fn test_varbind_list_roundtrip() {
140 let varbinds = vec![
141 VarBind::new(oid!(1, 3, 6, 1), Value::Integer(1)),
142 VarBind::new(oid!(1, 3, 6, 2), Value::Integer(2)),
143 ];
144
145 let mut buf = EncodeBuf::new();
146 encode_varbind_list(&mut buf, &varbinds);
147 let bytes = buf.finish();
148
149 let mut decoder = Decoder::new(bytes);
150 let decoded = decode_varbind_list(&mut decoder).unwrap();
151
152 assert_eq!(varbinds, decoded);
153 }
154
155 #[test]
160 fn test_varbind_no_such_object() {
161 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject);
162
163 let mut buf = EncodeBuf::new();
164 vb.encode(&mut buf);
165 let bytes = buf.finish();
166
167 let mut decoder = Decoder::new(bytes);
168 let decoded = VarBind::decode(&mut decoder).unwrap();
169
170 assert_eq!(vb, decoded);
171 assert!(decoded.value.is_exception());
172 }
173
174 #[test]
175 fn test_varbind_no_such_instance() {
176 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchInstance);
177
178 let mut buf = EncodeBuf::new();
179 vb.encode(&mut buf);
180 let bytes = buf.finish();
181
182 let mut decoder = Decoder::new(bytes);
183 let decoded = VarBind::decode(&mut decoder).unwrap();
184
185 assert_eq!(vb, decoded);
186 assert!(decoded.value.is_exception());
187 }
188
189 #[test]
190 fn test_varbind_end_of_mib_view() {
191 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::EndOfMibView);
192
193 let mut buf = EncodeBuf::new();
194 vb.encode(&mut buf);
195 let bytes = buf.finish();
196
197 let mut decoder = Decoder::new(bytes);
198 let decoded = VarBind::decode(&mut decoder).unwrap();
199
200 assert_eq!(vb, decoded);
201 assert!(decoded.value.is_exception());
202 }
203
204 #[test]
209 fn test_varbind_list_empty() {
210 let varbinds: Vec<VarBind> = vec![];
211
212 let mut buf = EncodeBuf::new();
213 encode_varbind_list(&mut buf, &varbinds);
214 let bytes = buf.finish();
215
216 let mut decoder = Decoder::new(bytes);
217 let decoded = decode_varbind_list(&mut decoder).unwrap();
218
219 assert!(decoded.is_empty());
220 }
221
222 #[test]
223 fn test_varbind_list_single() {
224 let varbinds = vec![VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42))];
225
226 let mut buf = EncodeBuf::new();
227 encode_varbind_list(&mut buf, &varbinds);
228 let bytes = buf.finish();
229
230 let mut decoder = Decoder::new(bytes);
231 let decoded = decode_varbind_list(&mut decoder).unwrap();
232
233 assert_eq!(varbinds, decoded);
234 }
235
236 #[test]
237 fn test_varbind_list_with_exceptions() {
238 let varbinds = vec![
239 VarBind::new(
240 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
241 Value::OctetString(Bytes::from_static(b"Linux router")),
242 ),
243 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 99, 0), Value::NoSuchObject),
244 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::TimeTicks(123456)),
245 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 100, 0), Value::NoSuchInstance),
246 ];
247
248 let mut buf = EncodeBuf::new();
249 encode_varbind_list(&mut buf, &varbinds);
250 let bytes = buf.finish();
251
252 let mut decoder = Decoder::new(bytes);
253 let decoded = decode_varbind_list(&mut decoder).unwrap();
254
255 assert_eq!(varbinds, decoded);
256 assert!(!decoded[0].value.is_exception());
257 assert!(decoded[1].value.is_exception());
258 assert!(!decoded[2].value.is_exception());
259 assert!(decoded[3].value.is_exception());
260 }
261
262 #[test]
263 fn test_varbind_list_all_exceptions() {
264 let varbinds = vec![
265 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject),
266 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::NoSuchInstance),
267 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::EndOfMibView),
268 ];
269
270 let mut buf = EncodeBuf::new();
271 encode_varbind_list(&mut buf, &varbinds);
272 let bytes = buf.finish();
273
274 let mut decoder = Decoder::new(bytes);
275 let decoded = decode_varbind_list(&mut decoder).unwrap();
276
277 assert_eq!(varbinds, decoded);
278 assert!(decoded.iter().all(|vb| vb.value.is_exception()));
279 }
280
281 #[test]
282 fn test_varbind_list_mixed_value_types() {
283 let varbinds = vec![
284 VarBind::new(
285 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
286 Value::OctetString(Bytes::from_static(b"test")),
287 ),
288 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::Integer(42)),
289 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::Counter32(1000)),
290 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 4, 0), Value::Gauge32(500)),
291 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 5, 0), Value::TimeTicks(99999)),
292 VarBind::new(
293 oid!(1, 3, 6, 1, 2, 1, 1, 6, 0),
294 Value::IpAddress([192, 168, 1, 1]),
295 ),
296 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 7, 0), Value::Counter64(u64::MAX)),
297 VarBind::new(
298 oid!(1, 3, 6, 1, 2, 1, 1, 8, 0),
299 Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4)),
300 ),
301 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 9, 0), Value::Null),
302 ];
303
304 let mut buf = EncodeBuf::new();
305 encode_varbind_list(&mut buf, &varbinds);
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!(varbinds, decoded);
312 }
313
314 #[test]
315 fn test_null_varbinds_encoding() {
316 let oids = vec![
317 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
318 oid!(1, 3, 6, 1, 2, 1, 1, 3, 0),
319 oid!(1, 3, 6, 1, 2, 1, 1, 5, 0),
320 ];
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_eq!(decoded.len(), 3);
330 for (i, vb) in decoded.iter().enumerate() {
331 assert_eq!(vb.oid, oids[i]);
332 assert_eq!(vb.value, Value::Null);
333 }
334 }
335
336 #[test]
337 fn test_null_varbinds_empty() {
338 let oids: Vec<Oid> = vec![];
339
340 let mut buf = EncodeBuf::new();
341 encode_null_varbinds(&mut buf, &oids);
342 let bytes = buf.finish();
343
344 let mut decoder = Decoder::new(bytes);
345 let decoded = decode_varbind_list(&mut decoder).unwrap();
346
347 assert!(decoded.is_empty());
348 }
349
350 #[test]
355 fn test_varbind_display() {
356 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::Integer(42));
357 let display = format!("{}", vb);
358 assert!(display.contains("1.3.6.1.2.1.1.1.0"));
359 assert!(display.contains("42"));
360 }
361
362 #[test]
363 fn test_varbind_display_exception() {
364 let vb = VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject);
365 let display = format!("{}", vb);
366 assert!(display.contains("noSuchObject"));
367 }
368
369 #[test]
374 fn test_varbind_null_constructor() {
375 let vb = VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
376 assert_eq!(vb.oid, oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
377 assert_eq!(vb.value, Value::Null);
378 }
379
380 fn verify_encoded_size(vb: &VarBind) {
386 let mut buf = EncodeBuf::new();
387 vb.encode(&mut buf);
388 let actual = buf.len();
389 let computed = vb.encoded_size();
390 assert_eq!(
391 computed, actual,
392 "encoded_size mismatch for {:?}: computed={}, actual={}",
393 vb, computed, actual
394 );
395 }
396
397 #[test]
398 fn test_encoded_size_null() {
399 verify_encoded_size(&VarBind::null(oid!(1, 3, 6, 1)));
400 verify_encoded_size(&VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)));
401 }
402
403 #[test]
404 fn test_encoded_size_integer() {
405 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(0)));
406 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(127)));
407 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(128)));
408 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(-1)));
409 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(i32::MAX)));
410 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(i32::MIN)));
411 }
412
413 #[test]
414 fn test_encoded_size_octet_string() {
415 verify_encoded_size(&VarBind::new(
416 oid!(1, 3, 6, 1),
417 Value::OctetString(Bytes::new()),
418 ));
419 verify_encoded_size(&VarBind::new(
420 oid!(1, 3, 6, 1),
421 Value::OctetString(Bytes::from_static(b"hello world")),
422 ));
423 verify_encoded_size(&VarBind::new(
425 oid!(1, 3, 6, 1),
426 Value::OctetString(Bytes::from(vec![0u8; 200])),
427 ));
428 }
429
430 #[test]
431 fn test_encoded_size_counters() {
432 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter32(0)));
433 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter32(u32::MAX)));
434 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Gauge32(12345)));
435 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::TimeTicks(99999)));
436 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter64(0)));
437 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter64(u64::MAX)));
438 }
439
440 #[test]
441 fn test_encoded_size_oid_value() {
442 verify_encoded_size(&VarBind::new(
443 oid!(1, 3, 6, 1, 2, 1, 1, 2, 0),
444 Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4, 1, 9999)),
445 ));
446 }
447
448 #[test]
449 fn test_encoded_size_exceptions() {
450 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject));
451 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchInstance));
452 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::EndOfMibView));
453 }
454
455 #[test]
456 fn test_encoded_size_ip_address() {
457 verify_encoded_size(&VarBind::new(
458 oid!(1, 3, 6, 1),
459 Value::IpAddress([192, 168, 1, 1]),
460 ));
461 }
462
463 mod proptests {
464 use super::*;
465 use crate::oid::Oid;
466 use proptest::prelude::*;
467
468 fn arb_oid() -> impl Strategy<Value = Oid> {
469 (0u32..3, 0u32..40, prop::collection::vec(0u32..10000, 0..8)).prop_map(
471 |(arc1, arc2, rest)| {
472 let mut arcs = vec![arc1, arc2];
473 arcs.extend(rest);
474 Oid::from_slice(&arcs)
475 },
476 )
477 }
478
479 fn arb_value() -> impl Strategy<Value = Value> {
480 prop_oneof![
481 any::<i32>().prop_map(Value::Integer),
482 prop::collection::vec(any::<u8>(), 0..256)
483 .prop_map(|v| Value::OctetString(Bytes::from(v))),
484 Just(Value::Null),
485 arb_oid().prop_map(Value::ObjectIdentifier),
486 any::<[u8; 4]>().prop_map(Value::IpAddress),
487 any::<u32>().prop_map(Value::Counter32),
488 any::<u32>().prop_map(Value::Gauge32),
489 any::<u32>().prop_map(Value::TimeTicks),
490 any::<u64>().prop_map(Value::Counter64),
491 Just(Value::NoSuchObject),
492 Just(Value::NoSuchInstance),
493 Just(Value::EndOfMibView),
494 ]
495 }
496
497 proptest! {
498 #[test]
499 fn encoded_size_matches_encoding(
500 oid in arb_oid(),
501 value in arb_value()
502 ) {
503 let vb = VarBind::new(oid, value);
504 let mut buf = EncodeBuf::new();
505 vb.encode(&mut buf);
506 prop_assert_eq!(
507 vb.encoded_size(),
508 buf.len(),
509 "encoded_size mismatch: computed={}, actual={}",
510 vb.encoded_size(),
511 buf.len()
512 );
513 }
514 }
515 }
516}