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 #[must_use]
27 pub fn null(oid: Oid) -> Self {
28 Self {
29 oid,
30 value: Value::Null,
31 }
32 }
33
34 pub fn encode(&self, buf: &mut EncodeBuf) {
36 buf.push_sequence(|buf| {
37 self.value.encode(buf);
38 buf.push_oid(&self.oid);
39 });
40 }
41
42 pub fn encoded_size(&self) -> usize {
47 use crate::ber::length_encoded_len;
48
49 let oid_len = self.oid.ber_encoded_size();
51 let value_len = self.value.ber_encoded_size();
52 let content_len = oid_len + value_len;
53
54 1 + length_encoded_len(content_len) + content_len
56 }
57
58 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
60 let mut seq = decoder.read_sequence()?;
61 let oid = seq.read_oid()?;
62 let value = Value::decode(&mut seq)?;
63 Ok(VarBind { oid, value })
64 }
65}
66
67impl std::fmt::Display for VarBind {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "{} = {}", self.oid, self.value)
70 }
71}
72
73pub fn encode_varbind_list(buf: &mut EncodeBuf, varbinds: &[VarBind]) {
78 buf.push_sequence(|buf| {
79 for vb in varbinds.iter().rev() {
81 vb.encode(buf);
82 }
83 });
84}
85
86pub fn decode_varbind_list(decoder: &mut Decoder) -> Result<Vec<VarBind>> {
90 let mut seq = decoder.read_sequence()?;
91
92 let estimated_capacity = (seq.remaining() / 16).max(1);
95 let mut varbinds = Vec::with_capacity(estimated_capacity);
96
97 while !seq.is_empty() {
98 varbinds.push(VarBind::decode(&mut seq)?);
99 }
100
101 Ok(varbinds)
102}
103
104pub fn encode_null_varbinds(buf: &mut EncodeBuf, oids: &[Oid]) {
109 buf.push_sequence(|buf| {
110 for oid in oids.iter().rev() {
111 buf.push_sequence(|buf| {
112 buf.push_null();
113 buf.push_oid(oid);
114 });
115 }
116 });
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::oid;
123 use bytes::Bytes;
124
125 #[test]
126 fn test_varbind_roundtrip() {
127 let vb = VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42));
128
129 let mut buf = EncodeBuf::new();
130 vb.encode(&mut buf);
131 let bytes = buf.finish();
132
133 let mut decoder = Decoder::new(bytes);
134 let decoded = VarBind::decode(&mut decoder).unwrap();
135
136 assert_eq!(vb, decoded);
137 }
138
139 #[test]
140 fn test_varbind_list_roundtrip() {
141 let varbinds = vec![
142 VarBind::new(oid!(1, 3, 6, 1), Value::Integer(1)),
143 VarBind::new(oid!(1, 3, 6, 2), Value::Integer(2)),
144 ];
145
146 let mut buf = EncodeBuf::new();
147 encode_varbind_list(&mut buf, &varbinds);
148 let bytes = buf.finish();
149
150 let mut decoder = Decoder::new(bytes);
151 let decoded = decode_varbind_list(&mut decoder).unwrap();
152
153 assert_eq!(varbinds, decoded);
154 }
155
156 #[test]
161 fn test_varbind_no_such_object() {
162 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject);
163
164 let mut buf = EncodeBuf::new();
165 vb.encode(&mut buf);
166 let bytes = buf.finish();
167
168 let mut decoder = Decoder::new(bytes);
169 let decoded = VarBind::decode(&mut decoder).unwrap();
170
171 assert_eq!(vb, decoded);
172 assert!(decoded.value.is_exception());
173 }
174
175 #[test]
176 fn test_varbind_no_such_instance() {
177 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchInstance);
178
179 let mut buf = EncodeBuf::new();
180 vb.encode(&mut buf);
181 let bytes = buf.finish();
182
183 let mut decoder = Decoder::new(bytes);
184 let decoded = VarBind::decode(&mut decoder).unwrap();
185
186 assert_eq!(vb, decoded);
187 assert!(decoded.value.is_exception());
188 }
189
190 #[test]
191 fn test_varbind_end_of_mib_view() {
192 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::EndOfMibView);
193
194 let mut buf = EncodeBuf::new();
195 vb.encode(&mut buf);
196 let bytes = buf.finish();
197
198 let mut decoder = Decoder::new(bytes);
199 let decoded = VarBind::decode(&mut decoder).unwrap();
200
201 assert_eq!(vb, decoded);
202 assert!(decoded.value.is_exception());
203 }
204
205 #[test]
206 fn test_varbind_zero_length_oid_end_of_mib_view() {
207 let bytes = bytes::Bytes::from_static(&[0x30, 0x04, 0x06, 0x00, 0x82, 0x00]);
215 let mut decoder = Decoder::new(bytes);
216 let vb = VarBind::decode(&mut decoder).unwrap();
217 assert!(vb.oid.is_empty());
218 assert_eq!(vb.value, Value::EndOfMibView);
219 }
220
221 #[test]
222 fn test_varbind_octetstring_length_exceeds_sequence() {
223 let bytes = bytes::Bytes::from_static(&[
234 0x30, 0x0e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0x04, 0x03, 0x61, 0x62, ]);
238 let mut decoder = Decoder::new(bytes);
239 let vb = VarBind::decode(&mut decoder).unwrap();
240 assert_eq!(vb.oid, crate::oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
241 assert_eq!(
242 vb.value,
243 Value::OctetString(bytes::Bytes::from_static(b"ab"))
244 );
245 }
246
247 #[test]
252 fn test_varbind_list_empty() {
253 let varbinds: Vec<VarBind> = vec![];
254
255 let mut buf = EncodeBuf::new();
256 encode_varbind_list(&mut buf, &varbinds);
257 let bytes = buf.finish();
258
259 let mut decoder = Decoder::new(bytes);
260 let decoded = decode_varbind_list(&mut decoder).unwrap();
261
262 assert!(decoded.is_empty());
263 }
264
265 #[test]
266 fn test_varbind_list_single() {
267 let varbinds = vec![VarBind::new(oid!(1, 3, 6, 1), Value::Integer(42))];
268
269 let mut buf = EncodeBuf::new();
270 encode_varbind_list(&mut buf, &varbinds);
271 let bytes = buf.finish();
272
273 let mut decoder = Decoder::new(bytes);
274 let decoded = decode_varbind_list(&mut decoder).unwrap();
275
276 assert_eq!(varbinds, decoded);
277 }
278
279 #[test]
280 fn test_varbind_list_with_exceptions() {
281 let varbinds = vec![
282 VarBind::new(
283 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
284 Value::OctetString(Bytes::from_static(b"Linux router")),
285 ),
286 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 99, 0), Value::NoSuchObject),
287 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::TimeTicks(123_456)),
288 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 100, 0), Value::NoSuchInstance),
289 ];
290
291 let mut buf = EncodeBuf::new();
292 encode_varbind_list(&mut buf, &varbinds);
293 let bytes = buf.finish();
294
295 let mut decoder = Decoder::new(bytes);
296 let decoded = decode_varbind_list(&mut decoder).unwrap();
297
298 assert_eq!(varbinds, decoded);
299 assert!(!decoded[0].value.is_exception());
300 assert!(decoded[1].value.is_exception());
301 assert!(!decoded[2].value.is_exception());
302 assert!(decoded[3].value.is_exception());
303 }
304
305 #[test]
306 fn test_varbind_list_all_exceptions() {
307 let varbinds = vec![
308 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::NoSuchObject),
309 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::NoSuchInstance),
310 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::EndOfMibView),
311 ];
312
313 let mut buf = EncodeBuf::new();
314 encode_varbind_list(&mut buf, &varbinds);
315 let bytes = buf.finish();
316
317 let mut decoder = Decoder::new(bytes);
318 let decoded = decode_varbind_list(&mut decoder).unwrap();
319
320 assert_eq!(varbinds, decoded);
321 assert!(decoded.iter().all(|vb| vb.value.is_exception()));
322 }
323
324 #[test]
325 fn test_varbind_list_mixed_value_types() {
326 let varbinds = vec![
327 VarBind::new(
328 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
329 Value::OctetString(Bytes::from_static(b"test")),
330 ),
331 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 2, 0), Value::Integer(42)),
332 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::Counter32(1000)),
333 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 4, 0), Value::Gauge32(500)),
334 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 5, 0), Value::TimeTicks(99999)),
335 VarBind::new(
336 oid!(1, 3, 6, 1, 2, 1, 1, 6, 0),
337 Value::IpAddress([192, 168, 1, 1]),
338 ),
339 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 7, 0), Value::Counter64(u64::MAX)),
340 VarBind::new(
341 oid!(1, 3, 6, 1, 2, 1, 1, 8, 0),
342 Value::ObjectIdentifier(oid!(1, 3, 6, 1, 4)),
343 ),
344 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 9, 0), Value::Null),
345 ];
346
347 let mut buf = EncodeBuf::new();
348 encode_varbind_list(&mut buf, &varbinds);
349 let bytes = buf.finish();
350
351 let mut decoder = Decoder::new(bytes);
352 let decoded = decode_varbind_list(&mut decoder).unwrap();
353
354 assert_eq!(varbinds, decoded);
355 }
356
357 #[test]
358 fn test_null_varbinds_encoding() {
359 let oids = vec![
360 oid!(1, 3, 6, 1, 2, 1, 1, 1, 0),
361 oid!(1, 3, 6, 1, 2, 1, 1, 3, 0),
362 oid!(1, 3, 6, 1, 2, 1, 1, 5, 0),
363 ];
364
365 let mut buf = EncodeBuf::new();
366 encode_null_varbinds(&mut buf, &oids);
367 let bytes = buf.finish();
368
369 let mut decoder = Decoder::new(bytes);
370 let decoded = decode_varbind_list(&mut decoder).unwrap();
371
372 assert_eq!(decoded.len(), 3);
373 for (i, vb) in decoded.iter().enumerate() {
374 assert_eq!(vb.oid, oids[i]);
375 assert_eq!(vb.value, Value::Null);
376 }
377 }
378
379 #[test]
380 fn test_null_varbinds_empty() {
381 let oids: Vec<Oid> = vec![];
382
383 let mut buf = EncodeBuf::new();
384 encode_null_varbinds(&mut buf, &oids);
385 let bytes = buf.finish();
386
387 let mut decoder = Decoder::new(bytes);
388 let decoded = decode_varbind_list(&mut decoder).unwrap();
389
390 assert!(decoded.is_empty());
391 }
392
393 #[test]
398 fn test_varbind_display() {
399 let vb = VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0), Value::Integer(42));
400 let display = format!("{vb}");
401 assert!(display.contains("1.3.6.1.2.1.1.1.0"));
402 assert!(display.contains("42"));
403 }
404
405 #[test]
406 fn test_varbind_display_exception() {
407 let vb = VarBind::new(oid!(1, 3, 6, 1), Value::NoSuchObject);
408 let display = format!("{vb}");
409 assert!(display.contains("noSuchObject"));
410 }
411
412 #[test]
417 fn test_varbind_null_constructor() {
418 let vb = VarBind::null(oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
419 assert_eq!(vb.oid, oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
420 assert_eq!(vb.value, Value::Null);
421 }
422
423 fn verify_encoded_size(vb: &VarBind) {
429 let mut buf = EncodeBuf::new();
430 vb.encode(&mut buf);
431 let actual = buf.len();
432 let computed = vb.encoded_size();
433 assert_eq!(
434 computed, actual,
435 "encoded_size mismatch for {vb:?}: computed={computed}, actual={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}