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];
56 * hopefully this will be enough to identify the host as unknown
57 * (note that this is longer than the 16 char maximum NetBIOS
60 strcpy(hostname, "-unknown-nbtscanner-");
61 strcpy(fileservername, "-unknown-nbtscanner-");
62 strcpy(group, "-unknown-nbtscanner-");
75 for (i = strlen(qname); --i >= 0; ) {
76 if (!isprint((int)qname[i])) qname[i] = '.';
80 * this is code duplication (see parse_nmb_name in nmb.c)
81 * but it obviously isn't done in our case :-)
83 for (i = strlen(qname); --i >= 0; ) {
84 if (qname[i] == ' ') {
91 /* <ACTIVE>, non-<GROUP> server hostname (type 0x00) */
92 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
93 strcpy(hostname, qname);
95 /* <ACTIVE>, non-<GROUP> server hostname (type 0x20) */
96 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x20) {
97 strcpy(fileservername, qname);
99 /* <ACTIVE>, <GROUP> group (type 0x00) */
100 if ((p[0] & 0x80) == 0x80 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
101 strcpy(group, qname);
108 add_record_mysql(inet_ntoa(in), hostname, fileservername, group);
110 printf("%s,%s,%s,%s\n", inet_ntoa(in), hostname, fileservername, group);
114 void resend_timed_out_packets(int fd)
118 struct nmb_packet nmb;
120 gettimeofday(&now, NULL);
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]);
130 delete_record_mysql(inet_ntoa(ids[i].sin));
134 gettimeofday(&(ids[i].timestamp), NULL);
138 build_nbt_packet(&nmb, i);
139 send_packet(&nmb, ids[i].sin, 137, fd);
140 recv_nbt_packets(fd); /* delay and empty buffer */
147 void send_nbt_packet(int fd, struct in_addr to_ip)
149 struct nmb_packet nmb;
150 static int name_trn_id = 0;
151 struct id_entry *i = id_get_free_id();
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 "
161 fprintf(stderr, "of retries, or the scanning "
162 "speed. nbtscanner is now "
170 gettimeofday(&(i->timestamp), NULL);
173 build_nbt_packet(&nmb, name_trn_id);
175 /* FIXME: what to do with sending errors? */
176 send_packet(&nmb, to_ip, 137, fd);
184 * This function not only receives and handles any NBT replies, but
185 * also guarantees a delay of minimum delay_time.
187 void recv_nbt_packets(int fd)
189 struct nmb_packet *nmb;
191 struct timeval start;
194 gettimeofday(&start, NULL);
201 gettimeofday(&now, NULL);
202 dt = delay_time - mydifftime(start, now);
206 nmb = receive_packet(fd, dt, &in);
208 struct id_entry *i = &ids[nmb->header.name_trn_id];
210 if (!nmb->header.response) {
211 /* It's not for us */
212 free_nmb_packet(nmb);
215 if (i->in_use == 0) {
216 if (verbosity < 2) continue;
218 fprintf(stderr, "Warning: received NBT "
219 "response for unused "
224 if (i->in_use == 2) {
225 if (verbosity < 2) continue;
227 fprintf(stderr, "Warning: received "
228 "duplicate NBT response "
233 if (in.s_addr != i->sin.s_addr) {
234 char got_addr[32], want_addr[32];
236 if (verbosity < 2) continue;
238 strcpy(got_addr, inet_ntoa(in));
239 strcpy(want_addr, inet_ntoa(i->sin));
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);
248 if (nmb->header.opcode != 0 ||
249 nmb->header.nm_flags.bcast ||
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);
260 num_recv_retries = i->retries;
262 interpret_node_status(&nmb->answers->rdata[0], in);
263 free_nmb_packet(nmb);
270 void scan_range(struct in_addr ip, int rangesize)
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) {
278 send_nbt_packet(server_fd, ip);
279 recv_nbt_packets(server_fd);
281 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
282 send_nbt_packet(server_fd, ip);
283 recv_nbt_packets(server_fd);
285 /* scan a range of IPs, minus the top and bottom one */
288 ip.s_addr = htonl(ntohl(ip.s_addr) + 1); /* skip bottom */
290 for (i = 0; i < (1 << (32-rangesize)) - 2; i++) {
291 send_nbt_packet(server_fd, ip);
292 recv_nbt_packets(server_fd);
294 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
299 int main(int argc, char *argv[])
301 int fd = open_sockets();
306 /* note that parse_configfile() will call scan_range()! */
310 /* for (i=1;i<argc;i++) {
313 ip.s_addr = inet_addr(argv[i]); //interpret_addr2(argv[i]);
314 send_nbt_packet(fd, ip);
315 recv_nbt_packets(fd);
319 * receive answers and resend packets until all answers have
320 * been received, or all packets have been timed out
323 while (get_num_free_ids() != 65536) {
324 recv_nbt_packets(fd);
325 resend_timed_out_packets(fd);
332 if (verbosity >= 3) {
334 fprintf(stderr, "\n");