use alloc::format;
use alloc::string::String;
use crate::ast::Protocol;
use crate::reverse::{ProtocolDissection, bytes_to_hex_lower, push_if};
use super::snmp1::int_val;
use super::snmp2::{append_v2_pdu_body_tokens, v2_pdu_keyword};
use super::{ok_dissect, push_str_if};
pub(crate) fn dissect_snmp3(payload: &[u8]) -> Option<ProtocolDissection> {
use rasn_snmp::v3::{Message, ScopedPduData, USMSecurityParameters};
let (msg, rem): (Message, &[u8]) = rasn::ber::decode_with_remainder(payload).ok()?;
if int_val::<u32>(&msg.version) != Some(3) {
return None;
}
let flags = msg.global_data.flags.as_ref().first().copied().unwrap_or(0);
if (flags & 0x02) != 0 {
return None;
}
let ScopedPduData::CleartextPdu(scoped) = &msg.scoped_data else {
return None;
};
let usm: USMSecurityParameters = rasn::ber::decode(msg.security_parameters.as_ref()).ok()?;
let user = core::str::from_utf8(usm.user_name.as_ref()).ok()?;
let engine_id_hex = bytes_to_hex_lower(usm.authoritative_engine_id.as_ref());
let boots = int_val::<u32>(&usm.authoritative_engine_boots).unwrap_or(0);
let time = int_val::<u32>(&usm.authoritative_engine_time).unwrap_or(0);
let tokens = format_scoped_pdu_tokens(scoped, user, &engine_id_hex, boots, time)?;
ok_dissect(Protocol::Snmp3, tokens, rem.to_vec())
}
fn format_scoped_pdu_tokens(
scoped_pdu: &rasn_snmp::v3::ScopedPdu,
user: &str,
engine_id_hex: &str,
engine_boots: u32,
engine_time: u32,
) -> Option<String> {
let mut tokens = format!("snmp3 {}", v2_pdu_keyword(&scoped_pdu.data)?);
append_usm_header_tokens(&mut tokens, user, engine_id_hex, engine_boots, engine_time);
push_str_if(
&mut tokens,
"contextname",
core::str::from_utf8(scoped_pdu.name.as_ref()).ok()?,
"",
);
append_v2_pdu_body_tokens(&mut tokens, &scoped_pdu.data)?;
Some(tokens)
}
fn append_usm_header_tokens(
tokens: &mut String,
user: &str,
engine_id_hex: &str,
boots: u32,
time: u32,
) {
push_str_if(tokens, "user", user, "admin");
if !engine_id_hex.is_empty() {
push_str_if(tokens, "engineid", engine_id_hex, "8000000001");
}
push_if(tokens, "engineboots", boots, 0);
push_if(tokens, "enginetime", time, 0);
}