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]
205 fn test_varbind_zero_length_oid_end_of_mib_view() {
206 let bytes = bytes::Bytes::from_static(&[0x30, 0x04, 0x06, 0x00, 0x82, 0x00]);
214 let mut decoder = Decoder::new(bytes);
215 let vb = VarBind::decode(&mut decoder).unwrap();
216 assert!(vb.oid.is_empty());
217 assert_eq!(vb.value, Value::EndOfMibView);
218 }
219
220 #[test]
221 fn test_varbind_octetstring_length_exceeds_sequence() {
222 let bytes = bytes::Bytes::from_static(&[
233 0x30, 0x0e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0x04, 0x03, 0x61, 0x62, ]);
237 let mut decoder = Decoder::new(bytes);
238 let vb = VarBind::decode(&mut decoder).unwrap();
239 assert_eq!(vb.oid, crate::oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
240 assert_eq!(
241 vb.value,
242 Value::OctetString(bytes::Bytes::from_static(b"ab"))
243 );
244 }
245
246 #[test]
251 fn test_varbind_list_empty() {
252 let varbinds: Vec<VarBind> = vec![];
253
254 let mut buf = EncodeBuf::new();
255 encode_varbind_list(&mut buf, &varbinds);
256 let bytes = buf.finish();
257
258 let mut decoder = Decoder::new(bytes);
259 let decoded = decode_varbind_list(&mut decoder).unwrap();
260
261 assert!(decoded.is_empty());
262 }
263
264 #[test]
265 fn test_varbind_list_single() {
266 let varbinds = vec![VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42))];
267
268 let mut buf = EncodeBuf::new();
269 encode_varbind_list(&mut buf, &varbinds);
270 let bytes = buf.finish();
271
272 let mut decoder = Decoder::new(bytes);
273 let decoded = decode_varbind_list(&mut decoder).unwrap();
274
275 assert_eq!(varbinds, decoded);
276 }
277
278 #[test]
279 fn test_varbind_list_with_exceptions() {
280 let varbinds = vec![
281 VarBind::new(
282 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
283 Value::OctetString(Bytes::from_static(b"Linux router")),
284 ),
285 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 99, 0), Value::NoSuchObject),
286 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::TimeTicks(123456)),
287 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 100, 0), Value::NoSuchInstance),
288 ];
289
290 let mut buf = EncodeBuf::new();
291 encode_varbind_list(&mut buf, &varbinds);
292 let bytes = buf.finish();
293
294 let mut decoder = Decoder::new(bytes);
295 let decoded = decode_varbind_list(&mut decoder).unwrap();
296
297 assert_eq!(varbinds, decoded);
298 assert!(!decoded[0].value.is_exception());
299 assert!(decoded[1].value.is_exception());
300 assert!(!decoded[2].value.is_exception());
301 assert!(decoded[3].value.is_exception());
302 }
303
304 #[test]
305 fn test_varbind_list_all_exceptions() {
306 let varbinds = vec![
307 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject),
308 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::NoSuchInstance),
309 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::EndOfMibView),
310 ];
311
312 let mut buf = EncodeBuf::new();
313 encode_varbind_list(&mut buf, &varbinds);
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!(varbinds, decoded);
320 assert!(decoded.iter().all(|vb| vb.value.is_exception()));
321 }
322
323 #[test]
324 fn test_varbind_list_mixed_value_types() {
325 let varbinds = vec![
326 VarBind::new(
327 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
328 Value::OctetString(Bytes::from_static(b"test")),
329 ),
330 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::Integer(42)),
331 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::Counter32(1000)),
332 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 4, 0), Value::Gauge32(500)),
333 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 5, 0), Value::TimeTicks(99999)),
334 VarBind::new(
335 oid!(1, 3, 6, 1, 2, 1, 1, 6, 0),
336 Value::IpAddress([192, 168, 1, 1]),
337 ),
338 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 7, 0), Value::Counter64(u64::MAX)),
339 VarBind::new(
340 oid!(1, 3, 6, 1, 2, 1, 1, 8, 0),
341 Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4)),
342 ),
343 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 9, 0), Value::Null),
344 ];
345
346 let mut buf = EncodeBuf::new();
347 encode_varbind_list(&mut buf, &varbinds);
348 let bytes = buf.finish();
349
350 let mut decoder = Decoder::new(bytes);
351 let decoded = decode_varbind_list(&mut decoder).unwrap();
352
353 assert_eq!(varbinds, decoded);
354 }
355
356 #[test]
357 fn test_null_varbinds_encoding() {
358 let oids = vec![
359 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
360 oid!(1, 3, 6, 1, 2, 1, 1, 3, 0),
361 oid!(1, 3, 6, 1, 2, 1, 1, 5, 0),
362 ];
363
364 let mut buf = EncodeBuf::new();
365 encode_null_varbinds(&mut buf, &oids);
366 let bytes = buf.finish();
367
368 let mut decoder = Decoder::new(bytes);
369 let decoded = decode_varbind_list(&mut decoder).unwrap();
370
371 assert_eq!(decoded.len(), 3);
372 for (i, vb) in decoded.iter().enumerate() {
373 assert_eq!(vb.oid, oids[i]);
374 assert_eq!(vb.value, Value::Null);
375 }
376 }
377
378 #[test]
379 fn test_null_varbinds_empty() {
380 let oids: Vec<Oid> = vec![];
381
382 let mut buf = EncodeBuf::new();
383 encode_null_varbinds(&mut buf, &oids);
384 let bytes = buf.finish();
385
386 let mut decoder = Decoder::new(bytes);
387 let decoded = decode_varbind_list(&mut decoder).unwrap();
388
389 assert!(decoded.is_empty());
390 }
391
392 #[test]
397 fn test_varbind_display() {
398 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::Integer(42));
399 let display = format!("{}", vb);
400 assert!(display.contains("1.3.6.1.2.1.1.1.0"));
401 assert!(display.contains("42"));
402 }
403
404 #[test]
405 fn test_varbind_display_exception() {
406 let vb = VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject);
407 let display = format!("{}", vb);
408 assert!(display.contains("noSuchObject"));
409 }
410
411 #[test]
416 fn test_varbind_null_constructor() {
417 let vb = VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
418 assert_eq!(vb.oid, oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
419 assert_eq!(vb.value, Value::Null);
420 }
421
422 fn verify_encoded_size(vb: &VarBind) {
428 let mut buf = EncodeBuf::new();
429 vb.encode(&mut buf);
430 let actual = buf.len();
431 let computed = vb.encoded_size();
432 assert_eq!(
433 computed, actual,
434 "encoded_size mismatch for {:?}: computed={}, actual={}",
435 vb, computed, actual
436 );
437 }
438
439 #[test]
440 fn test_encoded_size_null() {
441 verify_encoded_size(&VarBind::null(oid!(1, 3, 6, 1)));
442 verify_encoded_size(&VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)));
443 }
444
445 #[test]
446 fn test_encoded_size_integer() {
447 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(0)));
448 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(127)));
449 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(128)));
450 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(-1)));
451 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(i32::MAX)));
452 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Integer(i32::MIN)));
453 }
454
455 #[test]
456 fn test_encoded_size_octet_string() {
457 verify_encoded_size(&VarBind::new(
458 oid!(1, 3, 6, 1),
459 Value::OctetString(Bytes::new()),
460 ));
461 verify_encoded_size(&VarBind::new(
462 oid!(1, 3, 6, 1),
463 Value::OctetString(Bytes::from_static(b"hello world")),
464 ));
465 verify_encoded_size(&VarBind::new(
467 oid!(1, 3, 6, 1),
468 Value::OctetString(Bytes::from(vec![0u8; 200])),
469 ));
470 }
471
472 #[test]
473 fn test_encoded_size_counters() {
474 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter32(0)));
475 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter32(u32::MAX)));
476 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Gauge32(12345)));
477 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::TimeTicks(99999)));
478 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter64(0)));
479 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::Counter64(u64::MAX)));
480 }
481
482 #[test]
483 fn test_encoded_size_oid_value() {
484 verify_encoded_size(&VarBind::new(
485 oid!(1, 3, 6, 1, 2, 1, 1, 2, 0),
486 Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4, 1, 9999)),
487 ));
488 }
489
490 #[test]
491 fn test_encoded_size_exceptions() {
492 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject));
493 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchInstance));
494 verify_encoded_size(&VarBind::new(oid!(1, 3, 6, 1), Value::EndOfMibView));
495 }
496
497 #[test]
498 fn test_encoded_size_ip_address() {
499 verify_encoded_size(&VarBind::new(
500 oid!(1, 3, 6, 1),
501 Value::IpAddress([192, 168, 1, 1]),
502 ));
503 }
504
505 mod proptests {
506 use super::*;
507 use crate::oid::Oid;
508 use proptest::prelude::*;
509
510 fn arb_oid() -> impl Strategy<Value = Oid> {
511 (0u32..3, 0u32..40, prop::collection::vec(0u32..10000, 0..8)).prop_map(
513 |(arc1, arc2, rest)| {
514 let mut arcs = vec![arc1, arc2];
515 arcs.extend(rest);
516 Oid::from_slice(&arcs)
517 },
518 )
519 }
520
521 fn arb_value() -> impl Strategy<Value = Value> {
522 prop_oneof![
523 any::<i32>().prop_map(Value::Integer),
524 prop::collection::vec(any::<u8>(), 0..256)
525 .prop_map(|v| Value::OctetString(Bytes::from(v))),
526 Just(Value::Null),
527 arb_oid().prop_map(Value::ObjectIdentifier),
528 any::<[u8; 4]>().prop_map(Value::IpAddress),
529 any::<u32>().prop_map(Value::Counter32),
530 any::<u32>().prop_map(Value::Gauge32),
531 any::<u32>().prop_map(Value::TimeTicks),
532 any::<u64>().prop_map(Value::Counter64),
533 Just(Value::NoSuchObject),
534 Just(Value::NoSuchInstance),
535 Just(Value::EndOfMibView),
536 (any::<u8>(), prop::collection::vec(any::<u8>(), 0..256)).prop_map(
537 |(tag, data)| Value::Unknown {
538 tag,
539 data: Bytes::from(data),
540 }
541 ),
542 ]
543 }
544
545 proptest! {
546 #[test]
547 fn encoded_size_matches_encoding(
548 oid in arb_oid(),
549 value in arb_value()
550 ) {
551 let vb = VarBind::new(oid, value);
552 let mut buf = EncodeBuf::new();
553 vb.encode(&mut buf);
554 prop_assert_eq!(
555 vb.encoded_size(),
556 buf.len(),
557 "encoded_size mismatch: computed={}, actual={}",
558 vb.encoded_size(),
559 buf.len()
560 );
561 }
562 }
563 }
564}