Skip to main content

wireforge_app/netbios/
parser.rs

1use super::types::NetbiosNameType;
2use wireforge_core::util::read_u16be;
3
4pub const NETBIOS_NS_MIN_LEN: usize = 12;
5
6#[derive(Debug, Clone)]
7pub struct NetbiosNsPacket<'a> { buf: &'a [u8] }
8impl<'a> NetbiosNsPacket<'a> {
9    pub fn new(buf:&'a[u8])->Option<Self> { if buf.len()<NETBIOS_NS_MIN_LEN{return None;} Some(Self{buf}) }
10    pub fn transaction_id(&self)->u16 { read_u16be(&self.buf[..2]) }
11    pub fn flags(&self)->u16 { read_u16be(&self.buf[2..4]) }
12    pub fn is_query(&self)->bool { self.flags()&0x8000==0 }
13    pub fn opcode(&self)->u8 { ((self.flags()>>11)&0x0F)as u8 }
14    pub fn is_broadcast(&self)->bool { self.flags()&0x0010!=0 }
15    pub fn is_truncated(&self)->bool { self.flags()&0x0002!=0 }
16    pub fn is_recursion_desired(&self)->bool { self.flags()&0x0100!=0 }
17    pub fn question_name(&self)->String { decode_nb_name(&self.buf[12..44].try_into().unwrap_or([0u8;32])) }
18    pub fn question_type(&self)->NetbiosNameType { NetbiosNameType::from(read_u16be(&self.buf[44..46])) }
19}
20
21fn decode_nb_name(encoded: &[u8;32]) -> String {
22    let mut name=String::with_capacity(16);
23    for i in 0..16 {
24        let hi=encoded[i*2].wrapping_sub(b'A');
25        let lo=encoded[i*2+1].wrapping_sub(b'A');
26        name.push(((hi<<4)|lo)as char);
27    }
28    name.trim_end().to_string()
29}
30
31#[cfg(test)]mod tests{use super::*;
32    #[test]fn decode_workstation(){
33        let mut enc=[0u8;32];for(i,b)in b"WORKSTATION1    ".iter().enumerate(){enc[i*2]=(b>>4)+0x41;enc[i*2+1]=(b&0x0F)+0x41;}
34        assert_eq!(decode_nb_name(&enc),"WORKSTATION1");
35    }
36    #[test]fn parse_name_query(){
37        let mut data=vec![0x00,0x01,0x01,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00];
38        let mut enc=[0u8;32];let name=b"HOST1           ";for(i,b)in name.iter().enumerate(){enc[i*2]=(b>>4)+0x41;enc[i*2+1]=(b&0x0F)+0x41;}
39        data.extend_from_slice(&enc);data.extend_from_slice(&[0x00,0x20,0x00,0x01]);
40        let p=NetbiosNsPacket::new(&data).unwrap();
41        assert!(p.is_query());assert_eq!(p.question_name(),"HOST1");assert_eq!(p.question_type(),NetbiosNameType::NB);
42    }
43}