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 #[must_use]
40 pub fn from_i32(value: i32) -> Option<Self> {
41 match value {
42 3 => Some(Self::Usm),
43 _ => None,
44 }
45 }
46
47 #[must_use]
49 pub fn as_i32(self) -> i32 {
50 self as i32
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
59pub enum SecurityLevel {
60 NoAuthNoPriv,
62 AuthNoPriv,
64 AuthPriv,
66}
67
68impl SecurityLevel {
69 #[must_use]
71 pub fn from_flags(flags: u8) -> Option<Self> {
72 let auth = flags & 0x01 != 0;
73 let priv_ = flags & 0x02 != 0;
74
75 match (auth, priv_) {
76 (false, false) => Some(Self::NoAuthNoPriv),
77 (true, false) => Some(Self::AuthNoPriv),
78 (true, true) => Some(Self::AuthPriv),
79 (false, true) => None, }
81 }
82
83 #[must_use]
85 pub fn to_flags(self) -> u8 {
86 match self {
87 Self::NoAuthNoPriv => 0x00,
88 Self::AuthNoPriv => 0x01,
89 Self::AuthPriv => 0x03,
90 }
91 }
92
93 #[must_use]
95 pub fn requires_auth(self) -> bool {
96 matches!(self, Self::AuthNoPriv | Self::AuthPriv)
97 }
98
99 #[must_use]
101 pub fn requires_priv(self) -> bool {
102 matches!(self, Self::AuthPriv)
103 }
104}
105
106impl TryFrom<u8> for SecurityLevel {
107 type Error = u8;
108
109 fn try_from(flags: u8) -> std::result::Result<Self, u8> {
110 Self::from_flags(flags).ok_or(flags)
111 }
112}
113
114impl From<SecurityLevel> for u8 {
115 fn from(level: SecurityLevel) -> u8 {
116 level.to_flags()
117 }
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub struct MsgFlags {
123 pub security_level: SecurityLevel,
125 pub reportable: bool,
127}
128
129impl MsgFlags {
130 #[must_use]
132 pub fn new(security_level: SecurityLevel, reportable: bool) -> Self {
133 Self {
134 security_level,
135 reportable,
136 }
137 }
138
139 pub fn from_byte(byte: u8) -> Result<Self> {
141 let security_level = SecurityLevel::from_flags(byte).ok_or_else(|| {
142 tracing::debug!(target: "async_snmp::v3", { byte, kind = %DecodeErrorKind::InvalidMsgFlags }, "decode error");
143 Error::MalformedResponse {
144 target: UNKNOWN_TARGET,
145 }
146 .boxed()
147 })?;
148 let reportable = byte & 0x04 != 0;
149 Ok(Self {
150 security_level,
151 reportable,
152 })
153 }
154
155 #[must_use]
157 pub fn to_byte(self) -> u8 {
158 let mut flags = self.security_level.to_flags();
159 if self.reportable {
160 flags |= 0x04;
161 }
162 flags
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
168pub struct MsgGlobalData {
169 pub msg_id: i32,
171 pub msg_max_size: i32,
173 pub msg_flags: MsgFlags,
175 pub msg_security_model: SecurityModel,
177}
178
179impl MsgGlobalData {
180 #[must_use]
182 pub fn new(msg_id: i32, msg_max_size: i32, msg_flags: MsgFlags) -> Self {
183 Self {
184 msg_id,
185 msg_max_size,
186 msg_flags,
187 msg_security_model: SecurityModel::Usm,
188 }
189 }
190
191 pub fn encode(&self, buf: &mut EncodeBuf) {
193 buf.push_sequence(|buf| {
194 buf.push_integer(self.msg_security_model.as_i32());
195 buf.push_octet_string(&[self.msg_flags.to_byte()]);
197 buf.push_integer(self.msg_max_size);
198 buf.push_integer(self.msg_id);
199 });
200 }
201
202 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
209 const MSG_MAX_SIZE_MINIMUM: i32 = 484;
210
211 let mut seq = decoder.read_sequence()?;
212
213 let msg_id = seq.read_integer()?;
214 let msg_max_size = seq.read_integer()?;
215
216 if msg_id < 0 {
218 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_id, kind = %DecodeErrorKind::InvalidMsgId { value: msg_id } }, "decode error");
219 return Err(Error::MalformedResponse {
220 target: UNKNOWN_TARGET,
221 }
222 .boxed());
223 }
224
225 if msg_max_size < 0 {
228 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_max_size, kind = %DecodeErrorKind::MsgMaxSizeTooLarge { value: msg_max_size } }, "decode error");
229 return Err(Error::MalformedResponse {
230 target: UNKNOWN_TARGET,
231 }
232 .boxed());
233 }
234
235 if msg_max_size < MSG_MAX_SIZE_MINIMUM {
236 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");
237 return Err(Error::MalformedResponse {
238 target: UNKNOWN_TARGET,
239 }
240 .boxed());
241 }
242
243 let flags_bytes = seq.read_octet_string()?;
244 if flags_bytes.len() != 1 {
245 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), expected = 1, actual = flags_bytes.len() }, "invalid msgFlags length");
246 return Err(Error::MalformedResponse {
247 target: UNKNOWN_TARGET,
248 }
249 .boxed());
250 }
251 let msg_flags = MsgFlags::from_byte(flags_bytes[0])?;
252
253 let msg_security_model_raw = seq.read_integer()?;
254 let msg_security_model =
256 SecurityModel::from_i32(msg_security_model_raw).ok_or_else(|| {
257 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), model = msg_security_model_raw, kind = %DecodeErrorKind::UnknownSecurityModel(msg_security_model_raw) }, "decode error");
258 Error::MalformedResponse {
259 target: UNKNOWN_TARGET,
260 }
261 .boxed()
262 })?;
263
264 Ok(Self {
265 msg_id,
266 msg_max_size,
267 msg_flags,
268 msg_security_model,
269 })
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
275pub struct ScopedPdu {
276 pub context_engine_id: Bytes,
278 pub context_name: Bytes,
280 pub pdu: Pdu,
282}
283
284impl ScopedPdu {
285 pub fn new(
287 context_engine_id: impl Into<Bytes>,
288 context_name: impl Into<Bytes>,
289 pdu: Pdu,
290 ) -> Self {
291 Self {
292 context_engine_id: context_engine_id.into(),
293 context_name: context_name.into(),
294 pdu,
295 }
296 }
297
298 #[must_use]
300 pub fn with_empty_context(pdu: Pdu) -> Self {
301 Self {
302 context_engine_id: Bytes::new(),
303 context_name: Bytes::new(),
304 pdu,
305 }
306 }
307
308 pub fn encode(&self, buf: &mut EncodeBuf) {
310 buf.push_sequence(|buf| {
311 self.pdu.encode(buf);
312 buf.push_octet_string(&self.context_name);
313 buf.push_octet_string(&self.context_engine_id);
314 });
315 }
316
317 pub fn encode_to_bytes(&self) -> Bytes {
319 let mut buf = EncodeBuf::new();
320 self.encode(&mut buf);
321 buf.finish()
322 }
323
324 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
326 let mut seq = decoder.read_sequence()?;
327
328 let context_engine_id = seq.read_octet_string()?;
329 let context_name = seq.read_octet_string()?;
330 let pdu = Pdu::decode(&mut seq)?;
331
332 Ok(Self {
333 context_engine_id,
334 context_name,
335 pdu,
336 })
337 }
338}
339
340#[derive(Debug, Clone, PartialEq, Eq)]
342pub struct V3Message {
343 pub global_data: MsgGlobalData,
345 pub security_params: Bytes,
347 pub data: V3MessageData,
349}
350
351#[derive(Debug, Clone, PartialEq, Eq)]
353pub enum V3MessageData {
354 Plaintext(ScopedPdu),
356 Encrypted(Bytes),
358}
359
360impl V3Message {
361 pub fn new(global_data: MsgGlobalData, security_params: Bytes, scoped_pdu: ScopedPdu) -> Self {
363 Self {
364 global_data,
365 security_params,
366 data: V3MessageData::Plaintext(scoped_pdu),
367 }
368 }
369
370 pub fn new_encrypted(
372 global_data: MsgGlobalData,
373 security_params: Bytes,
374 encrypted: Bytes,
375 ) -> Self {
376 Self {
377 global_data,
378 security_params,
379 data: V3MessageData::Encrypted(encrypted),
380 }
381 }
382
383 pub fn scoped_pdu(&self) -> Option<&ScopedPdu> {
385 match &self.data {
386 V3MessageData::Plaintext(pdu) => Some(pdu),
387 V3MessageData::Encrypted(_) => None,
388 }
389 }
390
391 pub fn into_scoped_pdu(self) -> Option<ScopedPdu> {
393 match self.data {
394 V3MessageData::Plaintext(pdu) => Some(pdu),
395 V3MessageData::Encrypted(_) => None,
396 }
397 }
398
399 pub fn pdu(&self) -> Option<&Pdu> {
401 self.scoped_pdu().map(|s| &s.pdu)
402 }
403
404 pub fn into_pdu(self) -> Option<Pdu> {
406 self.into_scoped_pdu().map(|s| s.pdu)
407 }
408
409 pub fn msg_id(&self) -> i32 {
411 self.global_data.msg_id
412 }
413
414 pub fn security_level(&self) -> SecurityLevel {
416 self.global_data.msg_flags.security_level
417 }
418
419 pub fn encode(&self) -> Bytes {
426 let mut buf = EncodeBuf::new();
427
428 buf.push_sequence(|buf| {
429 match &self.data {
431 V3MessageData::Plaintext(scoped_pdu) => {
432 scoped_pdu.encode(buf);
433 }
434 V3MessageData::Encrypted(ciphertext) => {
435 buf.push_octet_string(ciphertext);
436 }
437 }
438
439 buf.push_octet_string(&self.security_params);
441
442 self.global_data.encode(buf);
444
445 buf.push_integer(3);
447 });
448
449 buf.finish()
450 }
451
452 pub fn decode(data: Bytes) -> Result<Self> {
457 let mut decoder = Decoder::new(data);
458 let mut seq = decoder.read_sequence()?;
459
460 let version = seq.read_integer()?;
462 if version != 3 {
463 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), version, kind = %DecodeErrorKind::UnknownVersion(version) }, "decode error");
464 return Err(Error::MalformedResponse {
465 target: UNKNOWN_TARGET,
466 }
467 .boxed());
468 }
469
470 Self::decode_from_sequence(&mut seq)
471 }
472
473 pub(crate) fn decode_from_sequence(seq: &mut Decoder) -> Result<Self> {
475 let global_data = MsgGlobalData::decode(seq)?;
477
478 let security_params = seq.read_octet_string()?;
480
481 let data = if global_data.msg_flags.security_level.requires_priv() {
483 let encrypted = seq.read_octet_string()?;
485 V3MessageData::Encrypted(encrypted)
486 } else {
487 let scoped_pdu = ScopedPdu::decode(seq)?;
489 V3MessageData::Plaintext(scoped_pdu)
490 };
491
492 Ok(Self {
493 global_data,
494 security_params,
495 data,
496 })
497 }
498
499 #[must_use]
504 pub fn discovery_request(msg_id: i32) -> Self {
505 let global_data = MsgGlobalData::new(
506 msg_id,
507 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
509 );
510
511 let security_params = crate::v3::UsmSecurityParams::empty().encode();
513
514 let pdu = Pdu::get_request(0, &[]);
516 let scoped_pdu = ScopedPdu::with_empty_context(pdu);
517
518 Self::new(global_data, security_params, scoped_pdu)
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525 use crate::oid;
526
527 #[test]
528 fn test_security_level_flags() {
529 assert_eq!(SecurityLevel::NoAuthNoPriv.to_flags(), 0x00);
530 assert_eq!(SecurityLevel::AuthNoPriv.to_flags(), 0x01);
531 assert_eq!(SecurityLevel::AuthPriv.to_flags(), 0x03);
532
533 assert_eq!(
534 SecurityLevel::from_flags(0x00),
535 Some(SecurityLevel::NoAuthNoPriv)
536 );
537 assert_eq!(
538 SecurityLevel::from_flags(0x01),
539 Some(SecurityLevel::AuthNoPriv)
540 );
541 assert_eq!(
542 SecurityLevel::from_flags(0x03),
543 Some(SecurityLevel::AuthPriv)
544 );
545 assert_eq!(SecurityLevel::from_flags(0x02), None); }
547
548 #[test]
549 fn security_level_try_from_u8() {
550 assert_eq!(
551 SecurityLevel::try_from(0x00),
552 Ok(SecurityLevel::NoAuthNoPriv)
553 );
554 assert_eq!(SecurityLevel::try_from(0x01), Ok(SecurityLevel::AuthNoPriv));
555 assert_eq!(SecurityLevel::try_from(0x03), Ok(SecurityLevel::AuthPriv));
556 assert_eq!(SecurityLevel::try_from(0x02), Err(0x02));
557 }
558
559 #[test]
560 fn security_level_into_u8() {
561 assert_eq!(u8::from(SecurityLevel::NoAuthNoPriv), 0x00);
562 assert_eq!(u8::from(SecurityLevel::AuthNoPriv), 0x01);
563 assert_eq!(u8::from(SecurityLevel::AuthPriv), 0x03);
564 }
565
566 #[test]
567 fn test_msg_flags_roundtrip() {
568 let flags = MsgFlags::new(SecurityLevel::AuthPriv, true);
569 let byte = flags.to_byte();
570 assert_eq!(byte, 0x07); let decoded = MsgFlags::from_byte(byte).unwrap();
573 assert_eq!(decoded.security_level, SecurityLevel::AuthPriv);
574 assert!(decoded.reportable);
575 }
576
577 #[test]
578 fn test_msg_global_data_roundtrip() {
579 let global =
580 MsgGlobalData::new(12345, 1472, MsgFlags::new(SecurityLevel::AuthNoPriv, true));
581
582 let mut buf = EncodeBuf::new();
583 global.encode(&mut buf);
584 let encoded = buf.finish();
585
586 let mut decoder = Decoder::new(encoded);
587 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
588
589 assert_eq!(decoded.msg_id, 12345);
590 assert_eq!(decoded.msg_max_size, 1472);
591 assert_eq!(decoded.msg_flags.security_level, SecurityLevel::AuthNoPriv);
592 assert!(decoded.msg_flags.reportable);
593 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
594 }
595
596 #[test]
597 fn test_scoped_pdu_roundtrip() {
598 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
599 let scoped = ScopedPdu::new(b"engine".as_slice(), b"ctx".as_slice(), pdu);
600
601 let mut buf = EncodeBuf::new();
602 scoped.encode(&mut buf);
603 let encoded = buf.finish();
604
605 let mut decoder = Decoder::new(encoded);
606 let decoded = ScopedPdu::decode(&mut decoder).unwrap();
607
608 assert_eq!(decoded.context_engine_id.as_ref(), b"engine");
609 assert_eq!(decoded.context_name.as_ref(), b"ctx");
610 assert_eq!(decoded.pdu.request_id, 42);
611 }
612
613 #[test]
614 fn test_v3_message_plaintext_roundtrip() {
615 let global =
616 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
617 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
618 let scoped = ScopedPdu::with_empty_context(pdu);
619 let msg = V3Message::new(global, Bytes::from_static(b"usm-params"), scoped);
620
621 let encoded = msg.encode();
622 let decoded = V3Message::decode(encoded).unwrap();
623
624 assert_eq!(decoded.global_data.msg_id, 100);
625 assert_eq!(decoded.security_level(), SecurityLevel::NoAuthNoPriv);
626 assert_eq!(decoded.security_params.as_ref(), b"usm-params");
627
628 let scoped_pdu = decoded.scoped_pdu().unwrap();
629 assert_eq!(scoped_pdu.pdu.request_id, 42);
630 }
631
632 #[test]
633 fn test_v3_message_encrypted_roundtrip() {
634 let global = MsgGlobalData::new(200, 1472, MsgFlags::new(SecurityLevel::AuthPriv, false));
635 let msg = V3Message::new_encrypted(
636 global,
637 Bytes::from_static(b"usm-params"),
638 Bytes::from_static(b"encrypted-data"),
639 );
640
641 let encoded = msg.encode();
642 let decoded = V3Message::decode(encoded).unwrap();
643
644 assert_eq!(decoded.global_data.msg_id, 200);
645 assert_eq!(decoded.security_level(), SecurityLevel::AuthPriv);
646
647 match &decoded.data {
648 V3MessageData::Encrypted(data) => {
649 assert_eq!(data.as_ref(), b"encrypted-data");
650 }
651 V3MessageData::Plaintext(_) => panic!("expected encrypted data"),
652 }
653 }
654
655 #[test]
656 fn test_msg_global_data_rejects_msg_max_size_below_minimum() {
657 let global = MsgGlobalData {
659 msg_id: 100,
660 msg_max_size: 400, msg_flags: MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
662 msg_security_model: SecurityModel::Usm,
663 };
664
665 let mut buf = EncodeBuf::new();
666 global.encode(&mut buf);
667 let encoded = buf.finish();
668
669 let mut decoder = Decoder::new(encoded);
670 let result = MsgGlobalData::decode(&mut decoder);
671
672 assert!(result.is_err());
673 assert!(matches!(
674 *result.unwrap_err(),
675 Error::MalformedResponse { .. }
676 ));
677 }
678
679 #[test]
680 fn test_msg_global_data_accepts_msg_max_size_at_minimum() {
681 let global = MsgGlobalData::new(100, 484, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
683
684 let mut buf = EncodeBuf::new();
685 global.encode(&mut buf);
686 let encoded = buf.finish();
687
688 let mut decoder = Decoder::new(encoded);
689 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
690
691 assert_eq!(decoded.msg_max_size, 484);
692 }
693
694 #[test]
695 fn test_msg_global_data_rejects_unknown_security_model() {
696 let mut buf = EncodeBuf::new();
699 buf.push_sequence(|buf| {
700 buf.push_integer(99); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(100); });
705 let encoded = buf.finish();
706
707 let mut decoder = Decoder::new(encoded);
708 let result = MsgGlobalData::decode(&mut decoder);
709
710 assert!(result.is_err());
711 assert!(matches!(
712 *result.unwrap_err(),
713 Error::MalformedResponse { .. }
714 ));
715 }
716
717 #[test]
718 fn test_msg_global_data_accepts_usm_security_model() {
719 let global =
721 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
722
723 let mut buf = EncodeBuf::new();
724 global.encode(&mut buf);
725 let encoded = buf.finish();
726
727 let mut decoder = Decoder::new(encoded);
728 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
729
730 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
731 }
732
733 #[test]
742 fn test_msg_global_data_rejects_negative_msg_id() {
743 let mut buf = EncodeBuf::new();
746 buf.push_sequence(|buf| {
747 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(-1); });
752 let encoded = buf.finish();
753
754 let mut decoder = Decoder::new(encoded);
755 let result = MsgGlobalData::decode(&mut decoder);
756
757 assert!(result.is_err());
758 assert!(matches!(
759 *result.unwrap_err(),
760 Error::MalformedResponse { .. }
761 ));
762 }
763
764 #[test]
765 fn test_msg_global_data_rejects_negative_msg_max_size() {
766 let mut buf = EncodeBuf::new();
769 buf.push_sequence(|buf| {
770 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(-1); buf.push_integer(100); });
775 let encoded = buf.finish();
776
777 let mut decoder = Decoder::new(encoded);
778 let result = MsgGlobalData::decode(&mut decoder);
779
780 assert!(result.is_err());
781 assert!(matches!(
782 *result.unwrap_err(),
783 Error::MalformedResponse { .. }
784 ));
785 }
786
787 #[test]
788 fn test_msg_global_data_accepts_msg_id_at_zero() {
789 let mut buf = EncodeBuf::new();
791 buf.push_sequence(|buf| {
792 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(0); });
797 let encoded = buf.finish();
798
799 let mut decoder = Decoder::new(encoded);
800 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
801
802 assert_eq!(decoded.msg_id, 0);
803 }
804
805 #[test]
806 fn test_msg_global_data_accepts_msg_id_at_maximum() {
807 let mut buf = EncodeBuf::new();
809 buf.push_sequence(|buf| {
810 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(i32::MAX); });
815 let encoded = buf.finish();
816
817 let mut decoder = Decoder::new(encoded);
818 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
819
820 assert_eq!(decoded.msg_id, i32::MAX);
821 }
822
823 #[test]
824 fn test_msg_global_data_accepts_msg_max_size_at_maximum() {
825 let mut buf = EncodeBuf::new();
827 buf.push_sequence(|buf| {
828 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(i32::MAX); buf.push_integer(100); });
833 let encoded = buf.finish();
834
835 let mut decoder = Decoder::new(encoded);
836 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
837
838 assert_eq!(decoded.msg_max_size, i32::MAX);
839 }
840}