1use bytes::Bytes;
23
24use crate::ber::{Decoder, EncodeBuf};
25use crate::error::internal::DecodeErrorKind;
26use crate::error::{Error, Result, UNKNOWN_TARGET};
27use crate::pdu::Pdu;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[repr(i32)]
32pub enum SecurityModel {
33 Usm = 3,
35}
36
37impl SecurityModel {
38 pub fn from_i32(value: i32) -> Option<Self> {
40 match value {
41 3 => Some(Self::Usm),
42 _ => None,
43 }
44 }
45
46 pub fn as_i32(self) -> i32 {
48 self as i32
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
57pub enum SecurityLevel {
58 NoAuthNoPriv,
60 AuthNoPriv,
62 AuthPriv,
64}
65
66impl SecurityLevel {
67 pub fn from_flags(flags: u8) -> Option<Self> {
69 let auth = flags & 0x01 != 0;
70 let priv_ = flags & 0x02 != 0;
71
72 match (auth, priv_) {
73 (false, false) => Some(Self::NoAuthNoPriv),
74 (true, false) => Some(Self::AuthNoPriv),
75 (true, true) => Some(Self::AuthPriv),
76 (false, true) => None, }
78 }
79
80 pub fn to_flags(self) -> u8 {
82 match self {
83 Self::NoAuthNoPriv => 0x00,
84 Self::AuthNoPriv => 0x01,
85 Self::AuthPriv => 0x03,
86 }
87 }
88
89 pub fn requires_auth(self) -> bool {
91 matches!(self, Self::AuthNoPriv | Self::AuthPriv)
92 }
93
94 pub fn requires_priv(self) -> bool {
96 matches!(self, Self::AuthPriv)
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub struct MsgFlags {
103 pub security_level: SecurityLevel,
105 pub reportable: bool,
107}
108
109impl MsgFlags {
110 pub fn new(security_level: SecurityLevel, reportable: bool) -> Self {
112 Self {
113 security_level,
114 reportable,
115 }
116 }
117
118 pub fn from_byte(byte: u8) -> Result<Self> {
120 let security_level = SecurityLevel::from_flags(byte).ok_or_else(|| {
121 tracing::debug!(target: "async_snmp::v3", { byte, kind = %DecodeErrorKind::InvalidMsgFlags }, "decode error");
122 Error::MalformedResponse {
123 target: UNKNOWN_TARGET,
124 }
125 .boxed()
126 })?;
127 let reportable = byte & 0x04 != 0;
128 Ok(Self {
129 security_level,
130 reportable,
131 })
132 }
133
134 pub fn to_byte(self) -> u8 {
136 let mut flags = self.security_level.to_flags();
137 if self.reportable {
138 flags |= 0x04;
139 }
140 flags
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct MsgGlobalData {
147 pub msg_id: i32,
149 pub msg_max_size: i32,
151 pub msg_flags: MsgFlags,
153 pub msg_security_model: SecurityModel,
155}
156
157impl MsgGlobalData {
158 pub fn new(msg_id: i32, msg_max_size: i32, msg_flags: MsgFlags) -> Self {
160 Self {
161 msg_id,
162 msg_max_size,
163 msg_flags,
164 msg_security_model: SecurityModel::Usm,
165 }
166 }
167
168 pub fn encode(&self, buf: &mut EncodeBuf) {
170 buf.push_sequence(|buf| {
171 buf.push_integer(self.msg_security_model.as_i32());
172 buf.push_octet_string(&[self.msg_flags.to_byte()]);
174 buf.push_integer(self.msg_max_size);
175 buf.push_integer(self.msg_id);
176 });
177 }
178
179 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
186 const MSG_MAX_SIZE_MINIMUM: i32 = 484;
187
188 let mut seq = decoder.read_sequence()?;
189
190 let msg_id = seq.read_integer()?;
191 let msg_max_size = seq.read_integer()?;
192
193 if msg_id < 0 {
195 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_id, kind = %DecodeErrorKind::InvalidMsgId { value: msg_id } }, "decode error");
196 return Err(Error::MalformedResponse {
197 target: UNKNOWN_TARGET,
198 }
199 .boxed());
200 }
201
202 if msg_max_size < 0 {
205 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_max_size, kind = %DecodeErrorKind::MsgMaxSizeTooLarge { value: msg_max_size } }, "decode error");
206 return Err(Error::MalformedResponse {
207 target: UNKNOWN_TARGET,
208 }
209 .boxed());
210 }
211
212 if msg_max_size < MSG_MAX_SIZE_MINIMUM {
213 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_max_size, minimum = MSG_MAX_SIZE_MINIMUM, kind = %DecodeErrorKind::MsgMaxSizeTooSmall { value: msg_max_size, minimum: MSG_MAX_SIZE_MINIMUM } }, "decode error");
214 return Err(Error::MalformedResponse {
215 target: UNKNOWN_TARGET,
216 }
217 .boxed());
218 }
219
220 let flags_bytes = seq.read_octet_string()?;
221 if flags_bytes.len() != 1 {
222 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), expected = 1, actual = flags_bytes.len() }, "invalid msgFlags length");
223 return Err(Error::MalformedResponse {
224 target: UNKNOWN_TARGET,
225 }
226 .boxed());
227 }
228 let msg_flags = MsgFlags::from_byte(flags_bytes[0])?;
229
230 let msg_security_model_raw = seq.read_integer()?;
231 let msg_security_model =
233 SecurityModel::from_i32(msg_security_model_raw).ok_or_else(|| {
234 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), model = msg_security_model_raw, kind = %DecodeErrorKind::UnknownSecurityModel(msg_security_model_raw) }, "decode error");
235 Error::MalformedResponse {
236 target: UNKNOWN_TARGET,
237 }
238 .boxed()
239 })?;
240
241 Ok(Self {
242 msg_id,
243 msg_max_size,
244 msg_flags,
245 msg_security_model,
246 })
247 }
248}
249
250#[derive(Debug, Clone)]
252pub struct ScopedPdu {
253 pub context_engine_id: Bytes,
255 pub context_name: Bytes,
257 pub pdu: Pdu,
259}
260
261impl ScopedPdu {
262 pub fn new(
264 context_engine_id: impl Into<Bytes>,
265 context_name: impl Into<Bytes>,
266 pdu: Pdu,
267 ) -> Self {
268 Self {
269 context_engine_id: context_engine_id.into(),
270 context_name: context_name.into(),
271 pdu,
272 }
273 }
274
275 pub fn with_empty_context(pdu: Pdu) -> Self {
277 Self {
278 context_engine_id: Bytes::new(),
279 context_name: Bytes::new(),
280 pdu,
281 }
282 }
283
284 pub fn encode(&self, buf: &mut EncodeBuf) {
286 buf.push_sequence(|buf| {
287 self.pdu.encode(buf);
288 buf.push_octet_string(&self.context_name);
289 buf.push_octet_string(&self.context_engine_id);
290 });
291 }
292
293 pub fn encode_to_bytes(&self) -> Bytes {
295 let mut buf = EncodeBuf::new();
296 self.encode(&mut buf);
297 buf.finish()
298 }
299
300 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
302 let mut seq = decoder.read_sequence()?;
303
304 let context_engine_id = seq.read_octet_string()?;
305 let context_name = seq.read_octet_string()?;
306 let pdu = Pdu::decode(&mut seq)?;
307
308 Ok(Self {
309 context_engine_id,
310 context_name,
311 pdu,
312 })
313 }
314}
315
316#[derive(Debug, Clone)]
318pub struct V3Message {
319 pub global_data: MsgGlobalData,
321 pub security_params: Bytes,
323 pub data: V3MessageData,
325}
326
327#[derive(Debug, Clone)]
329pub enum V3MessageData {
330 Plaintext(ScopedPdu),
332 Encrypted(Bytes),
334}
335
336impl V3Message {
337 pub fn new(global_data: MsgGlobalData, security_params: Bytes, scoped_pdu: ScopedPdu) -> Self {
339 Self {
340 global_data,
341 security_params,
342 data: V3MessageData::Plaintext(scoped_pdu),
343 }
344 }
345
346 pub fn new_encrypted(
348 global_data: MsgGlobalData,
349 security_params: Bytes,
350 encrypted: Bytes,
351 ) -> Self {
352 Self {
353 global_data,
354 security_params,
355 data: V3MessageData::Encrypted(encrypted),
356 }
357 }
358
359 pub fn scoped_pdu(&self) -> Option<&ScopedPdu> {
361 match &self.data {
362 V3MessageData::Plaintext(pdu) => Some(pdu),
363 V3MessageData::Encrypted(_) => None,
364 }
365 }
366
367 pub fn into_scoped_pdu(self) -> Option<ScopedPdu> {
369 match self.data {
370 V3MessageData::Plaintext(pdu) => Some(pdu),
371 V3MessageData::Encrypted(_) => None,
372 }
373 }
374
375 pub fn pdu(&self) -> Option<&Pdu> {
377 self.scoped_pdu().map(|s| &s.pdu)
378 }
379
380 pub fn into_pdu(self) -> Option<Pdu> {
382 self.into_scoped_pdu().map(|s| s.pdu)
383 }
384
385 pub fn msg_id(&self) -> i32 {
387 self.global_data.msg_id
388 }
389
390 pub fn security_level(&self) -> SecurityLevel {
392 self.global_data.msg_flags.security_level
393 }
394
395 pub fn encode(&self) -> Bytes {
402 let mut buf = EncodeBuf::new();
403
404 buf.push_sequence(|buf| {
405 match &self.data {
407 V3MessageData::Plaintext(scoped_pdu) => {
408 scoped_pdu.encode(buf);
409 }
410 V3MessageData::Encrypted(ciphertext) => {
411 buf.push_octet_string(ciphertext);
412 }
413 }
414
415 buf.push_octet_string(&self.security_params);
417
418 self.global_data.encode(buf);
420
421 buf.push_integer(3);
423 });
424
425 buf.finish()
426 }
427
428 pub fn decode(data: Bytes) -> Result<Self> {
433 let mut decoder = Decoder::new(data);
434 let mut seq = decoder.read_sequence()?;
435
436 let version = seq.read_integer()?;
438 if version != 3 {
439 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), version, kind = %DecodeErrorKind::UnknownVersion(version) }, "decode error");
440 return Err(Error::MalformedResponse {
441 target: UNKNOWN_TARGET,
442 }
443 .boxed());
444 }
445
446 Self::decode_from_sequence(&mut seq)
447 }
448
449 pub(crate) fn decode_from_sequence(seq: &mut Decoder) -> Result<Self> {
451 let global_data = MsgGlobalData::decode(seq)?;
453
454 let security_params = seq.read_octet_string()?;
456
457 let data = if global_data.msg_flags.security_level.requires_priv() {
459 let encrypted = seq.read_octet_string()?;
461 V3MessageData::Encrypted(encrypted)
462 } else {
463 let scoped_pdu = ScopedPdu::decode(seq)?;
465 V3MessageData::Plaintext(scoped_pdu)
466 };
467
468 Ok(Self {
469 global_data,
470 security_params,
471 data,
472 })
473 }
474
475 pub fn discovery_request(msg_id: i32) -> Self {
480 let global_data = MsgGlobalData::new(
481 msg_id,
482 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
484 );
485
486 let security_params = crate::v3::UsmSecurityParams::empty().encode();
488
489 let pdu = Pdu::get_request(0, &[]);
491 let scoped_pdu = ScopedPdu::with_empty_context(pdu);
492
493 Self::new(global_data, security_params, scoped_pdu)
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500 use crate::oid;
501
502 #[test]
503 fn test_security_level_flags() {
504 assert_eq!(SecurityLevel::NoAuthNoPriv.to_flags(), 0x00);
505 assert_eq!(SecurityLevel::AuthNoPriv.to_flags(), 0x01);
506 assert_eq!(SecurityLevel::AuthPriv.to_flags(), 0x03);
507
508 assert_eq!(
509 SecurityLevel::from_flags(0x00),
510 Some(SecurityLevel::NoAuthNoPriv)
511 );
512 assert_eq!(
513 SecurityLevel::from_flags(0x01),
514 Some(SecurityLevel::AuthNoPriv)
515 );
516 assert_eq!(
517 SecurityLevel::from_flags(0x03),
518 Some(SecurityLevel::AuthPriv)
519 );
520 assert_eq!(SecurityLevel::from_flags(0x02), None); }
522
523 #[test]
524 fn test_msg_flags_roundtrip() {
525 let flags = MsgFlags::new(SecurityLevel::AuthPriv, true);
526 let byte = flags.to_byte();
527 assert_eq!(byte, 0x07); let decoded = MsgFlags::from_byte(byte).unwrap();
530 assert_eq!(decoded.security_level, SecurityLevel::AuthPriv);
531 assert!(decoded.reportable);
532 }
533
534 #[test]
535 fn test_msg_global_data_roundtrip() {
536 let global =
537 MsgGlobalData::new(12345, 1472, MsgFlags::new(SecurityLevel::AuthNoPriv, true));
538
539 let mut buf = EncodeBuf::new();
540 global.encode(&mut buf);
541 let encoded = buf.finish();
542
543 let mut decoder = Decoder::new(encoded);
544 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
545
546 assert_eq!(decoded.msg_id, 12345);
547 assert_eq!(decoded.msg_max_size, 1472);
548 assert_eq!(decoded.msg_flags.security_level, SecurityLevel::AuthNoPriv);
549 assert!(decoded.msg_flags.reportable);
550 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
551 }
552
553 #[test]
554 fn test_scoped_pdu_roundtrip() {
555 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
556 let scoped = ScopedPdu::new(b"engine".as_slice(), b"ctx".as_slice(), pdu);
557
558 let mut buf = EncodeBuf::new();
559 scoped.encode(&mut buf);
560 let encoded = buf.finish();
561
562 let mut decoder = Decoder::new(encoded);
563 let decoded = ScopedPdu::decode(&mut decoder).unwrap();
564
565 assert_eq!(decoded.context_engine_id.as_ref(), b"engine");
566 assert_eq!(decoded.context_name.as_ref(), b"ctx");
567 assert_eq!(decoded.pdu.request_id, 42);
568 }
569
570 #[test]
571 fn test_v3_message_plaintext_roundtrip() {
572 let global =
573 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
574 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
575 let scoped = ScopedPdu::with_empty_context(pdu);
576 let msg = V3Message::new(global, Bytes::from_static(b"usm-params"), scoped);
577
578 let encoded = msg.encode();
579 let decoded = V3Message::decode(encoded).unwrap();
580
581 assert_eq!(decoded.global_data.msg_id, 100);
582 assert_eq!(decoded.security_level(), SecurityLevel::NoAuthNoPriv);
583 assert_eq!(decoded.security_params.as_ref(), b"usm-params");
584
585 let scoped_pdu = decoded.scoped_pdu().unwrap();
586 assert_eq!(scoped_pdu.pdu.request_id, 42);
587 }
588
589 #[test]
590 fn test_v3_message_encrypted_roundtrip() {
591 let global = MsgGlobalData::new(200, 1472, MsgFlags::new(SecurityLevel::AuthPriv, false));
592 let msg = V3Message::new_encrypted(
593 global,
594 Bytes::from_static(b"usm-params"),
595 Bytes::from_static(b"encrypted-data"),
596 );
597
598 let encoded = msg.encode();
599 let decoded = V3Message::decode(encoded).unwrap();
600
601 assert_eq!(decoded.global_data.msg_id, 200);
602 assert_eq!(decoded.security_level(), SecurityLevel::AuthPriv);
603
604 match &decoded.data {
605 V3MessageData::Encrypted(data) => {
606 assert_eq!(data.as_ref(), b"encrypted-data");
607 }
608 V3MessageData::Plaintext(_) => panic!("expected encrypted data"),
609 }
610 }
611
612 #[test]
613 fn test_msg_global_data_rejects_msg_max_size_below_minimum() {
614 let global = MsgGlobalData {
616 msg_id: 100,
617 msg_max_size: 400, msg_flags: MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
619 msg_security_model: SecurityModel::Usm,
620 };
621
622 let mut buf = EncodeBuf::new();
623 global.encode(&mut buf);
624 let encoded = buf.finish();
625
626 let mut decoder = Decoder::new(encoded);
627 let result = MsgGlobalData::decode(&mut decoder);
628
629 assert!(result.is_err());
630 assert!(matches!(
631 *result.unwrap_err(),
632 Error::MalformedResponse { .. }
633 ));
634 }
635
636 #[test]
637 fn test_msg_global_data_accepts_msg_max_size_at_minimum() {
638 let global = MsgGlobalData::new(100, 484, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
640
641 let mut buf = EncodeBuf::new();
642 global.encode(&mut buf);
643 let encoded = buf.finish();
644
645 let mut decoder = Decoder::new(encoded);
646 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
647
648 assert_eq!(decoded.msg_max_size, 484);
649 }
650
651 #[test]
652 fn test_msg_global_data_rejects_unknown_security_model() {
653 let mut buf = EncodeBuf::new();
656 buf.push_sequence(|buf| {
657 buf.push_integer(99); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(100); });
662 let encoded = buf.finish();
663
664 let mut decoder = Decoder::new(encoded);
665 let result = MsgGlobalData::decode(&mut decoder);
666
667 assert!(result.is_err());
668 assert!(matches!(
669 *result.unwrap_err(),
670 Error::MalformedResponse { .. }
671 ));
672 }
673
674 #[test]
675 fn test_msg_global_data_accepts_usm_security_model() {
676 let global =
678 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
679
680 let mut buf = EncodeBuf::new();
681 global.encode(&mut buf);
682 let encoded = buf.finish();
683
684 let mut decoder = Decoder::new(encoded);
685 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
686
687 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
688 }
689
690 #[test]
699 fn test_msg_global_data_rejects_negative_msg_id() {
700 let mut buf = EncodeBuf::new();
703 buf.push_sequence(|buf| {
704 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(-1); });
709 let encoded = buf.finish();
710
711 let mut decoder = Decoder::new(encoded);
712 let result = MsgGlobalData::decode(&mut decoder);
713
714 assert!(result.is_err());
715 assert!(matches!(
716 *result.unwrap_err(),
717 Error::MalformedResponse { .. }
718 ));
719 }
720
721 #[test]
722 fn test_msg_global_data_rejects_negative_msg_max_size() {
723 let mut buf = EncodeBuf::new();
726 buf.push_sequence(|buf| {
727 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(-1); buf.push_integer(100); });
732 let encoded = buf.finish();
733
734 let mut decoder = Decoder::new(encoded);
735 let result = MsgGlobalData::decode(&mut decoder);
736
737 assert!(result.is_err());
738 assert!(matches!(
739 *result.unwrap_err(),
740 Error::MalformedResponse { .. }
741 ));
742 }
743
744 #[test]
745 fn test_msg_global_data_accepts_msg_id_at_zero() {
746 let mut buf = EncodeBuf::new();
748 buf.push_sequence(|buf| {
749 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(0); });
754 let encoded = buf.finish();
755
756 let mut decoder = Decoder::new(encoded);
757 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
758
759 assert_eq!(decoded.msg_id, 0);
760 }
761
762 #[test]
763 fn test_msg_global_data_accepts_msg_id_at_maximum() {
764 let mut buf = EncodeBuf::new();
766 buf.push_sequence(|buf| {
767 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(i32::MAX); });
772 let encoded = buf.finish();
773
774 let mut decoder = Decoder::new(encoded);
775 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
776
777 assert_eq!(decoded.msg_id, i32::MAX);
778 }
779
780 #[test]
781 fn test_msg_global_data_accepts_msg_max_size_at_maximum() {
782 let mut buf = EncodeBuf::new();
784 buf.push_sequence(|buf| {
785 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(i32::MAX); buf.push_integer(100); });
790 let encoded = buf.finish();
791
792 let mut decoder = Decoder::new(encoded);
793 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
794
795 assert_eq!(decoded.msg_max_size, i32::MAX);
796 }
797}