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