wireforge-app 1.0.3

Application-layer protocol parsers/builders and pcap file I/O
Documentation
use super::types::NetbiosNameType;
use wireforge_core::util::read_u16be;

pub const NETBIOS_NS_MIN_LEN: usize = 12;

pub struct NetbiosNsPacket<'a> { buf: &'a [u8] }
impl<'a> NetbiosNsPacket<'a> {
    pub fn new(buf:&'a[u8])->Option<Self> { if buf.len()<NETBIOS_NS_MIN_LEN{return None;} Some(Self{buf}) }
    pub fn transaction_id(&self)->u16 { read_u16be(&self.buf[..2]) }
    pub fn flags(&self)->u16 { read_u16be(&self.buf[2..4]) }
    pub fn is_query(&self)->bool { self.flags()&0x8000==0 }
    pub fn opcode(&self)->u8 { ((self.flags()>>11)&0x0F)as u8 }
    pub fn is_broadcast(&self)->bool { self.flags()&0x0010!=0 }
    pub fn is_truncated(&self)->bool { self.flags()&0x0002!=0 }
    pub fn is_recursion_desired(&self)->bool { self.flags()&0x0100!=0 }
    pub fn question_name(&self)->String { decode_nb_name(&self.buf[12..44].try_into().unwrap_or([0u8;32])) }
    pub fn question_type(&self)->NetbiosNameType { NetbiosNameType::from(read_u16be(&self.buf[44..46])) }
}

fn decode_nb_name(encoded: &[u8;32]) -> String {
    let mut name=String::with_capacity(16);
    for i in 0..16 {
        let hi=encoded[i*2].wrapping_sub(b'A');
        let lo=encoded[i*2+1].wrapping_sub(b'A');
        name.push(((hi<<4)|lo)as char);
    }
    name.trim_end().to_string()
}

#[cfg(test)]mod tests{use super::*;
    #[test]fn decode_workstation(){
        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;}
        assert_eq!(decode_nb_name(&enc),"WORKSTATION1");
    }
    #[test]fn parse_name_query(){
        let mut data=vec![0x00,0x01,0x01,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00];
        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;}
        data.extend_from_slice(&enc);data.extend_from_slice(&[0x00,0x20,0x00,0x01]);
        let p=NetbiosNsPacket::new(&data).unwrap();
        assert!(p.is_query());assert_eq!(p.question_name(),"HOST1");assert_eq!(p.question_type(),NetbiosNameType::NB);
    }
}