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>
37 #include "nbtscanner.h"
38 #include "mysql_interface.h"
39 #include "configfile.h"
44 #include "byteorder.h"
48 /* one global variable -- the UDP socket */
51 void interpret_node_status(char *p, struct in_addr in)
53 int numnames = CVAL(p,0);
54 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-");
76 for (i = strlen(qname); --i >= 0; ) {
77 if (!isprint((int)qname[i])) qname[i] = '.';
81 * this is code duplication (see parse_nmb_name in nmb.c)
82 * but it obviously isn't done in our case :-)
84 for (i = strlen(qname); --i >= 0; ) {
85 if (qname[i] == ' ') {
92 /* <ACTIVE>, non-<GROUP> server hostname (type 0x00) */
93 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
94 strcpy(hostname, qname);
96 /* <ACTIVE>, non-<GROUP> server hostname (type 0x20) */
97 if ((p[0] & 0x80) == 0x00 && ((p[0] & 0x1f) == 0x04) && type == 0x20) {
98 strcpy(fileservername, qname);
100 /* <ACTIVE>, <GROUP> group (type 0x00) */
101 if ((p[0] & 0x80) == 0x80 && ((p[0] & 0x1f) == 0x04) && type == 0x00) {
102 strcpy(group, qname);
109 add_record_mysql(inet_ntoa(in), hostname, fileservername, group);
111 printf("%s,%s,%s,%s\n", inet_ntoa(in), hostname, fileservername, group);
115 void resend_timed_out_packets(int fd)
119 struct nmb_packet nmb;
121 gettimeofday(&now, NULL);
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]);
131 delete_record_mysql(inet_ntoa(ids[i].sin));
135 gettimeofday(&(ids[i].timestamp), NULL);
139 build_nbt_packet(&nmb, i);
140 send_packet(&nmb, ids[i].sin, 137, fd);
141 recv_nbt_packets(fd); /* delay and empty buffer */
148 void send_nbt_packet(int fd, struct in_addr to_ip)
150 struct nmb_packet nmb;
151 static int name_trn_id = 0;
152 struct id_entry *i = id_get_free_id();
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 "
162 fprintf(stderr, "of retries, or the scanning "
163 "speed. nbtscanner is now "
171 gettimeofday(&(i->timestamp), NULL);
174 build_nbt_packet(&nmb, name_trn_id);
176 /* FIXME: what to do with sending errors? */
177 send_packet(&nmb, to_ip, 137, fd);
185 * This function not only receives and handles any NBT replies, but
186 * also guarantees a delay of minimum delay_time.
188 void recv_nbt_packets(int fd)
190 struct nmb_packet *nmb;
192 struct timeval start;
195 gettimeofday(&start, NULL);
202 gettimeofday(&now, NULL);
203 dt = delay_time - mydifftime(start, now);
207 nmb = receive_packet(fd, dt, &in);
209 struct id_entry *i = &ids[nmb->header.name_trn_id];
211 if (!nmb->header.response) {
212 /* It's not for us */
213 free_nmb_packet(nmb);
216 if (i->in_use == 0) {
217 if (verbosity < 2) continue;
219 fprintf(stderr, "Warning: received NBT "
220 "response for unused "
225 if (i->in_use == 2) {
226 if (verbosity < 2) continue;
228 fprintf(stderr, "Warning: received "
229 "duplicate NBT response "
234 if (in.s_addr != i->sin.s_addr) {
235 char got_addr[32], want_addr[32];
237 if (verbosity < 2) continue;
239 strcpy(got_addr, inet_ntoa(in));
240 strcpy(want_addr, inet_ntoa(i->sin));
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);
249 if (nmb->header.opcode != 0 ||
250 nmb->header.nm_flags.bcast ||
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);
261 num_recv_retries = i->retries;
263 interpret_node_status(&nmb->answers->rdata[0], in);
264 free_nmb_packet(nmb);
271 void scan_range(struct in_addr ip, int rangesize)
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) {
279 send_nbt_packet(server_fd, ip);
280 recv_nbt_packets(server_fd);
282 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
283 send_nbt_packet(server_fd, ip);
284 recv_nbt_packets(server_fd);
286 /* scan a range of IPs, minus the top and bottom one */
289 ip.s_addr = htonl(ntohl(ip.s_addr) + 1); /* skip bottom */
291 for (i = 0; i < (1 << (32-rangesize)) - 2; i++) {
292 send_nbt_packet(server_fd, ip);
293 recv_nbt_packets(server_fd);
295 ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
300 int main(int argc, char *argv[])
303 int fd = open_sockets();
312 /* really ugly to have here */
314 init_mysql(mysql_host, mysql_username, mysql_password);
317 for (i = 0; i < ranges; i++) {
318 scan_range(scanrange[i], scanrangesize[i]);
322 * receive answers and resend packets until all answers have
323 * been received, or all packets have been timed out
326 while (get_num_free_ids() != 65536) {
327 recv_nbt_packets(fd);
328 resend_timed_out_packets(fd);
335 if (verbosity >= 3) {
337 fprintf(stderr, "\n");