]> git.sesse.net Git - nbtscanner/blob - nbtscanner.c
Import nbtscanner 0.1.2.
[nbtscanner] / nbtscanner.c
1 /*
2  * nbtscanner -- a tool for scanning large networks for SMB servers.
3  *
4  * nbtscanner.c: nbtscanner main body. 
5  * Copyright (C) 2000 Steinar H. Gunderson
6  *
7  * Large amounts of code adapted from Samba (http://www.samba.org/)
8  * Copyright (C) Andrew Tridgell 1994-1998, and others.
9  * 
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program 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 General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <sys/time.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <arpa/inet.h>
34 #include <stdlib.h>
35
36 #include "nbtscanner.h"
37 #include "mysql_interface.h"
38 #include "configfile.h"
39 #include "nmb.h"
40 #include "socket.h"
41 #include "stats.h"
42 #include "packet.h"
43 #include "byteorder.h"
44 #include "util.h"
45 #include "id_list.h"
46
47 /* one global variable -- the UDP socket */
48 int server_fd;
49
50 void interpret_node_status(char *p, struct in_addr in)
51 {
52         int numnames = CVAL(p,0);
53         char hostname[32], fileservername[32], group[32];
54
55         /*
56          * hopefully this will be enough to identify the host as unknown
57          * (note that this is longer than the 16 char maximum NetBIOS
58          * name length)
59          */
60         strcpy(hostname, "-unknown-nbtscanner-");
61         strcpy(fileservername, "-unknown-nbtscanner-");
62         strcpy(group, "-unknown-nbtscanner-");
63
64         p += 1;
65         while (numnames--) {
66                 char qname[17];
67                 int type;
68                 int i;
69
70                 strncpy(qname,p,15);
71                 qname[15] = 0;
72                 type = CVAL(p,15);
73                 p += 16;
74
75                 for (i = strlen(qname); --i >= 0; ) {
76                         if (!isprint((int)qname[i])) qname[i] = '.';
77                 }
78
79                 /*
80                  * this is code duplication (see parse_nmb_name in nmb.c) 
81                  * but it obviously isn't done in our case :-)
82                  */
83                 for (i = strlen(qname); --i >= 0; ) {
84                         if (qname[i] == ' ') {
85                                 qname[i] = 0;
86                         } else {
87                                 break;
88                         }
89                 }
90
91                 /* <ACTIVE>, non-<GROUP> server hostname (type 0x00) */ 
92                 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
93                         strcpy(hostname, qname);
94                 }
95                 /* <ACTIVE>, non-<GROUP> server hostname (type 0x20) */ 
96                 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x20) {
97                         strcpy(fileservername, qname);
98                 }
99                 /* <ACTIVE>, <GROUP> group (type 0x00) */
100                 if ((p[0] & 0x80) == 0x80 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
101                         strcpy(group, qname);
102                 }
103
104                 p += 2;
105         }
106
107         if (use_mysql) {
108                 add_record_mysql(inet_ntoa(in), hostname, fileservername, group);
109         } else {
110                 printf("%s,%s,%s,%s\n", inet_ntoa(in), hostname, fileservername, group);
111         }
112 }
113
114 void resend_timed_out_packets(int fd)
115 {
116         int i;
117         struct timeval now;
118         struct nmb_packet nmb;
119
120         gettimeofday(&now, NULL);
121
122         for (i = 0; i < 65536; i++) {
123                 if (ids[i].in_use == 1 &&
124                     mydifftime(ids[i].timestamp, now) > retry_time) {
125                         if (ids[i].retries >= num_retries) {
126                                 id_mark_free(&ids[i]);
127
128                                 num_failed++;
129                                 if (use_mysql) {
130                                         delete_record_mysql(inet_ntoa(ids[i].sin));
131                                 }
132                                 print_stats(0);
133                         } else {
134                                 gettimeofday(&(ids[i].timestamp), NULL);
135                                 ids[i].retries++;
136                                 num_sent_total++;
137         
138                                 build_nbt_packet(&nmb, i);
139                                 send_packet(&nmb, ids[i].sin, 137, fd);
140                                 recv_nbt_packets(fd);   /* delay and empty buffer */
141                                 print_stats(0);
142                         }
143                 }
144         }
145 }
146
147 void send_nbt_packet(int fd, struct in_addr to_ip)
148 {
149         struct nmb_packet nmb;
150         static int name_trn_id = 0;
151         struct id_entry *i = id_get_free_id();
152
153         if (i == NULL) {
154                 /* uh-oh... try to remove timed-out IDs */
155                 if (id_cleanup(retry_time + 1000) == 0 ||
156                         (i=id_get_free_id()) == NULL) {
157                         if (verbosity >= 1) {
158                                 fprintf(stderr, "No free NBT IDs! Reduce "
159                                                 "the number of hosts, the "
160                                                 "number\n");
161                                 fprintf(stderr, "of retries, or the scanning "
162                                                 "speed. nbtscanner is now "
163                                                 "exiting.\n");
164                         }
165                         exit(1);
166                 }
167         }
168         name_trn_id = i->id;
169         i->sin = to_ip;
170         gettimeofday(&(i->timestamp), NULL);
171         i->retries = 0;
172
173         build_nbt_packet(&nmb, name_trn_id);
174
175         /* FIXME: what to do with sending errors? */
176         send_packet(&nmb, to_ip, 137, fd);
177
178         num_sent++;
179         num_sent_total++;
180         print_stats(0);
181 }
182
183 /*
184  * This function not only receives and handles any NBT replies, but
185  * also guarantees a delay of minimum delay_time.
186  */
187 void recv_nbt_packets(int fd)
188 {
189         struct nmb_packet *nmb;
190         struct in_addr in;
191         struct timeval start;
192         int dt = delay_time;
193
194         gettimeofday(&start, NULL);
195
196         while (dt > 0) {
197                 struct timeval now;
198
199                 print_stats(0);
200
201                 gettimeofday(&now, NULL);
202                 dt = delay_time - mydifftime(start, now);
203
204                 if (dt < 0) break;
205
206                 nmb = receive_packet(fd, dt, &in);
207                 if (nmb) {
208                         struct id_entry *i = &ids[nmb->header.name_trn_id];
209         
210                         if (!nmb->header.response) {
211                                 /* It's not for us */
212                                 free_nmb_packet(nmb);
213                                 continue;
214                         }
215                         if (i->in_use == 0) {
216                                 if (verbosity < 2) continue;
217
218                                 fprintf(stderr, "Warning: received NBT "
219                                                 "response for unused "
220                                                 "id %04x!\n",
221                                         i->id);
222                                 continue;
223                         }
224                         if (i->in_use == 2) {
225                                 if (verbosity < 2) continue;
226
227                                 fprintf(stderr, "Warning: received "
228                                                 "duplicate NBT response "
229                                                 "for id %04x!\n",       
230                                         i->id);
231                                 continue;
232                         }
233                         if (in.s_addr != i->sin.s_addr) {
234                                 char got_addr[32], want_addr[32];
235         
236                                 if (verbosity < 2) continue;
237
238                                 strcpy(got_addr, inet_ntoa(in));
239                                 strcpy(want_addr, inet_ntoa(i->sin));
240         
241                                 fprintf(stderr, "Warning: received NBT "
242                                                 "response for id %04x "
243                                                 "from %s instead of %s!\n",
244                                         i->id, got_addr, want_addr);
245                                 continue;
246                         }
247
248                         if (nmb->header.opcode != 0 ||
249                             nmb->header.nm_flags.bcast ||
250                             nmb->header.rcode ||
251                             !nmb->header.ancount ||
252                             nmb->answers->rr_type != 0x21) {
253                                 /* XXXX what do we do with this? could be a redirect, but
254                                    we'll discard it for the moment */
255                                 free_nmb_packet(nmb);
256                                 continue;
257                         }
258
259                         num_recv++;
260                         num_recv_retries = i->retries;
261         
262                         interpret_node_status(&nmb->answers->rdata[0], in);
263                         free_nmb_packet(nmb);
264         
265                         id_mark_free(i);
266                 }
267         }
268 }       
269
270 void scan_range(struct in_addr ip, int rangesize)
271 {
272         if (rangesize == 32) {
273                 /* scan a single IP */
274                 send_nbt_packet(server_fd, ip);
275                 recv_nbt_packets(server_fd);
276         } else if (rangesize == 31) {
277                 /* scan two IPs */
278                 send_nbt_packet(server_fd, ip);
279                 recv_nbt_packets(server_fd);
280
281                 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
282                 send_nbt_packet(server_fd, ip);
283                 recv_nbt_packets(server_fd);
284         } else {
285                 /* scan a range of IPs, minus the top and bottom one */
286                 int i;
287
288                 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);        /* skip bottom */
289
290                 for (i = 0; i < (1 << (32-rangesize)) - 2; i++) {
291                         send_nbt_packet(server_fd, ip);
292                         recv_nbt_packets(server_fd);
293
294                         ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
295                 }
296         }
297 }
298
299 int main(int argc, char *argv[])
300 {
301         int fd = open_sockets();
302         int i;
303
304         server_fd = fd;
305         parse_configfile();
306
307         if (use_mysql) {
308                 init_mysql(mysql_host, mysql_username, mysql_password);
309         }
310
311         for ( ;; ) {
312                 struct timeval start, now;
313                 unsigned int delay_ms;
314
315                 id_list_init();
316                 init_stats();
317
318                 gettimeofday(&start, NULL);
319
320                 for (i = 0; i < ranges; i++) {
321                         scan_range(scanrange[i], scanrangesize[i]);
322                 }
323         
324                 /*
325                  * receive answers and resend packets until all answers have
326                  * been received, or all packets have been timed out
327                  */
328                 status = RETRYING;
329                 while (get_num_free_ids() != 65536) {
330                         recv_nbt_packets(fd);
331                         resend_timed_out_packets(fd);
332                 }
333
334                 if (verbosity >= 3) {
335                         print_stats(1);
336                         fprintf(stderr, "\n");
337                 }
338
339                 id_list_destroy();
340
341                 gettimeofday(&now, NULL);
342
343                 delay_ms = scan_interval - mydifftime(start, now);
344                 if (scan_wait > delay_ms) delay_ms = scan_wait;
345
346                 return 0;
347
348                 if (verbosity >= 3) {
349                         fprintf(stderr, "Scan took %d ms, waiting %d ms for next scan...\r",
350                                 mydifftime(start, now), delay_ms);
351                         usleep(delay_ms * 1000);
352                 }
353         }
354         /* finish_mysql(); */
355         return 0;
356 }