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
100impl TryFrom<u8> for SecurityLevel {
101 type Error = u8;
102
103 fn try_from(flags: u8) -> std::result::Result<Self, u8> {
104 Self::from_flags(flags).ok_or(flags)
105 }
106}
107
108impl From<SecurityLevel> for u8 {
109 fn from(level: SecurityLevel) -> u8 {
110 level.to_flags()
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub struct MsgFlags {
117 pub security_level: SecurityLevel,
119 pub reportable: bool,
121}
122
123impl MsgFlags {
124 pub fn new(security_level: SecurityLevel, reportable: bool) -> Self {
126 Self {
127 security_level,
128 reportable,
129 }
130 }
131
132 pub fn from_byte(byte: u8) -> Result<Self> {
134 let security_level = SecurityLevel::from_flags(byte).ok_or_else(|| {
135 tracing::debug!(target: "async_snmp::v3", { byte, kind = %DecodeErrorKind::InvalidMsgFlags }, "decode error");
136 Error::MalformedResponse {
137 target: UNKNOWN_TARGET,
138 }
139 .boxed()
140 })?;
141 let reportable = byte & 0x04 != 0;
142 Ok(Self {
143 security_level,
144 reportable,
145 })
146 }
147
148 pub fn to_byte(self) -> u8 {
150 let mut flags = self.security_level.to_flags();
151 if self.reportable {
152 flags |= 0x04;
153 }
154 flags
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct MsgGlobalData {
161 pub msg_id: i32,
163 pub msg_max_size: i32,
165 pub msg_flags: MsgFlags,
167 pub msg_security_model: SecurityModel,
169}
170
171impl MsgGlobalData {
172 pub fn new(msg_id: i32, msg_max_size: i32, msg_flags: MsgFlags) -> Self {
174 Self {
175 msg_id,
176 msg_max_size,
177 msg_flags,
178 msg_security_model: SecurityModel::Usm,
179 }
180 }
181
182 pub fn encode(&self, buf: &mut EncodeBuf) {
184 buf.push_sequence(|buf| {
185 buf.push_integer(self.msg_security_model.as_i32());
186 buf.push_octet_string(&[self.msg_flags.to_byte()]);
188 buf.push_integer(self.msg_max_size);
189 buf.push_integer(self.msg_id);
190 });
191 }
192
193 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
200 const MSG_MAX_SIZE_MINIMUM: i32 = 484;
201
202 let mut seq = decoder.read_sequence()?;
203
204 let msg_id = seq.read_integer()?;
205 let msg_max_size = seq.read_integer()?;
206
207 if msg_id < 0 {
209 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_id, kind = %DecodeErrorKind::InvalidMsgId { value: msg_id } }, "decode error");
210 return Err(Error::MalformedResponse {
211 target: UNKNOWN_TARGET,
212 }
213 .boxed());
214 }
215
216 if msg_max_size < 0 {
219 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), value = msg_max_size, kind = %DecodeErrorKind::MsgMaxSizeTooLarge { value: msg_max_size } }, "decode error");
220 return Err(Error::MalformedResponse {
221 target: UNKNOWN_TARGET,
222 }
223 .boxed());
224 }
225
226 if msg_max_size < MSG_MAX_SIZE_MINIMUM {
227 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");
228 return Err(Error::MalformedResponse {
229 target: UNKNOWN_TARGET,
230 }
231 .boxed());
232 }
233
234 let flags_bytes = seq.read_octet_string()?;
235 if flags_bytes.len() != 1 {
236 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), expected = 1, actual = flags_bytes.len() }, "invalid msgFlags length");
237 return Err(Error::MalformedResponse {
238 target: UNKNOWN_TARGET,
239 }
240 .boxed());
241 }
242 let msg_flags = MsgFlags::from_byte(flags_bytes[0])?;
243
244 let msg_security_model_raw = seq.read_integer()?;
245 let msg_security_model =
247 SecurityModel::from_i32(msg_security_model_raw).ok_or_else(|| {
248 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), model = msg_security_model_raw, kind = %DecodeErrorKind::UnknownSecurityModel(msg_security_model_raw) }, "decode error");
249 Error::MalformedResponse {
250 target: UNKNOWN_TARGET,
251 }
252 .boxed()
253 })?;
254
255 Ok(Self {
256 msg_id,
257 msg_max_size,
258 msg_flags,
259 msg_security_model,
260 })
261 }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
266pub struct ScopedPdu {
267 pub context_engine_id: Bytes,
269 pub context_name: Bytes,
271 pub pdu: Pdu,
273}
274
275impl ScopedPdu {
276 pub fn new(
278 context_engine_id: impl Into<Bytes>,
279 context_name: impl Into<Bytes>,
280 pdu: Pdu,
281 ) -> Self {
282 Self {
283 context_engine_id: context_engine_id.into(),
284 context_name: context_name.into(),
285 pdu,
286 }
287 }
288
289 pub fn with_empty_context(pdu: Pdu) -> Self {
291 Self {
292 context_engine_id: Bytes::new(),
293 context_name: Bytes::new(),
294 pdu,
295 }
296 }
297
298 pub fn encode(&self, buf: &mut EncodeBuf) {
300 buf.push_sequence(|buf| {
301 self.pdu.encode(buf);
302 buf.push_octet_string(&self.context_name);
303 buf.push_octet_string(&self.context_engine_id);
304 });
305 }
306
307 pub fn encode_to_bytes(&self) -> Bytes {
309 let mut buf = EncodeBuf::new();
310 self.encode(&mut buf);
311 buf.finish()
312 }
313
314 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
316 let mut seq = decoder.read_sequence()?;
317
318 let context_engine_id = seq.read_octet_string()?;
319 let context_name = seq.read_octet_string()?;
320 let pdu = Pdu::decode(&mut seq)?;
321
322 Ok(Self {
323 context_engine_id,
324 context_name,
325 pdu,
326 })
327 }
328}
329
330#[derive(Debug, Clone, PartialEq, Eq)]
332pub struct V3Message {
333 pub global_data: MsgGlobalData,
335 pub security_params: Bytes,
337 pub data: V3MessageData,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq)]
343pub enum V3MessageData {
344 Plaintext(ScopedPdu),
346 Encrypted(Bytes),
348}
349
350impl V3Message {
351 pub fn new(global_data: MsgGlobalData, security_params: Bytes, scoped_pdu: ScopedPdu) -> Self {
353 Self {
354 global_data,
355 security_params,
356 data: V3MessageData::Plaintext(scoped_pdu),
357 }
358 }
359
360 pub fn new_encrypted(
362 global_data: MsgGlobalData,
363 security_params: Bytes,
364 encrypted: Bytes,
365 ) -> Self {
366 Self {
367 global_data,
368 security_params,
369 data: V3MessageData::Encrypted(encrypted),
370 }
371 }
372
373 pub fn scoped_pdu(&self) -> Option<&ScopedPdu> {
375 match &self.data {
376 V3MessageData::Plaintext(pdu) => Some(pdu),
377 V3MessageData::Encrypted(_) => None,
378 }
379 }
380
381 pub fn into_scoped_pdu(self) -> Option<ScopedPdu> {
383 match self.data {
384 V3MessageData::Plaintext(pdu) => Some(pdu),
385 V3MessageData::Encrypted(_) => None,
386 }
387 }
388
389 pub fn pdu(&self) -> Option<&Pdu> {
391 self.scoped_pdu().map(|s| &s.pdu)
392 }
393
394 pub fn into_pdu(self) -> Option<Pdu> {
396 self.into_scoped_pdu().map(|s| s.pdu)
397 }
398
399 pub fn msg_id(&self) -> i32 {
401 self.global_data.msg_id
402 }
403
404 pub fn security_level(&self) -> SecurityLevel {
406 self.global_data.msg_flags.security_level
407 }
408
409 pub fn encode(&self) -> Bytes {
416 let mut buf = EncodeBuf::new();
417
418 buf.push_sequence(|buf| {
419 match &self.data {
421 V3MessageData::Plaintext(scoped_pdu) => {
422 scoped_pdu.encode(buf);
423 }
424 V3MessageData::Encrypted(ciphertext) => {
425 buf.push_octet_string(ciphertext);
426 }
427 }
428
429 buf.push_octet_string(&self.security_params);
431
432 self.global_data.encode(buf);
434
435 buf.push_integer(3);
437 });
438
439 buf.finish()
440 }
441
442 pub fn decode(data: Bytes) -> Result<Self> {
447 let mut decoder = Decoder::new(data);
448 let mut seq = decoder.read_sequence()?;
449
450 let version = seq.read_integer()?;
452 if version != 3 {
453 tracing::debug!(target: "async_snmp::v3", { offset = seq.offset(), version, kind = %DecodeErrorKind::UnknownVersion(version) }, "decode error");
454 return Err(Error::MalformedResponse {
455 target: UNKNOWN_TARGET,
456 }
457 .boxed());
458 }
459
460 Self::decode_from_sequence(&mut seq)
461 }
462
463 pub(crate) fn decode_from_sequence(seq: &mut Decoder) -> Result<Self> {
465 let global_data = MsgGlobalData::decode(seq)?;
467
468 let security_params = seq.read_octet_string()?;
470
471 let data = if global_data.msg_flags.security_level.requires_priv() {
473 let encrypted = seq.read_octet_string()?;
475 V3MessageData::Encrypted(encrypted)
476 } else {
477 let scoped_pdu = ScopedPdu::decode(seq)?;
479 V3MessageData::Plaintext(scoped_pdu)
480 };
481
482 Ok(Self {
483 global_data,
484 security_params,
485 data,
486 })
487 }
488
489 pub fn discovery_request(msg_id: i32) -> Self {
494 let global_data = MsgGlobalData::new(
495 msg_id,
496 65507, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
498 );
499
500 let security_params = crate::v3::UsmSecurityParams::empty().encode();
502
503 let pdu = Pdu::get_request(0, &[]);
505 let scoped_pdu = ScopedPdu::with_empty_context(pdu);
506
507 Self::new(global_data, security_params, scoped_pdu)
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514 use crate::oid;
515
516 #[test]
517 fn test_security_level_flags() {
518 assert_eq!(SecurityLevel::NoAuthNoPriv.to_flags(), 0x00);
519 assert_eq!(SecurityLevel::AuthNoPriv.to_flags(), 0x01);
520 assert_eq!(SecurityLevel::AuthPriv.to_flags(), 0x03);
521
522 assert_eq!(
523 SecurityLevel::from_flags(0x00),
524 Some(SecurityLevel::NoAuthNoPriv)
525 );
526 assert_eq!(
527 SecurityLevel::from_flags(0x01),
528 Some(SecurityLevel::AuthNoPriv)
529 );
530 assert_eq!(
531 SecurityLevel::from_flags(0x03),
532 Some(SecurityLevel::AuthPriv)
533 );
534 assert_eq!(SecurityLevel::from_flags(0x02), None); }
536
537 #[test]
538 fn security_level_try_from_u8() {
539 assert_eq!(
540 SecurityLevel::try_from(0x00),
541 Ok(SecurityLevel::NoAuthNoPriv)
542 );
543 assert_eq!(SecurityLevel::try_from(0x01), Ok(SecurityLevel::AuthNoPriv));
544 assert_eq!(SecurityLevel::try_from(0x03), Ok(SecurityLevel::AuthPriv));
545 assert_eq!(SecurityLevel::try_from(0x02), Err(0x02));
546 }
547
548 #[test]
549 fn security_level_into_u8() {
550 assert_eq!(u8::from(SecurityLevel::NoAuthNoPriv), 0x00);
551 assert_eq!(u8::from(SecurityLevel::AuthNoPriv), 0x01);
552 assert_eq!(u8::from(SecurityLevel::AuthPriv), 0x03);
553 }
554
555 #[test]
556 fn test_msg_flags_roundtrip() {
557 let flags = MsgFlags::new(SecurityLevel::AuthPriv, true);
558 let byte = flags.to_byte();
559 assert_eq!(byte, 0x07); let decoded = MsgFlags::from_byte(byte).unwrap();
562 assert_eq!(decoded.security_level, SecurityLevel::AuthPriv);
563 assert!(decoded.reportable);
564 }
565
566 #[test]
567 fn test_msg_global_data_roundtrip() {
568 let global =
569 MsgGlobalData::new(12345, 1472, MsgFlags::new(SecurityLevel::AuthNoPriv, true));
570
571 let mut buf = EncodeBuf::new();
572 global.encode(&mut buf);
573 let encoded = buf.finish();
574
575 let mut decoder = Decoder::new(encoded);
576 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
577
578 assert_eq!(decoded.msg_id, 12345);
579 assert_eq!(decoded.msg_max_size, 1472);
580 assert_eq!(decoded.msg_flags.security_level, SecurityLevel::AuthNoPriv);
581 assert!(decoded.msg_flags.reportable);
582 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
583 }
584
585 #[test]
586 fn test_scoped_pdu_roundtrip() {
587 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
588 let scoped = ScopedPdu::new(b"engine".as_slice(), b"ctx".as_slice(), pdu);
589
590 let mut buf = EncodeBuf::new();
591 scoped.encode(&mut buf);
592 let encoded = buf.finish();
593
594 let mut decoder = Decoder::new(encoded);
595 let decoded = ScopedPdu::decode(&mut decoder).unwrap();
596
597 assert_eq!(decoded.context_engine_id.as_ref(), b"engine");
598 assert_eq!(decoded.context_name.as_ref(), b"ctx");
599 assert_eq!(decoded.pdu.request_id, 42);
600 }
601
602 #[test]
603 fn test_v3_message_plaintext_roundtrip() {
604 let global =
605 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
606 let pdu = Pdu::get_request(42, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
607 let scoped = ScopedPdu::with_empty_context(pdu);
608 let msg = V3Message::new(global, Bytes::from_static(b"usm-params"), scoped);
609
610 let encoded = msg.encode();
611 let decoded = V3Message::decode(encoded).unwrap();
612
613 assert_eq!(decoded.global_data.msg_id, 100);
614 assert_eq!(decoded.security_level(), SecurityLevel::NoAuthNoPriv);
615 assert_eq!(decoded.security_params.as_ref(), b"usm-params");
616
617 let scoped_pdu = decoded.scoped_pdu().unwrap();
618 assert_eq!(scoped_pdu.pdu.request_id, 42);
619 }
620
621 #[test]
622 fn test_v3_message_encrypted_roundtrip() {
623 let global = MsgGlobalData::new(200, 1472, MsgFlags::new(SecurityLevel::AuthPriv, false));
624 let msg = V3Message::new_encrypted(
625 global,
626 Bytes::from_static(b"usm-params"),
627 Bytes::from_static(b"encrypted-data"),
628 );
629
630 let encoded = msg.encode();
631 let decoded = V3Message::decode(encoded).unwrap();
632
633 assert_eq!(decoded.global_data.msg_id, 200);
634 assert_eq!(decoded.security_level(), SecurityLevel::AuthPriv);
635
636 match &decoded.data {
637 V3MessageData::Encrypted(data) => {
638 assert_eq!(data.as_ref(), b"encrypted-data");
639 }
640 V3MessageData::Plaintext(_) => panic!("expected encrypted data"),
641 }
642 }
643
644 #[test]
645 fn test_msg_global_data_rejects_msg_max_size_below_minimum() {
646 let global = MsgGlobalData {
648 msg_id: 100,
649 msg_max_size: 400, msg_flags: MsgFlags::new(SecurityLevel::NoAuthNoPriv, true),
651 msg_security_model: SecurityModel::Usm,
652 };
653
654 let mut buf = EncodeBuf::new();
655 global.encode(&mut buf);
656 let encoded = buf.finish();
657
658 let mut decoder = Decoder::new(encoded);
659 let result = MsgGlobalData::decode(&mut decoder);
660
661 assert!(result.is_err());
662 assert!(matches!(
663 *result.unwrap_err(),
664 Error::MalformedResponse { .. }
665 ));
666 }
667
668 #[test]
669 fn test_msg_global_data_accepts_msg_max_size_at_minimum() {
670 let global = MsgGlobalData::new(100, 484, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
672
673 let mut buf = EncodeBuf::new();
674 global.encode(&mut buf);
675 let encoded = buf.finish();
676
677 let mut decoder = Decoder::new(encoded);
678 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
679
680 assert_eq!(decoded.msg_max_size, 484);
681 }
682
683 #[test]
684 fn test_msg_global_data_rejects_unknown_security_model() {
685 let mut buf = EncodeBuf::new();
688 buf.push_sequence(|buf| {
689 buf.push_integer(99); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(100); });
694 let encoded = buf.finish();
695
696 let mut decoder = Decoder::new(encoded);
697 let result = MsgGlobalData::decode(&mut decoder);
698
699 assert!(result.is_err());
700 assert!(matches!(
701 *result.unwrap_err(),
702 Error::MalformedResponse { .. }
703 ));
704 }
705
706 #[test]
707 fn test_msg_global_data_accepts_usm_security_model() {
708 let global =
710 MsgGlobalData::new(100, 1472, MsgFlags::new(SecurityLevel::NoAuthNoPriv, true));
711
712 let mut buf = EncodeBuf::new();
713 global.encode(&mut buf);
714 let encoded = buf.finish();
715
716 let mut decoder = Decoder::new(encoded);
717 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
718
719 assert_eq!(decoded.msg_security_model, SecurityModel::Usm);
720 }
721
722 #[test]
731 fn test_msg_global_data_rejects_negative_msg_id() {
732 let mut buf = EncodeBuf::new();
735 buf.push_sequence(|buf| {
736 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(-1); });
741 let encoded = buf.finish();
742
743 let mut decoder = Decoder::new(encoded);
744 let result = MsgGlobalData::decode(&mut decoder);
745
746 assert!(result.is_err());
747 assert!(matches!(
748 *result.unwrap_err(),
749 Error::MalformedResponse { .. }
750 ));
751 }
752
753 #[test]
754 fn test_msg_global_data_rejects_negative_msg_max_size() {
755 let mut buf = EncodeBuf::new();
758 buf.push_sequence(|buf| {
759 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(-1); buf.push_integer(100); });
764 let encoded = buf.finish();
765
766 let mut decoder = Decoder::new(encoded);
767 let result = MsgGlobalData::decode(&mut decoder);
768
769 assert!(result.is_err());
770 assert!(matches!(
771 *result.unwrap_err(),
772 Error::MalformedResponse { .. }
773 ));
774 }
775
776 #[test]
777 fn test_msg_global_data_accepts_msg_id_at_zero() {
778 let mut buf = EncodeBuf::new();
780 buf.push_sequence(|buf| {
781 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(0); });
786 let encoded = buf.finish();
787
788 let mut decoder = Decoder::new(encoded);
789 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
790
791 assert_eq!(decoded.msg_id, 0);
792 }
793
794 #[test]
795 fn test_msg_global_data_accepts_msg_id_at_maximum() {
796 let mut buf = EncodeBuf::new();
798 buf.push_sequence(|buf| {
799 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(1472); buf.push_integer(i32::MAX); });
804 let encoded = buf.finish();
805
806 let mut decoder = Decoder::new(encoded);
807 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
808
809 assert_eq!(decoded.msg_id, i32::MAX);
810 }
811
812 #[test]
813 fn test_msg_global_data_accepts_msg_max_size_at_maximum() {
814 let mut buf = EncodeBuf::new();
816 buf.push_sequence(|buf| {
817 buf.push_integer(3); buf.push_octet_string(&[0x04]); buf.push_integer(i32::MAX); buf.push_integer(100); });
822 let encoded = buf.finish();
823
824 let mut decoder = Decoder::new(encoded);
825 let decoded = MsgGlobalData::decode(&mut decoder).unwrap();
826
827 assert_eq!(decoded.msg_max_size, i32::MAX);
828 }
829}