2 * nbtscanner -- a tool for scanning large networks for SMB servers.
4 * nbtscanner.c: nbtscanner main body.
5 * Copyright (C) 2000 Steinar H. Gunderson
7 * Large amounts of code adapted from Samba (http://www.samba.org/)
8 * Copyright (C) Andrew Tridgell 1994-1998, and others.
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.
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.
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.
28 #include <sys/socket.h>
29 #include <netinet/in.h>
31 #include <sys/types.h>
33 #include <arpa/inet.h>
36 #include "nbtscanner.h"
37 #include "mysql_interface.h"
38 #include "configfile.h"
43 #include "byteorder.h"
47 /* one global variable -- the UDP socket */
50 void interpret_node_status(char *p, struct in_addr in)
52 int numnames = CVAL(p,0);
53 char hostname[32], fileservername[32], group[32];
57 * hopefully this will be enough to identify the host as unknown
58 * (note that this is longer than the 16 char maximum NetBIOS
61 strcpy(hostname, "-unknown-nbtscanner-");
62 strcpy(fileservername, "-unknown-nbtscanner-");
63 strcpy(group, "-unknown-nbtscanner-");
77 for (i = strlen(qname); --i >= 0; ) {
78 if (!isprint((int)qname[i])) qname[i] = '.';
82 * this is code duplication (see parse_nmb_name in nmb.c)
83 * but it obviously isn't done in our case :-)
85 for (i = strlen(qname); --i >= 0; ) {
86 if (qname[i] == ' ') {
93 /* <ACTIVE>, non-<GROUP> server hostname (type 0x00) */
94 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
95 strcpy(hostname, qname);
97 /* <ACTIVE>, non-<GROUP> server hostname (type 0x20) */
98 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x20) {
99 strcpy(fileservername, qname);
101 /* <ACTIVE>, <GROUP> group (type 0x00) */
102 if ((p[0] & 0x80) == 0x80 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
103 strcpy(group, qname);
105 /* Messenger up (type 0x03) */
114 add_record_mysql(inet_ntoa(in), hostname, fileservername, group, messenger);
116 printf("%s,%s,%s,%s,%c\n", inet_ntoa(in), hostname, fileservername, group, messenger);
120 void resend_timed_out_packets(int fd)
124 struct nmb_packet nmb;
126 gettimeofday(&now, NULL);
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]);
136 delete_record_mysql(inet_ntoa(ids[i].sin));
140 gettimeofday(&(ids[i].timestamp), NULL);
144 build_nbt_packet(&nmb, i);
145 send_packet(&nmb, ids[i].sin, 137, fd);
146 recv_nbt_packets(fd); /* delay and empty buffer */
153 void send_nbt_packet(int fd, struct in_addr to_ip)
155 struct nmb_packet nmb;
156 static int name_trn_id = 0;
157 struct id_entry *i = id_get_free_id();
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 "
167 fprintf(stderr, "of retries, or the scanning "
168 "speed. nbtscanner is now "
176 gettimeofday(&(i->timestamp), NULL);
179 build_nbt_packet(&nmb, name_trn_id);
181 /* FIXME: what to do with sending errors? */
182 send_packet(&nmb, to_ip, 137, fd);
190 * This function not only receives and handles any NBT replies, but
191 * also guarantees a delay of minimum delay_time.
193 void recv_nbt_packets(int fd)
195 struct nmb_packet *nmb;
197 struct timeval start;
200 gettimeofday(&start, NULL);
207 gettimeofday(&now, NULL);
208 dt = delay_time - mydifftime(start, now);
212 nmb = receive_packet(fd, dt, &in);
214 struct id_entry *i = &ids[nmb->header.name_trn_id];
216 if (!nmb->header.response) {
217 /* It's not for us */
218 free_nmb_packet(nmb);
221 if (i->in_use == 0) {
222 if (verbosity < 2) continue;
224 fprintf(stderr, "Warning: received NBT "
225 "response for unused "
230 if (i->in_use == 2) {
231 if (verbosity < 2) continue;
233 fprintf(stderr, "Warning: received "
234 "duplicate NBT response "
239 if (in.s_addr != i->sin.s_addr) {
240 char got_addr[32], want_addr[32];
242 if (verbosity < 2) continue;
244 strcpy(got_addr, inet_ntoa(in));
245 strcpy(want_addr, inet_ntoa(i->sin));
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);
254 if (nmb->header.opcode != 0 ||
255 nmb->header.nm_flags.bcast ||
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);
266 num_recv_retries = i->retries;
268 interpret_node_status(&nmb->answers->rdata[0], in);
269 free_nmb_packet(nmb);
276 void scan_range(struct in_addr ip, int rangesize)
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) {
284 send_nbt_packet(server_fd, ip);
285 recv_nbt_packets(server_fd);
287 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
288 send_nbt_packet(server_fd, ip);
289 recv_nbt_packets(server_fd);
291 /* scan a range of IPs, minus the top and bottom one */
294 ip.s_addr = htonl(ntohl(ip.s_addr) + 1); /* skip bottom */
296 for (i = 0; i < (1 << (32-rangesize)) - 2; i++) {
297 send_nbt_packet(server_fd, ip);
298 recv_nbt_packets(server_fd);
300 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
305 int main(int argc, char *argv[])
307 int fd = open_sockets();
314 init_mysql(mysql_host, mysql_username, mysql_password);
318 struct timeval start, now;
319 unsigned int delay_ms;
324 gettimeofday(&start, NULL);
326 for (i = 0; i < ranges; i++) {
327 scan_range(scanrange[i], scanrangesize[i]);
331 * receive answers and resend packets until all answers have
332 * been received, or all packets have been timed out
335 while (get_num_free_ids() != 65536) {
336 recv_nbt_packets(fd);
337 resend_timed_out_packets(fd);
340 if (verbosity >= 3) {
342 fprintf(stderr, "\n");
347 gettimeofday(&now, NULL);
349 delay_ms = scan_interval - mydifftime(start, now);
350 if (scan_wait > delay_ms) delay_ms = scan_wait;
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);
360 /* finish_mysql(); */