/src/ndpi/src/lib/protocols/netbios.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * netbios.c |
3 | | * |
4 | | * Copyright (C) 2011-25 - ntop.org |
5 | | * Copyright (C) 2009-11 - ipoque GmbH |
6 | | * |
7 | | * This file is part of nDPI, an open source deep packet inspection |
8 | | * library based on the OpenDPI and PACE technology by ipoque GmbH |
9 | | * |
10 | | * nDPI is free software: you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation, either version 3 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * nDPI is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with nDPI. If not, see <http://www.gnu.org/licenses/>. |
22 | | * |
23 | | */ |
24 | | |
25 | | |
26 | | #include "ndpi_protocol_ids.h" |
27 | | |
28 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_NETBIOS |
29 | | |
30 | | #include "ndpi_api.h" |
31 | | #include "ndpi_private.h" |
32 | | |
33 | | /* ****************************************************************** */ |
34 | | |
35 | | struct netbios_header { |
36 | | u_int16_t transaction_id, flags, questions, answer_rrs, authority_rrs, additional_rrs; |
37 | | }; |
38 | | |
39 | | /* ****************************************************************** */ |
40 | | |
41 | 0 | static int is_printable_char(unsigned char c) { |
42 | 0 | return(((c >= 0x20) && (c <= 0x7e)) ? 1 : 0); |
43 | 0 | } |
44 | | |
45 | | /* ****************************************************************** */ |
46 | | |
47 | 0 | static int is_stop_char(u_char c) { |
48 | 0 | return(((c < 'A') || (c > 'P')) ? 1 : 0); |
49 | 0 | } |
50 | | |
51 | | /* ****************************************************************** */ |
52 | | |
53 | | /* The function below has been inherited by tcpdump */ |
54 | 0 | int ndpi_netbios_name_interpret(u_char *in, u_int in_len, u_char *out, u_int out_len) { |
55 | 0 | u_int ret = 0, len, idx = in_len, out_idx = 0; |
56 | |
|
57 | 0 | len = in[0] / 2; |
58 | 0 | in++, in_len--; |
59 | | |
60 | 0 | out_len--; |
61 | 0 | out[out_idx] = 0; |
62 | |
|
63 | 0 | if((len > out_len) || (len < 1) || ((2*len) > in_len)) |
64 | 0 | return(-1); |
65 | | |
66 | 0 | while((len--) && (out_idx < out_len)) { |
67 | 0 | if((idx < 2) || is_stop_char(in[0]) || is_stop_char(in[1])) { |
68 | 0 | out[out_idx] = 0; |
69 | 0 | break; |
70 | 0 | } |
71 | | |
72 | 0 | out[out_idx] = ((in[0] - 'A') << 4) + (in[1] - 'A'); |
73 | 0 | in += 2, idx -= 2; |
74 | |
|
75 | 0 | if(is_printable_char(out[out_idx])) |
76 | 0 | out_idx++, ret++; |
77 | 0 | } |
78 | | |
79 | | /* Trim trailing whitespace from the returned string */ |
80 | 0 | if(out_idx > 0) { |
81 | 0 | out[out_idx] = 0; |
82 | 0 | out_idx--; |
83 | |
|
84 | 0 | while((out_idx > 0) && (out[out_idx] == ' ')) { |
85 | 0 | out[out_idx] = 0; |
86 | 0 | out_idx--; |
87 | 0 | } |
88 | 0 | } |
89 | |
|
90 | 0 | return(ret); |
91 | 0 | } |
92 | | |
93 | | /* ****************************************************************** */ |
94 | | |
95 | | static void ndpi_int_netbios_add_connection(struct ndpi_detection_module_struct *ndpi_struct, |
96 | | struct ndpi_flow_struct *flow, |
97 | 0 | u_int16_t sub_protocol) { |
98 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
99 | |
|
100 | 0 | unsigned char name[64]; |
101 | 0 | u_int off = packet->payload[12] == 0x20 ? 12 : 14; |
102 | |
|
103 | 0 | if((off < packet->payload_packet_len) |
104 | 0 | && ndpi_netbios_name_interpret((unsigned char*)&packet->payload[off], |
105 | 0 | (u_int)(packet->payload_packet_len - off), name, sizeof(name)-1) > 0) { |
106 | 0 | ndpi_hostname_sni_set(flow, (const u_int8_t *)name, strlen((char *)name), NDPI_HOSTNAME_NORM_ALL); |
107 | |
|
108 | 0 | ndpi_check_dga_name(ndpi_struct, flow, flow->host_server_name, 1, 1, 0); |
109 | 0 | } |
110 | |
|
111 | 0 | if(sub_protocol == NDPI_PROTOCOL_UNKNOWN) |
112 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_NETBIOS, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
113 | 0 | else |
114 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, sub_protocol, NDPI_PROTOCOL_NETBIOS, NDPI_CONFIDENCE_DPI); |
115 | 0 | } |
116 | | |
117 | | /* ****************************************************************** */ |
118 | | |
119 | | static void ndpi_search_netbios(struct ndpi_detection_module_struct *ndpi_struct, |
120 | 0 | struct ndpi_flow_struct *flow) { |
121 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
122 | 0 | u_int16_t dport; |
123 | |
|
124 | 0 | NDPI_LOG_DBG(ndpi_struct, "search netbios\n"); |
125 | |
|
126 | 0 | if(packet->udp != NULL) { |
127 | 0 | dport = ntohs(packet->udp->dest); |
128 | | |
129 | | /*check standard NETBIOS over udp to port 137 */ |
130 | 0 | if((dport == 137 || 0) && packet->payload_packet_len >= 50) { |
131 | 0 | struct netbios_header h; |
132 | |
|
133 | 0 | memcpy(&h, packet->payload, sizeof(struct netbios_header)); |
134 | 0 | h.transaction_id = ntohs(h.transaction_id), h.flags = ntohs(h.flags), |
135 | 0 | h.questions = ntohs(h.questions), h.answer_rrs = ntohs(h.answer_rrs), |
136 | 0 | h.authority_rrs = ntohs(h.authority_rrs), h.additional_rrs = ntohs(h.additional_rrs); |
137 | |
|
138 | 0 | NDPI_LOG_DBG(ndpi_struct, "found netbios port 137 and payload_packet_len 50\n"); |
139 | |
|
140 | 0 | if(h.flags == 0 && |
141 | 0 | h.questions == 1 && |
142 | 0 | h.answer_rrs == 0 && |
143 | 0 | h.authority_rrs == 0 && h.additional_rrs == 0) { |
144 | |
|
145 | 0 | NDPI_LOG_INFO(ndpi_struct, "found netbios with questions = 1 and answers = 0, authority = 0 \n"); |
146 | |
|
147 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
148 | 0 | return; |
149 | 0 | } |
150 | | |
151 | 0 | if(((h.flags & 0x8710) == 0x10) && |
152 | 0 | h.questions == 1 && |
153 | 0 | h.answer_rrs == 0 && |
154 | 0 | h.authority_rrs == 0) { |
155 | |
|
156 | 0 | NDPI_LOG_INFO(ndpi_struct, "found netbios with questions = 1 and answers = 0, authority = 0 and broadcast \n"); |
157 | |
|
158 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
159 | 0 | return; |
160 | 0 | } |
161 | | |
162 | 0 | if(packet->payload[2] == 0x80 && |
163 | 0 | h.questions == 1 && |
164 | 0 | h.answer_rrs == 0 && |
165 | 0 | h.authority_rrs == 0 && h.additional_rrs == 1) { |
166 | |
|
167 | 0 | NDPI_LOG_INFO(ndpi_struct, "found netbios with questions = 1 and answers, authority, additional = 0 \n"); |
168 | |
|
169 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
170 | 0 | return; |
171 | 0 | } |
172 | | |
173 | 0 | if(h.flags == 0x4000 && |
174 | 0 | h.questions == 1 && |
175 | 0 | h.answer_rrs == 0 && |
176 | 0 | h.authority_rrs == 0 && h.additional_rrs == 1) { |
177 | |
|
178 | 0 | NDPI_LOG_INFO(ndpi_struct, "found netbios with questions = 1 and answers = 0, authority = 0 \n"); |
179 | |
|
180 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
181 | 0 | return; |
182 | 0 | } |
183 | | |
184 | 0 | if(h.flags == 0x8400 && |
185 | 0 | h.questions == 0 && |
186 | 0 | h.answer_rrs == 1 && |
187 | 0 | h.authority_rrs == 0 && h.additional_rrs == 0) { |
188 | |
|
189 | 0 | NDPI_LOG_INFO(ndpi_struct, |
190 | 0 | "found netbios with flag 8400 questions = 0 and answers = 1, authority, additional = 0 \n"); |
191 | |
|
192 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
193 | 0 | return; |
194 | 0 | } |
195 | | |
196 | 0 | if(h.flags == 0x8500 && |
197 | 0 | h.questions == 0 && |
198 | 0 | h.answer_rrs == 1 && |
199 | 0 | h.authority_rrs == 0 && h.additional_rrs == 0) { |
200 | |
|
201 | 0 | NDPI_LOG_INFO(ndpi_struct, |
202 | 0 | "found netbios with flag 8500 questions = 0 and answers = 1, authority, additional = 0 \n"); |
203 | |
|
204 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
205 | 0 | return; |
206 | 0 | } |
207 | | |
208 | 0 | if(((h.flags == 0x2900) || (h.flags == 0x2910)) && |
209 | 0 | h.questions == 1 && |
210 | 0 | h.answer_rrs == 0 && |
211 | 0 | h.authority_rrs == 0 && h.additional_rrs == 1) { |
212 | |
|
213 | 0 | NDPI_LOG_INFO(ndpi_struct, |
214 | 0 | "found netbios with flag 2910, questions = 1 and answers, authority=0, additional = 1 \n"); |
215 | |
|
216 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
217 | 0 | return; |
218 | 0 | } |
219 | | |
220 | 0 | if(h.flags == 0xAD86 && |
221 | 0 | h.questions == 0 && |
222 | 0 | h.answer_rrs == 1 && |
223 | 0 | h.authority_rrs == 0 && h.additional_rrs == 0) { |
224 | |
|
225 | 0 | NDPI_LOG_INFO(ndpi_struct, |
226 | 0 | "found netbios with flag ad86 questions = 0 and answers = 1, authority, additional = 0 \n"); |
227 | |
|
228 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
229 | 0 | return; |
230 | 0 | } |
231 | | |
232 | 0 | if(h.flags == 0x0110 && |
233 | 0 | h.questions == 1 && |
234 | 0 | h.answer_rrs == 0 && |
235 | 0 | h.authority_rrs == 0 && h.additional_rrs == 0) { |
236 | |
|
237 | 0 | NDPI_LOG_INFO(ndpi_struct, |
238 | 0 | "found netbios with flag 0110 questions = 1 and answers = 0, authority, additional = 0 \n"); |
239 | |
|
240 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
241 | 0 | return; |
242 | 0 | } |
243 | | |
244 | 0 | if((h.flags & 0xf800) == 0) { |
245 | 0 | NDPI_LOG_DBG2(ndpi_struct, "possible netbios name query request\n"); |
246 | |
|
247 | 0 | if(get_u_int16_t(packet->payload, 4) == htons(1) && |
248 | 0 | get_u_int16_t(packet->payload, 6) == 0 && |
249 | 0 | get_u_int16_t(packet->payload, 8) == 0 && get_u_int16_t(packet->payload, 10) == 0) { |
250 | | |
251 | | /* name is encoded as described in rfc883 */ |
252 | 0 | u_int8_t name_length = packet->payload[12]; |
253 | |
|
254 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
255 | 0 | "possible netbios name query request, one question\n"); |
256 | |
|
257 | 0 | if(packet->payload_packet_len == 12 + 1 + name_length + 1 + 2 + 2) { |
258 | |
|
259 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
260 | 0 | "possible netbios name query request, length matches\n"); |
261 | | |
262 | | /* null terminated? */ |
263 | 0 | if(packet->payload[12 + name_length + 1] == 0 && |
264 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 2) == htons(0x0020) && |
265 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 4) == htons(0x0001)) { |
266 | |
|
267 | 0 | NDPI_LOG_INFO(ndpi_struct, |
268 | 0 | "found netbios name query request\n"); |
269 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
270 | 0 | return; |
271 | 0 | } |
272 | 0 | } |
273 | 0 | } |
274 | 0 | } else if((h.flags & 0xf800) == 0x8000) { |
275 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
276 | 0 | "possible netbios name query response\n"); |
277 | |
|
278 | 0 | if(get_u_int16_t(packet->payload, 4) == 0 && |
279 | 0 | get_u_int16_t(packet->payload, 6) == htons(1) && |
280 | 0 | get_u_int16_t(packet->payload, 8) == 0 && get_u_int16_t(packet->payload, 10) == 0) { |
281 | | |
282 | | /* name is encoded as described in rfc883 */ |
283 | 0 | u_int8_t name_length = packet->payload[12]; |
284 | |
|
285 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
286 | 0 | "possible netbios positive name query response, one answer\n"); |
287 | |
|
288 | 0 | if(packet->payload_packet_len >= 12 + 1 + name_length + 1 + 2 + 2) { |
289 | |
|
290 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
291 | 0 | "possible netbios name query response, length matches\n"); |
292 | | |
293 | | /* null terminated? */ |
294 | 0 | if(packet->payload[12 + name_length + 1] == 0 && |
295 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 2) == htons(0x0020) && |
296 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 4) == htons(0x0001)) { |
297 | |
|
298 | 0 | NDPI_LOG_INFO(ndpi_struct, |
299 | 0 | "found netbios name query response\n"); |
300 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
301 | 0 | return; |
302 | 0 | } |
303 | 0 | } |
304 | 0 | } else if(get_u_int16_t(packet->payload, 4) == 0 && |
305 | 0 | get_u_int16_t(packet->payload, 6) == 0 && |
306 | 0 | get_u_int16_t(packet->payload, 8) == 0 && get_u_int16_t(packet->payload, 10) == 0) { |
307 | | |
308 | | /* name is encoded as described in rfc883 */ |
309 | 0 | u_int8_t name_length = packet->payload[12]; |
310 | |
|
311 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
312 | 0 | "possible netbios negative name query response, one answer\n"); |
313 | |
|
314 | 0 | if(packet->payload_packet_len >= 12 + 1 + name_length + 1 + 2 + 2) { |
315 | |
|
316 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
317 | 0 | "possible netbios name query response, length matches\n"); |
318 | | |
319 | | /* null terminated? */ |
320 | 0 | if(packet->payload[12 + name_length + 1] == 0 && |
321 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 2) == htons(0x000A) && |
322 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 4) == htons(0x0001)) { |
323 | |
|
324 | 0 | NDPI_LOG_INFO(ndpi_struct, |
325 | 0 | "found netbios name query response\n"); |
326 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
327 | 0 | return; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | } else if(get_u_int16_t(packet->payload, 4) == 0 && |
331 | 0 | get_u_int16_t(packet->payload, 6) == 0 && |
332 | 0 | get_u_int16_t(packet->payload, 8) == htons(1) && get_u_int16_t(packet->payload, 10) == htons(1)) { |
333 | | |
334 | | /* name is encoded as described in rfc883 */ |
335 | 0 | u_int8_t name_length = packet->payload[12]; |
336 | |
|
337 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
338 | 0 | "possible netbios redirect name query response, one answer\n"); |
339 | |
|
340 | 0 | if(packet->payload_packet_len >= 12 + 1 + name_length + 1 + 2 + 2) { |
341 | |
|
342 | 0 | NDPI_LOG_DBG2(ndpi_struct, |
343 | 0 | "possible netbios name query response, length matches\n"); |
344 | | |
345 | | /* null terminated? */ |
346 | 0 | if(packet->payload[12 + name_length + 1] == 0 && |
347 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 2) == htons(0x0002) && |
348 | 0 | get_u_int16_t(packet->payload, 12 + name_length + 4) == htons(0x0001)) { |
349 | |
|
350 | 0 | NDPI_LOG_INFO(ndpi_struct, |
351 | 0 | "found netbios name query response\n"); |
352 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
353 | 0 | return; |
354 | 0 | } |
355 | 0 | } |
356 | 0 | } |
357 | 0 | } |
358 | | /* TODO: extend according to rfc1002 */ |
359 | 0 | } |
360 | | |
361 | | /* check standard NETBIOS over udp to port 138 */ |
362 | | |
363 | | /* netbios header token from http://www.protocolbase.net/protocols/protocol_NBDGM.php */ |
364 | | |
365 | 0 | if((dport == 138) && (packet->payload_packet_len >= 14)) { |
366 | 0 | u_int16_t netbios_len = ntohs(get_u_int16_t(packet->payload, 10)); |
367 | |
|
368 | 0 | if(netbios_len == packet->payload_packet_len - 14) { |
369 | 0 | NDPI_LOG_DBG2(ndpi_struct, "found netbios port 138 and payload length >= 112 \n"); |
370 | | |
371 | | /* TODO: ipv6 */ |
372 | 0 | if(packet->iph && packet->payload[0] >= 0x10 && packet->payload[0] <= 0x16) { |
373 | 0 | u_int32_t source_ip = ntohl(get_u_int32_t(packet->payload, 4)); |
374 | |
|
375 | 0 | NDPI_LOG_DBG2(ndpi_struct, "found netbios with MSG-type 0x10,0x11,0x12,0x13,0x14,0x15 or 0x16\n"); |
376 | |
|
377 | 0 | if(source_ip == ntohl(packet->iph->saddr)) { |
378 | 0 | int16_t leftover = netbios_len - 82; /* NetBIOS len */ |
379 | |
|
380 | 0 | NDPI_LOG_INFO(ndpi_struct, "found netbios with checked ip-address\n"); |
381 | |
|
382 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, (leftover > 0) ? NDPI_PROTOCOL_SMBV1 : NDPI_PROTOCOL_UNKNOWN); |
383 | 0 | return; |
384 | 0 | } |
385 | 0 | } |
386 | 0 | } |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | 0 | if(packet->tcp != NULL) { |
391 | 0 | dport = ntohs(packet->tcp->dest); |
392 | | |
393 | | /* destination port must be 139 */ |
394 | 0 | if(dport == 139) { |
395 | 0 | NDPI_LOG_DBG2(ndpi_struct, "found netbios with destination port 139\n"); |
396 | | |
397 | | /* payload_packet_len must be 72 */ |
398 | 0 | if(packet->payload_packet_len == 72) { |
399 | 0 | NDPI_LOG_DBG2(ndpi_struct, "found netbios with payload_packen_len = 72. \n"); |
400 | |
|
401 | 0 | if(packet->payload[0] == 0x81 && packet->payload[1] == 0 && ntohs(get_u_int16_t(packet->payload, 2)) == 68) { |
402 | 0 | NDPI_LOG_INFO(ndpi_struct, |
403 | 0 | "found netbios with session request = 81, flags=0 and length od following bytes = 68. \n"); |
404 | |
|
405 | 0 | ndpi_int_netbios_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN); |
406 | 0 | return; |
407 | 0 | } |
408 | 0 | } |
409 | 0 | } |
410 | |
|
411 | 0 | } |
412 | | |
413 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
414 | 0 | } |
415 | | |
416 | | /* ****************************************************************** */ |
417 | | |
418 | | void init_netbios_dissector(struct ndpi_detection_module_struct *ndpi_struct) |
419 | 1 | { |
420 | 1 | register_dissector("NETBIOS", ndpi_struct, |
421 | 1 | ndpi_search_netbios, |
422 | 1 | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
423 | 1 | 1, NDPI_PROTOCOL_NETBIOS); |
424 | 1 | } |