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