netbios_parser/
nbss_types.rs

1use crate::{be_u48, NetbiosError};
2use nom_derive::*;
3use rusticata_macros::newtype_enum;
4use std::fmt;
5use std::net::Ipv4Addr;
6
7/// NetBIOS Name Service Header
8#[derive(Debug, PartialEq)]
9pub struct NbssHeader {
10    pub name_trn_id: u16,
11    pub(crate) fields_16_32: u16,
12    pub qdcount: u16,
13    pub ancount: u16,
14    pub nscount: u16,
15    pub arcount: u16,
16}
17
18impl NbssHeader {
19    pub const fn size() -> usize {
20        12
21    }
22
23    pub fn opcode_r(&self) -> bool {
24        self.fields_16_32 & (1 << 15) != 0
25    }
26
27    pub fn request(&self) -> bool {
28        self.fields_16_32 & (1 << 15) == 0
29    }
30
31    pub fn response(&self) -> bool {
32        self.fields_16_32 & (1 << 15) != 0
33    }
34
35    pub fn opcode(&self) -> u8 {
36        (self.fields_16_32 >> 10 & 0b1_1111) as u8
37    }
38
39    pub fn nm_flags(&self) -> NMFlags {
40        NMFlags((self.fields_16_32 >> 4 & 0b0111_1111) as u8)
41    }
42
43    pub fn rcode(&self) -> RCode {
44        RCode((self.fields_16_32 & 0b1111) as u8)
45    }
46}
47
48#[derive(Clone, Copy, PartialEq, Eq)]
49pub struct NMFlags(pub u8);
50
51newtype_enum! {
52    impl debug NMFlags {
53        Broadcast = 1,
54        RecursionAvailable = 0b1000,
55        RecursionDesired = 0b1_0000,
56        Truncation = 0b10_0000,
57    }
58}
59
60#[derive(Clone, Copy, PartialEq, Eq)]
61pub struct RCode(pub u8);
62
63newtype_enum! {
64    impl debug RCode {
65        NoErr = 0,
66        FmtErr = 0x1,
67        SrvErr = 0x2,
68        ImpErr = 0x4,
69        RfsErr = 0x5,
70        ActErr = 0x6,
71        CftErr = 0x7,
72    }
73}
74
75/// NetBIOS Name
76#[derive(Debug, PartialEq)]
77pub struct NetbiosName {
78    pub nb_name: String,
79    pub nb_type: NetbiosNameType,
80}
81
82impl NetbiosName {
83    pub fn from_bytes(i: &[u8]) -> Result<Self, NetbiosError<&'static [u8]>> {
84        if i.len() != 16 {
85            return Err(NetbiosError::InvalidNameLength);
86        }
87        let nb_type = NetbiosNameType(i[15]);
88        let mut s = String::new();
89        for b in &i[..15] {
90            // remove padding (stop at first space)
91            if *b == 0x20 {
92                break;
93            }
94            s.push(*b as char);
95        }
96        Ok(NetbiosName {
97            nb_name: s,
98            nb_type,
99        })
100    }
101
102    /// Attempt to decode the NetBIOS Name and return the decoded name and type
103    pub fn decode(s: &str) -> Result<Self, NetbiosError<&[u8]>> {
104        let i = s.as_bytes();
105        if i.len() != 32 {
106            return Err(NetbiosError::InvalidNameLength);
107        }
108        let mut s = String::new();
109        for idx in (0..32).step_by(2) {
110            let c = (i[idx].wrapping_sub(0x41) << 4) | (i[idx + 1].wrapping_sub(0x41) & 0x4f);
111            s.push(c as char);
112        }
113        NetbiosName::from_bytes(s.as_bytes())
114    }
115}
116
117impl fmt::Display for NetbiosName {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_fmt(format_args!("{}<{:x}>", self.nb_name, self.nb_type.0))
120    }
121}
122
123/// NetBIOS Name
124#[derive(Debug)]
125pub struct EncodedName(pub(crate) String);
126
127impl EncodedName {
128    /// Attempt to decode the NetBIOS Name and return the decoded name and type
129    pub fn decode(&self) -> Result<NetbiosName, NetbiosError<&[u8]>> {
130        NetbiosName::decode(&self.0)
131    }
132
133    pub fn raw_str(&self) -> &str {
134        self.0.as_ref()
135    }
136}
137
138/// NetBIOS Name Service Name Query
139#[derive(Debug)]
140pub struct NetbiosQuestion {
141    pub qname: EncodedName,
142    pub qtype: QType,
143    pub qclass: RClass,
144}
145
146#[derive(Clone, Copy, PartialEq, Eq, NomBE)]
147#[nom(GenericErrors)]
148pub struct QType(pub u16);
149
150newtype_enum! {
151    impl debug QType {
152        NB = 0x0020,
153        NBSTAT = 0x0021,
154    }
155}
156
157/// NetBIOS Name Service Packet
158#[derive(Debug)]
159pub struct NbssPacket<'a> {
160    pub header: NbssHeader,
161    pub questions: Vec<NetbiosQuestion>,
162    pub rr_answer: Vec<NetbiosResource<'a>>,
163    pub rr_authority: Vec<NetbiosResource<'a>>,
164    pub rr_additional: Vec<NetbiosResource<'a>>,
165}
166
167#[derive(Clone, Copy, PartialEq, Eq)]
168pub struct NetbiosNameType(pub u8);
169
170newtype_enum! {
171    impl debug NetbiosNameType {
172        Workstation = 0,
173        DomainMasterBrowser = 0x1b,
174        LocalMasterBrowser = 0x1d,
175        FileServer = 0x20,
176    }
177}
178
179#[derive(Debug)]
180pub struct NetbiosResource<'a> {
181    pub rr_name: EncodedName,
182    pub rr_type: RType,
183    pub rr_class: RClass,
184    pub ttl: u32,
185    pub rdata: RData<'a>,
186}
187
188#[derive(Clone, Copy, PartialEq, Eq, NomBE)]
189#[nom(GenericErrors)]
190pub struct RType(pub u16);
191
192newtype_enum! {
193    impl debug RType {
194        A = 0x0001,
195        NS = 0x0002,
196        NULL = 0x000A,
197        NB = 0x0020,
198        NBSTAT = 0x0021,
199    }
200}
201
202#[derive(Clone, Copy, PartialEq, Eq, NomBE)]
203#[nom(GenericErrors)]
204pub struct RClass(pub u16);
205
206newtype_enum! {
207    impl debug RClass {
208        IN = 0x0001,
209    }
210}
211
212#[derive(Debug, PartialEq)]
213pub enum RData<'a> {
214    NB {
215        nb_flags: u16,
216        nb_address: Ipv4Addr,
217    },
218    NBStat {
219        names: Vec<NodeName>,
220        stats: NodeStatistics,
221    },
222    Unknown(&'a [u8]),
223}
224
225#[derive(Debug, PartialEq)]
226pub struct NodeName {
227    pub name: NetbiosName,
228    pub name_flags: u16,
229}
230
231#[derive(Debug, Default, PartialEq, NomBE)]
232#[nom(GenericErrors)]
233pub struct NodeStatistics {
234    // only 48 bits are used
235    #[nom(Parse = "be_u48")]
236    pub unit_id: u64,
237    pub jumpers: u8,
238    pub test_result: u8,
239    pub version_number: u16,
240    pub stats_period: u16,
241    pub num_crc: u16,
242    pub num_align_errors: u16,
243    pub num_collisions: u16,
244    pub num_send_aborts: u16,
245    pub num_good_sends: u32,
246    pub num_good_recvs: u32,
247    pub num_retransmits: u16,
248    pub num_no_res_conditions: u16,
249    pub num_free_cmd_blocks: u16,
250    pub num_total_cmd_blocks: u16,
251    pub max_total_cmd_blocks: u16,
252    pub num_pending_sessions: u16,
253    pub max_pending_sessions: u16,
254    pub max_total_sessions: u16,
255    pub session_data_packet_size: u16,
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn header_fields() {
264        let h = NbssHeader {
265            name_trn_id: 0xde2b,
266            fields_16_32: 0x0110,
267            qdcount: 0,
268            ancount: 0,
269            nscount: 0,
270            arcount: 0,
271        };
272        assert_eq!(h.opcode_r(), false);
273        assert!(h.request());
274        assert_eq!(h.opcode(), 0);
275        assert_eq!(h.nm_flags(), NMFlags(0x11));
276        assert!(h.nm_flags().0 & NMFlags::Broadcast.0 != 0);
277        assert!(h.nm_flags().0 & NMFlags::RecursionDesired.0 != 0);
278        assert_eq!(h.rcode(), RCode::NoErr);
279    }
280}