1use crate::{be_u48, NetbiosError};
2use nom_derive::*;
3use rusticata_macros::newtype_enum;
4use std::fmt;
5use std::net::Ipv4Addr;
6
7#[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#[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 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 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#[derive(Debug)]
125pub struct EncodedName(pub(crate) String);
126
127impl EncodedName {
128 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#[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#[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 #[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}