mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
1215 lines
38 KiB
C++
1215 lines
38 KiB
C++
|
|
/***************************************************************************
|
|
* nmap_dns.cc -- Handles parallel reverse DNS resolution for target IPs *
|
|
* *
|
|
***********************IMPORTANT NMAP LICENSE TERMS************************
|
|
* *
|
|
* The Nmap Security Scanner is (C) 1996-2004 Insecure.Com LLC. Nmap *
|
|
* is also a registered trademark of Insecure.Com LLC. This program is *
|
|
* free software; you may redistribute and/or modify it under the *
|
|
* terms of the GNU General Public License as published by the Free *
|
|
* Software Foundation; Version 2. This guarantees your right to use, *
|
|
* modify, and redistribute this software under certain conditions. If *
|
|
* you wish to embed Nmap technology into proprietary software, we may be *
|
|
* willing to sell alternative licenses (contact sales@insecure.com). *
|
|
* Many security scanner vendors already license Nmap technology such as *
|
|
* our remote OS fingerprinting database and code, service/version *
|
|
* detection system, and port scanning code. *
|
|
* *
|
|
* Note that the GPL places important restrictions on "derived works", yet *
|
|
* it does not provide a detailed definition of that term. To avoid *
|
|
* misunderstandings, we consider an application to constitute a *
|
|
* "derivative work" for the purpose of this license if it does any of the *
|
|
* following: *
|
|
* o Integrates source code from Nmap *
|
|
* o Reads or includes Nmap copyrighted data files, such as *
|
|
* nmap-os-fingerprints or nmap-service-probes. *
|
|
* o Executes Nmap and parses the results (as opposed to typical shell or *
|
|
* execution-menu apps, which simply display raw Nmap output and so are *
|
|
* not derivative works.) *
|
|
* o Integrates/includes/aggregates Nmap into a proprietary executable *
|
|
* installer, such as those produced by InstallShield. *
|
|
* o Links to a library or executes a program that does any of the above *
|
|
* *
|
|
* The term "Nmap" should be taken to also include any portions or derived *
|
|
* works of Nmap. This list is not exclusive, but is just meant to *
|
|
* clarify our interpretation of derived works with some common examples. *
|
|
* These restrictions only apply when you actually redistribute Nmap. For *
|
|
* example, nothing stops you from writing and selling a proprietary *
|
|
* front-end to Nmap. Just distribute it by itself, and point people to *
|
|
* http://www.insecure.org/nmap/ to download Nmap. *
|
|
* *
|
|
* We don't consider these to be added restrictions on top of the GPL, but *
|
|
* just a clarification of how we interpret "derived works" as it applies *
|
|
* to our GPL-licensed Nmap product. This is similar to the way Linus *
|
|
* Torvalds has announced his interpretation of how "derived works" *
|
|
* applies to Linux kernel modules. Our interpretation refers only to *
|
|
* Nmap - we don't speak for any other GPL products. *
|
|
* *
|
|
* If you have any questions about the GPL licensing restrictions on using *
|
|
* Nmap in non-GPL works, we would be happy to help. As mentioned above, *
|
|
* we also offer alternative license to integrate Nmap into proprietary *
|
|
* applications and appliances. These contracts have been sold to many *
|
|
* security vendors, and generally include a perpetual license as well as *
|
|
* providing for priority support and updates as well as helping to fund *
|
|
* the continued development of Nmap technology. Please email *
|
|
* sales@insecure.com for further information. *
|
|
* *
|
|
* As a special exception to the GPL terms, Insecure.Com LLC grants *
|
|
* permission to link the code of this program with any version of the *
|
|
* OpenSSL library which is distributed under a license identical to that *
|
|
* listed in the included Copying.OpenSSL file, and distribute linked *
|
|
* combinations including the two. You must obey the GNU GPL in all *
|
|
* respects for all of the code used other than OpenSSL. If you modify *
|
|
* this file, you may extend this exception to your version of the file, *
|
|
* but you are not obligated to do so. *
|
|
* *
|
|
* If you received these files with a written license agreement or *
|
|
* contract stating terms other than the terms above, then that *
|
|
* alternative license agreement takes precedence over these comments. *
|
|
* *
|
|
* Source is provided to this software because we believe users have a *
|
|
* right to know exactly what a program is going to do before they run it. *
|
|
* This also allows you to audit the software for security holes (none *
|
|
* have been found so far). *
|
|
* *
|
|
* Source code also allows you to port Nmap to new platforms, fix bugs, *
|
|
* and add new features. You are highly encouraged to send your changes *
|
|
* to fyodor@insecure.org for possible incorporation into the main *
|
|
* distribution. By sending these changes to Fyodor or one the *
|
|
* Insecure.Org development mailing lists, it is assumed that you are *
|
|
* offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right *
|
|
* to reuse, modify, and relicense the code. Nmap will always be *
|
|
* available Open Source, but this is important because the inability to *
|
|
* relicense code has caused devastating problems for other Free Software *
|
|
* projects (such as KDE and NASM). We also occasionally relicense the *
|
|
* code to third parties as discussed above. If you wish to specify *
|
|
* special license conditions of your contributions, just say so when you *
|
|
* send them. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details at *
|
|
* http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included *
|
|
* with Nmap. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
// mass_rdns - Parallel Asynchronous Reverse DNS Resolution
|
|
//
|
|
// One of Nmap's features is to perform reverse DNS queries
|
|
// on large number of IP addresses. Nmap supports 2 different
|
|
// methods of accomplishing this:
|
|
//
|
|
// System Resolver (specified using --system_dns):
|
|
// Performs sequential getnameinfo() calls on all the IPs.
|
|
// As reliable as your system resolver, almost guaranteed
|
|
// to be portable, but intolerably slow for scans of hundreds
|
|
// or more because the result from each query needs to be
|
|
// received before the next one can be sent.
|
|
//
|
|
// Mass/Async DNS (default):
|
|
// Attempts to resolve host names in parallel using a set
|
|
// of DNS servers. DNS servers are found here:
|
|
//
|
|
// --dns_servers <serv1[,serv2],...> (all platforms - overrides everything else)
|
|
//
|
|
// /etc/resolv.conf (only on unix)
|
|
//
|
|
// These registry keys: (only on windows)
|
|
//
|
|
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\NameServer
|
|
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DhcpNameServer
|
|
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\*\NameServer
|
|
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\*\DhcpNameServer
|
|
//
|
|
//
|
|
// Also, most systems maintain a file "/etc/hosts" that contains
|
|
// IP to hostname mappings. We also try to consult these files. Here
|
|
// is where we look for the files:
|
|
//
|
|
// Unix: /etc/hosts
|
|
//
|
|
// Windows:
|
|
// for 95/98/Me: WINDOWS_DIR\hosts
|
|
// for NT/2000/XP Pro: WINDOWS_DIR\system32\drivers\etc\hosts
|
|
// for XP Home: WINDOWS_DIR\system32\drivers\etc\hosts
|
|
// --see http://accs-net.com/hosts/how_to_use_hosts.html
|
|
//
|
|
//
|
|
// Created by Doug Hoyte
|
|
// doug at hcsw.org
|
|
// http://www.hcsw.org
|
|
|
|
|
|
// TODO:
|
|
//
|
|
// * Tune performance parameters
|
|
//
|
|
// * Figure out best way to estimate completion time
|
|
// and display it in a ScanProgressMeter
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <list>
|
|
#include <vector>
|
|
|
|
#include "nmap.h"
|
|
#include "NmapOps.h"
|
|
#include "nmap_dns.h"
|
|
#include "nsock.h"
|
|
#include "utils.h"
|
|
#include "tty.h"
|
|
|
|
extern NmapOps o;
|
|
|
|
|
|
|
|
//------------------- Performance Parameters ---------------------
|
|
|
|
// Algorithm:
|
|
//
|
|
// A batch of num_targets hosts is passed to nmap_mass_rdns():
|
|
// void nmap_mass_rdns(Target **targets, int num_targets)
|
|
//
|
|
// mass_dns sends out CAPACITY_MIN of these hosts to the DNS
|
|
// servers detected, alternating in sequence.
|
|
|
|
// When a request is fulfilled (either a resolved domain, NXDomain,
|
|
// or confirmed ServFail) CAPACITY_UP_STEP is added to the current
|
|
// capacity of the server the request was found by.
|
|
|
|
// When a request times out and retries on the same server,
|
|
// the server's capacity is scaled by CAPACITY_MINOR_DOWN_STEP.
|
|
|
|
// When a request times out and moves to the next server in
|
|
// sequence, the server's capacity is scaled by CAPACITY_MAJOR_DOWN_STEP.
|
|
|
|
// mass_dns tries to maintain the current number of "outstanding
|
|
// queries" on each server to that of its current capacity. The
|
|
// packet is dropped if it cycles through all specified DNS
|
|
// servers.
|
|
|
|
|
|
// Since multiple DNS servers can be specified, different sequences
|
|
// of timers are maintained. These are the various retransmission
|
|
// intervals for each server before we move on to the next DNS server:
|
|
|
|
// In milliseconds
|
|
// Each row MUST be terminated with -1
|
|
int read_timeouts[][4] = {
|
|
{ 4000, 4000, 5000, -1 }, // 1 server
|
|
{ 2500, 4000, -1, -1 }, // 2 servers
|
|
{ 2500, 3000, -1, -1 }, // 3+ servers
|
|
};
|
|
|
|
#define CAPACITY_MIN 10
|
|
#define CAPACITY_MAX 200
|
|
#define CAPACITY_UP_STEP 2
|
|
#define CAPACITY_MINOR_DOWN_SCALE 0.95
|
|
#define CAPACITY_MAJOR_DOWN_SCALE 0.7
|
|
|
|
// Each request will try to resolve on at most this many servers:
|
|
#define SERVERS_TO_TRY 3
|
|
|
|
|
|
//------------------- Other Parameters ---------------------
|
|
|
|
// How often to display a short debugging summary if debugging is
|
|
// specified. Lower numbers means it's displayed more often.
|
|
#define SUMMARY_DELAY 50
|
|
|
|
// Minimum debugging level to display packet trace
|
|
#define TRACE_DEBUG_LEVEL 4
|
|
|
|
// The amount of time we wait for nsock_write() to complete before
|
|
// retransmission. This should almost never happen. (in milliseconds)
|
|
#define WRITE_TIMEOUT 100
|
|
|
|
// Size of hash table used to hold the hosts from /etc/hosts
|
|
#define HASH_TABLE_SIZE 256
|
|
|
|
|
|
|
|
//------------------- Internal Structures ---------------------
|
|
|
|
typedef struct dns_server_s dns_server;
|
|
typedef struct request_s request;
|
|
typedef struct host_elem_s host_elem;
|
|
|
|
struct dns_server_s {
|
|
char *hostname;
|
|
sockaddr_in addr;
|
|
nsock_iod nsd;
|
|
int connected;
|
|
int reqs_on_wire;
|
|
int capacity;
|
|
int write_busy;
|
|
std::list<request *> to_process;
|
|
std::list<request *> in_process;
|
|
};
|
|
|
|
struct request_s {
|
|
Target *targ;
|
|
struct timeval timeout;
|
|
int tries;
|
|
int servers_tried;
|
|
dns_server *first_server;
|
|
dns_server *curr_server;
|
|
u16 id;
|
|
};
|
|
|
|
struct host_elem_s {
|
|
char *name;
|
|
u32 addr;
|
|
};
|
|
|
|
|
|
//------------------- Globals ---------------------
|
|
|
|
static std::list<dns_server *> servs;
|
|
static std::list<request *> new_reqs;
|
|
static std::list<request *> cname_reqs;
|
|
static int total_reqs;
|
|
static nsock_pool dnspool=NULL;
|
|
|
|
static int etchosts_filled=0;
|
|
static std::list<host_elem *> etchosts[HASH_TABLE_SIZE];
|
|
|
|
static int stat_actual, stat_ok, stat_nx, stat_sf, stat_trans, stat_dropped, stat_cname;
|
|
static struct timeval starttv;
|
|
static int read_timeout_index;
|
|
static u16 id_counter;
|
|
|
|
static ScanProgressMeter *SPM;
|
|
|
|
|
|
//------------------- Prototypes ---------------------
|
|
|
|
void close_dns_servers();
|
|
void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v);
|
|
void do_possible_writes();
|
|
|
|
|
|
|
|
|
|
//------------------- Misc code ---------------------
|
|
|
|
void output_summary() {
|
|
int tp = stat_ok + stat_nx + stat_sf + stat_dropped;
|
|
struct timeval now;
|
|
|
|
memcpy(&now, nsock_gettimeofday(), sizeof(struct timeval));
|
|
|
|
if (o.debugging && (tp%SUMMARY_DELAY == 0))
|
|
log_write(LOG_STDOUT, "mass_rdns: %.2fs %d/%d [#: %lu, OK: %d, NX: %d, SF: %d, DR: %d, TR: %d]\n",
|
|
TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0,
|
|
tp, stat_actual,
|
|
servs.size(), stat_ok, stat_nx, stat_sf, stat_dropped, stat_trans);
|
|
|
|
}
|
|
|
|
|
|
void check_capacities(dns_server *tpserv) {
|
|
if (tpserv->capacity < CAPACITY_MIN) tpserv->capacity = CAPACITY_MIN;
|
|
if (tpserv->capacity > CAPACITY_MAX) tpserv->capacity = CAPACITY_MAX;
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "CAPACITY <%s> = %d\n", tpserv->hostname, tpserv->capacity);
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------- Read handling code ---------------------
|
|
|
|
// After processing a DNS response, we search through the IPs we're
|
|
// looking for and update their results as necessary.
|
|
// Returns non-zero if this matches a query we're looking for
|
|
int process_result(u32 ia, char *result, int needs_system_dns) {
|
|
std::list<dns_server *>::iterator servI;
|
|
std::list<request *>::iterator reqI;
|
|
dns_server *tpserv;
|
|
request *tpreq;
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
tpserv = *servI;
|
|
|
|
for(reqI = tpserv->in_process.begin(); reqI != tpserv->in_process.end(); reqI++) {
|
|
tpreq = *reqI;
|
|
|
|
if ((u32) (tpreq->targ->v4host().s_addr) == ia) {
|
|
tpserv->capacity += CAPACITY_UP_STEP;
|
|
check_capacities(tpserv);
|
|
|
|
if (result) tpreq->targ->setHostName(result);
|
|
tpserv->in_process.remove(tpreq);
|
|
tpserv->reqs_on_wire--;
|
|
total_reqs--;
|
|
|
|
if (needs_system_dns)
|
|
cname_reqs.push_back(tpreq);
|
|
else
|
|
delete tpreq;
|
|
|
|
do_possible_writes();
|
|
|
|
// Close DNS servers if we're all done so that we kill
|
|
// all events and return from nsock_loop immediatley
|
|
if (total_reqs == 0)
|
|
close_dns_servers();
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Gets an IP address from a X.X.X.X.in-addr.arpa DNS
|
|
// encoded string inside a packet.
|
|
// maxlen is the very maximum length (in total bytes)
|
|
// that should be processed
|
|
u32 parse_inaddr_arpa(unsigned char *buf, int maxlen) {
|
|
u32 ip=0;
|
|
int i, j;
|
|
|
|
for (i=3; i>=0; i--) {
|
|
if (maxlen <= 0) return 0;
|
|
|
|
if (buf[0] < 1 || buf[0] > 3) return 0;
|
|
|
|
maxlen -= buf[0] + 1;
|
|
if (maxlen <= 0) return 0;
|
|
|
|
for (j=1; j<=buf[0]; j++) if (!isdigit(buf[j])) return 0;
|
|
|
|
ip |= atoi((char *) buf+1) << (8*i);
|
|
buf += buf[0] + 1;
|
|
}
|
|
|
|
if (maxlen < 14) return 0; // length of the following string
|
|
if (strcasecmp((char *) buf, "\x07in-addr\004arpa\0")) return 0;
|
|
|
|
return ip;
|
|
}
|
|
|
|
|
|
// Turns a DNS packet encoded name (see the RFC) and turns it into
|
|
// a normal decimal separated hostname.
|
|
// ASSUMES NAME LENGTH/VALIDITY HAS ALREADY BEEN VERIFIED
|
|
int encoded_name_to_normal(unsigned char *buf, char *output, int outputsize){
|
|
while (buf[0]) {
|
|
if (buf[0] >= outputsize-1) return -1;
|
|
memcpy(output, buf+1, buf[0]);
|
|
outputsize -= buf[0];
|
|
output += buf[0];
|
|
buf += buf[0]+1;
|
|
|
|
if (buf[0]) {
|
|
*output++ = '.';
|
|
outputsize--;
|
|
} else {
|
|
*output = '\0';
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Takes a pointer to the start of a DNS name inside a packet. It makes
|
|
// sure that there is enough space in the name, deals with compression, etc.
|
|
int advance_past_dns_name(unsigned char *buf, int buflen, int curbuf,
|
|
int *nameloc) {
|
|
int compression=0;
|
|
|
|
if (curbuf <= 0 || curbuf >= buflen) return -1;
|
|
|
|
if ((buf[curbuf] & 0xc0)) {
|
|
// Need 2 bytes for compression info
|
|
if (curbuf + 1 >= buflen) return -1;
|
|
|
|
// Compression is OK
|
|
compression = curbuf+2;
|
|
curbuf = ((buf[curbuf+1] & 0xFF) + ((buf[curbuf] & 0xFF) << 8)) & 0x3FFF;
|
|
if (curbuf < 0 || curbuf >= buflen) return -1;
|
|
}
|
|
|
|
if (nameloc != NULL) *nameloc = curbuf;
|
|
|
|
while(buf[curbuf]) {
|
|
if (curbuf + buf[curbuf] >= buflen || buf[curbuf] <= 0) return -1;
|
|
curbuf += buf[curbuf] + 1;
|
|
}
|
|
|
|
if (compression) return compression;
|
|
else return curbuf+1;
|
|
}
|
|
|
|
|
|
// Nsock read handler. One nsock read for each DNS server exists at each
|
|
// time. This function uses various helper functions as defined above.
|
|
void read_evt_handler(nsock_pool nsp, nsock_event evt, void *nothing) {
|
|
unsigned char *buf;
|
|
int buflen, curbuf=0;
|
|
int i, nameloc, rdlen, atype, aclass;
|
|
int errcode=0;
|
|
int queries, answers;
|
|
|
|
if (total_reqs >= 1)
|
|
nsock_read(nsp, nse_iod(evt), read_evt_handler, -1, NULL);
|
|
|
|
if (nse_type(evt) != NSE_TYPE_READ || nse_status(evt) != NSE_STATUS_SUCCESS) {
|
|
if (o.debugging)
|
|
log_write(LOG_STDOUT, "mass_dns: warning: got a %s:%s in read_evt_handler()\n",
|
|
nse_type2str(nse_type(evt)),
|
|
nse_status2str(nse_status(evt)));
|
|
return;
|
|
}
|
|
|
|
buf = (unsigned char *) nse_readbuf(evt, &buflen);
|
|
|
|
// Size of header is 12, and we must have additional data as well
|
|
if (buflen <= 12) return;
|
|
|
|
// Check that this is a response, standard query, and that no truncation was performed
|
|
// 0xFA == 11111010 (we're not concerned with AA or RD bits)
|
|
if ((buf[2] & 0xFA) != 0x80) return;
|
|
|
|
// Check that Recursion is available, the zero field is all zeros
|
|
// and there is no error condition:
|
|
if ((buf[3] & 0xFF) != 0x80) {
|
|
if ((buf[3] & 0xF) == 2) errcode = 2;
|
|
else if ((buf[3] & 0xF) == 3) errcode = 3;
|
|
else return;
|
|
}
|
|
|
|
queries = (buf[5] & 0xFF) + ((buf[4] & 0xFF) << 8);
|
|
answers = (buf[7] & 0xFF) + ((buf[6] & 0xFF) << 8);
|
|
|
|
// With a normal resolution, we should have 1+ queries and 1+ answers.
|
|
// If the domain doesn't resolve (NXDOMAIN or SERVFAIL) we should have
|
|
// 1+ queries and 0 answers:
|
|
if (errcode) {
|
|
if (queries <= 0 || answers > 0) return;
|
|
} else {
|
|
if (queries <= 0 || answers <= 0) return;
|
|
}
|
|
|
|
curbuf = 12;
|
|
|
|
// Need to safely skip past QUERY section
|
|
|
|
for (i=0; i<queries; i++) {
|
|
curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc);
|
|
if (curbuf == -1) return;
|
|
|
|
// Make sure we have the QTYPE and QCLASS fields
|
|
if (curbuf + 4 >= buflen) return;
|
|
curbuf += 4;
|
|
|
|
if (errcode) {
|
|
struct in_addr ia;
|
|
int found;
|
|
|
|
ia.s_addr = parse_inaddr_arpa(buf+nameloc, buflen-nameloc);
|
|
if (ia.s_addr == 0) return;
|
|
|
|
found = process_result(ia.s_addr, NULL, 0);
|
|
|
|
if (errcode == 2 && found) {
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: SERVFAIL <%s>\n", inet_ntoa(ia));
|
|
output_summary();
|
|
stat_sf++;
|
|
} else if (errcode == 3 && found) {
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: NXDOMAIN <%s>\n", inet_ntoa(ia));
|
|
output_summary();
|
|
stat_nx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// No answers if NXDOMAIN/SERVFAIL
|
|
if (errcode) return;
|
|
|
|
// We're now at the ANSWER section
|
|
|
|
for (i=0; i<answers; i++) {
|
|
curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc);
|
|
if (curbuf == -1) return;
|
|
|
|
// Make sure we have the TYPE (2), CLASS (2), TTL (4), and
|
|
// RDLENGTH (2) fields
|
|
if (curbuf + 10 >= buflen) return;
|
|
|
|
atype = (buf[curbuf+1] & 0xFF) + ((buf[curbuf+0] & 0xFF) << 8);
|
|
aclass = (buf[curbuf+3] & 0xFF) + ((buf[curbuf+2] & 0xFF) << 8);
|
|
rdlen = (buf[curbuf+9] & 0xFF) + ((buf[curbuf+8] & 0xFF) << 8);
|
|
curbuf += 10;
|
|
|
|
if (atype == 12 && aclass == 1) {
|
|
// TYPE 12 is PTR
|
|
struct in_addr ia;
|
|
char outbuf[512];
|
|
|
|
ia.s_addr = parse_inaddr_arpa(buf+nameloc, buflen-nameloc);
|
|
if (ia.s_addr == 0) return;
|
|
|
|
curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc);
|
|
if (curbuf == -1 || curbuf >= buflen) return;
|
|
|
|
if (encoded_name_to_normal(buf+nameloc, outbuf, sizeof(outbuf)) == -1) return;
|
|
|
|
if (process_result(ia.s_addr, outbuf, 0)) {
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: OK MATCHED <%s> to <%s>\n", inet_ntoa(ia), outbuf);
|
|
output_summary();
|
|
stat_ok++;
|
|
}
|
|
} else if (atype == 5 && aclass == 1) {
|
|
// TYPE 5 is CNAME
|
|
struct in_addr ia;
|
|
|
|
ia.s_addr = parse_inaddr_arpa(buf+nameloc, buflen-nameloc);
|
|
if (ia.s_addr == 0) return;
|
|
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: CNAME found for <%s>\n", inet_ntoa(ia));
|
|
process_result(ia.s_addr, NULL, 1);
|
|
} else {
|
|
if (rdlen < 0 || rdlen + curbuf >= buflen) return;
|
|
curbuf += rdlen;
|
|
}
|
|
|
|
if (curbuf >= buflen) return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------- Write handling code ---------------------
|
|
|
|
// Inserts an integer (endian non-specifically) into a DNS packet.
|
|
// Returns number of bytes written
|
|
int add_integer_to_dns_packet(char *packet, int c) {
|
|
char tpnum[4];
|
|
int tplen;
|
|
|
|
sprintf(tpnum, "%d", c);
|
|
tplen = strlen(tpnum);
|
|
packet[0] = (char) tplen;
|
|
memcpy(packet+1, tpnum, tplen);
|
|
|
|
return tplen+1;
|
|
}
|
|
|
|
|
|
// Takes a DNS request structure and actually puts it on the wire
|
|
// (calls nsock_write()). Does various other tasks like recording
|
|
// the time for the timeout.
|
|
void put_dns_packet_on_wire(request *req) {
|
|
char packet[512];
|
|
int plen=0;
|
|
u32 ip;
|
|
struct timeval now, timeout;
|
|
|
|
ip = (u32) req->targ->v4host().s_addr;
|
|
|
|
packet[0] = (req->id >> 8) & 0xFF;
|
|
packet[1] = req->id & 0xFF;
|
|
plen += 2;
|
|
|
|
memcpy(packet+plen, "\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00", 10);
|
|
plen += 10;
|
|
|
|
plen += add_integer_to_dns_packet(packet+plen, (ip>>24) & 0xFF);
|
|
plen += add_integer_to_dns_packet(packet+plen, (ip>>16) & 0xFF);
|
|
plen += add_integer_to_dns_packet(packet+plen, (ip>>8) & 0xFF);
|
|
plen += add_integer_to_dns_packet(packet+plen, ip & 0xFF);
|
|
|
|
memcpy(packet+plen, "\x07in-addr\004arpa\x00\x00\x0c\x00\x01", 18);
|
|
plen += 18;
|
|
|
|
req->curr_server->write_busy = 1;
|
|
req->curr_server->reqs_on_wire++;
|
|
|
|
memcpy(&now, nsock_gettimeofday(), sizeof(struct timeval));
|
|
TIMEVAL_MSEC_ADD(timeout, now, read_timeouts[read_timeout_index][req->tries]);
|
|
memcpy(&req->timeout, &timeout, sizeof(struct timeval));
|
|
|
|
req->tries++;
|
|
|
|
nsock_write(dnspool, req->curr_server->nsd, write_evt_handler, WRITE_TIMEOUT, req, packet, plen);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Processes DNS packets that have timed out
|
|
// Returns time until next read timeout
|
|
int deal_with_timedout_reads() {
|
|
std::list<dns_server *>::iterator servI;
|
|
std::list<dns_server *>::iterator servItemp;
|
|
std::list<request *>::iterator reqI;
|
|
std::list<request *>::iterator nextI;
|
|
dns_server *tpserv;
|
|
request *tpreq;
|
|
struct timeval now;
|
|
int tp, min_timeout = INT_MAX;
|
|
|
|
memcpy(&now, nsock_gettimeofday(), sizeof(struct timeval));
|
|
|
|
if (keyWasPressed())
|
|
SPM->printStats((double) (stat_ok + stat_nx + stat_sf + stat_dropped) / stat_actual, &now);
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
tpserv = *servI;
|
|
|
|
nextI = tpserv->in_process.begin();
|
|
if (nextI == tpserv->in_process.end()) continue;
|
|
|
|
do {
|
|
reqI = nextI++;
|
|
tpreq = *reqI;
|
|
|
|
tp = TIMEVAL_MSEC_SUBTRACT(tpreq->timeout, now);
|
|
if (tp > 0 && tp < min_timeout) min_timeout = tp;
|
|
|
|
if (tp <= 0) {
|
|
tpserv->capacity = (int) (tpserv->capacity * CAPACITY_MINOR_DOWN_SCALE);;
|
|
check_capacities(tpserv);
|
|
tpserv->in_process.erase(reqI);
|
|
tpserv->reqs_on_wire--;
|
|
|
|
// If we've tried this server enough times, move to the next one
|
|
if (read_timeouts[read_timeout_index][tpreq->tries] == -1) {
|
|
tpserv->capacity = (int) (tpserv->capacity * CAPACITY_MAJOR_DOWN_SCALE);
|
|
check_capacities(tpserv);
|
|
|
|
servItemp = servI;
|
|
servItemp++;
|
|
|
|
if (servItemp == servs.end()) servItemp = servs.begin();
|
|
|
|
tpreq->curr_server = *servItemp;
|
|
tpreq->tries = 0;
|
|
tpreq->servers_tried++;
|
|
|
|
if (tpreq->curr_server == tpreq->first_server || tpreq->servers_tried == SERVERS_TO_TRY) {
|
|
// Either give up on the IP
|
|
// or, for maximum reliability, put the server back into processing
|
|
// Note it's possible that this will never terminate.
|
|
// FIXME: Find a good compromise
|
|
|
|
// **** We've already tried all servers... give up
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: *DR*OPPING <%s>\n", tpreq->targ->targetipstr());
|
|
|
|
output_summary();
|
|
stat_dropped++;
|
|
total_reqs--;
|
|
delete tpreq;
|
|
|
|
// **** OR We start at the back of this server's queue
|
|
//(*servItemp)->to_process.push_back(tpreq);
|
|
} else {
|
|
(*servItemp)->to_process.push_back(tpreq);
|
|
}
|
|
} else {
|
|
tpserv->to_process.push_back(tpreq);
|
|
}
|
|
|
|
}
|
|
|
|
} while (nextI != tpserv->in_process.end());
|
|
|
|
}
|
|
|
|
if (min_timeout > 500) return 500;
|
|
else return min_timeout;
|
|
|
|
}
|
|
|
|
|
|
// Puts as many packets on the line as capacity will allow
|
|
void do_possible_writes() {
|
|
std::list<dns_server *>::iterator servI;
|
|
dns_server *tpserv;
|
|
request *tpreq;
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
tpserv = *servI;
|
|
|
|
if (tpserv->write_busy == 0 && tpserv->reqs_on_wire < tpserv->capacity) {
|
|
tpreq = NULL;
|
|
if (!tpserv->to_process.empty()) {
|
|
tpreq = tpserv->to_process.front();
|
|
tpserv->to_process.pop_front();
|
|
} else if (!new_reqs.empty()) {
|
|
tpreq = new_reqs.front();
|
|
tpreq->first_server = tpreq->curr_server = tpserv;
|
|
new_reqs.pop_front();
|
|
}
|
|
|
|
if (tpreq) {
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: TRANSMITTING for <%s> (server <%s>)\n", tpreq->targ->targetipstr() , tpserv->hostname);
|
|
stat_trans++;
|
|
put_dns_packet_on_wire(tpreq);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// nsock write handler
|
|
void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v) {
|
|
request *req = (request *) req_v;
|
|
|
|
req->curr_server->write_busy = 0;
|
|
req->curr_server->in_process.push_front(req);
|
|
|
|
do_possible_writes();
|
|
}
|
|
|
|
|
|
|
|
//------------------- DNS Server handling code ---------------------
|
|
|
|
// nsock connect handler - Empty because it doesn't really need to do anything...
|
|
void connect_evt_handler(nsock_pool nsp, nsock_event evt, void *servers) {
|
|
}
|
|
|
|
|
|
// Adds DNS servers to the dns_server list. They can be separated by
|
|
// commas or spaces - NOTE this doesn't actually do any connecting!
|
|
void add_dns_server(char *ipaddrs) {
|
|
std::list<dns_server *>::iterator servI;
|
|
dns_server *tpserv;
|
|
char *hostname;
|
|
struct sockaddr_in addr;
|
|
size_t addr_len = sizeof(addr);
|
|
|
|
for (hostname = strtok(ipaddrs, " ,"); hostname != NULL; hostname = strtok(NULL, " ,")) {
|
|
|
|
if (!resolve(hostname, (struct sockaddr_storage *) &addr, &addr_len, PF_INET)) continue;
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
tpserv = *servI;
|
|
|
|
// Already added!
|
|
if (memcmp(&addr, &tpserv->addr, sizeof(addr)) == 0) break;
|
|
}
|
|
|
|
// If it hasn't already been added, add it!
|
|
if (servI == servs.end()) {
|
|
tpserv = new dns_server;
|
|
|
|
tpserv->hostname = strdup(hostname);
|
|
memcpy(&tpserv->addr, &addr, sizeof(addr));
|
|
|
|
servs.push_front(tpserv);
|
|
|
|
if (o.debugging) log_write(LOG_STDOUT, "mass_rdns: Using DNS server %s\n", hostname);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Creates a new nsi for each DNS server
|
|
void connect_dns_servers() {
|
|
std::list<dns_server *>::iterator serverI;
|
|
dns_server *s;
|
|
|
|
for(serverI = servs.begin(); serverI != servs.end(); serverI++) {
|
|
s = *serverI;
|
|
|
|
s->nsd = nsi_new(dnspool, NULL);
|
|
s->reqs_on_wire = 0;
|
|
s->capacity = CAPACITY_MIN;
|
|
s->write_busy = 0;
|
|
|
|
nsock_connect_udp(dnspool, s->nsd, connect_evt_handler, NULL, (struct sockaddr *) &s->addr, sizeof(struct sockaddr), 53);
|
|
nsock_read(dnspool, s->nsd, read_evt_handler, -1, NULL);
|
|
s->connected = 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Closes all nsis created in connect_dns_servers()
|
|
void close_dns_servers() {
|
|
std::list<dns_server *>::iterator serverI;
|
|
|
|
for(serverI = servs.begin(); serverI != servs.end(); serverI++) {
|
|
if ((*serverI)->connected) {
|
|
nsi_delete((*serverI)->nsd, NSOCK_PENDING_SILENT);
|
|
(*serverI)->connected = 0;
|
|
(*serverI)->to_process.clear();
|
|
(*serverI)->in_process.clear();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Parses /etc/resolv.conf (unix) or the registry (win32) and adds
|
|
// all the nameservers found via the add_dns_server() function.
|
|
void parse_resolvdotconf() {
|
|
|
|
#ifndef WIN32
|
|
|
|
FILE *fp;
|
|
char buf[2048], *tp;
|
|
char ipaddr[16];
|
|
|
|
fp = fopen("/etc/resolv.conf", "r");
|
|
if (fp == NULL) {
|
|
fatal("Unable to open /etc/resolv.conf. Try using --system_dns or specify valid servers with --dns_servers");
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), fp)) {
|
|
tp = buf;
|
|
|
|
// Clip off comments #, \r, \n
|
|
while (*tp != '\r' && *tp != '\n' && *tp != '#' && *tp) tp++;
|
|
*tp = '\0';
|
|
|
|
tp = buf;
|
|
// Skip any leading whitespace
|
|
while (*tp == ' ' || *tp == '\t') tp++;
|
|
|
|
if (sscanf(tp, "nameserver %15s", ipaddr) == 1) add_dns_server(ipaddr);
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
#else
|
|
|
|
HKEY hKey;
|
|
HKEY hKey2;
|
|
char buf[2048], keyname[2048], *p;
|
|
long sz, i;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
|
|
0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
|
fatal("Error opening registry to read DNS servers. Try using --system_dns or specify valid servers with --dns_servers");
|
|
|
|
sz = sizeof(buf);
|
|
if (RegQueryValueEx(hKey, "NameServer", NULL, NULL, (LPBYTE) buf, (LPDWORD) &sz) == ERROR_SUCCESS)
|
|
add_dns_server(buf);
|
|
|
|
sz = sizeof(buf);
|
|
if (RegQueryValueEx(hKey, "DhcpNameServer", NULL, NULL, (LPBYTE) buf, (LPDWORD) &sz) == ERROR_SUCCESS)
|
|
add_dns_server(buf);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces",
|
|
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
|
|
for (i=0; sz = sizeof(buf) && RegEnumKeyEx(hKey, i, buf, (LPDWORD) &sz, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; i++) {
|
|
|
|
snprintf(keyname, sizeof(keyname), "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s", buf);
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
keyname,
|
|
0, KEY_READ, &hKey2) == ERROR_SUCCESS) {
|
|
|
|
sz = sizeof(buf);
|
|
if (RegQueryValueEx(hKey2, "DhcpNameServer", NULL, NULL, (LPBYTE) buf, (LPDWORD) &sz) == ERROR_SUCCESS)
|
|
add_dns_server(buf);
|
|
|
|
sz = sizeof(buf);
|
|
if (RegQueryValueEx(hKey2, "NameServer", NULL, NULL, (LPBYTE) buf, (LPDWORD) &sz) == ERROR_SUCCESS)
|
|
add_dns_server(buf);
|
|
|
|
RegCloseKey(hKey2);
|
|
}
|
|
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void parse_etchosts(char *fname) {
|
|
FILE *fp;
|
|
char buf[2048], hname[256], ipaddrstr[16], *tp;
|
|
struct in_addr ia;
|
|
host_elem *he;
|
|
|
|
fp = fopen(fname, "r");
|
|
if (fp == NULL) return; // silently is OK
|
|
|
|
while (fgets(buf, sizeof(buf), fp)) {
|
|
tp = buf;
|
|
|
|
// Clip off comments #, \r, \n
|
|
while (*tp != '\r' && *tp != '\n' && *tp != '#' && *tp) tp++;
|
|
*tp = '\0';
|
|
|
|
tp = buf;
|
|
// Skip any leading whitespace
|
|
while (*tp == ' ' || *tp == '\t') tp++;
|
|
|
|
if (sscanf(tp, "%15s %255s", ipaddrstr, hname) == 2) {
|
|
if (inet_aton(ipaddrstr, &ia)) {
|
|
he = new host_elem;
|
|
he->name = strdup(hname);
|
|
he->addr = (u32) ia.s_addr;
|
|
etchosts[he->addr % HASH_TABLE_SIZE].push_front(he);
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
char *lookup_etchosts(u32 ip) {
|
|
std::list<host_elem *>::iterator hostI;
|
|
host_elem *tpelem;
|
|
|
|
for(hostI = etchosts[ip % HASH_TABLE_SIZE].begin(); hostI != etchosts[ip % HASH_TABLE_SIZE].end(); hostI++) {
|
|
tpelem = *hostI;
|
|
if (tpelem->addr == ip) return tpelem->name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------- Main loops ---------------------
|
|
|
|
|
|
// Actual main loop
|
|
void nmap_mass_rdns_core(Target **targets, int num_targets) {
|
|
|
|
Target **hostI;
|
|
std::list<request *>::iterator reqI;
|
|
request *tpreq;
|
|
int timeout;
|
|
char *tpname;
|
|
int i;
|
|
|
|
if (o.mass_dns == false) {
|
|
Target *currenths;
|
|
struct sockaddr_storage ss;
|
|
size_t sslen;
|
|
char hostname[MAXHOSTNAMELEN + 1] = "";
|
|
|
|
for(hostI = targets; hostI < targets+num_targets; hostI++) {
|
|
currenths = *hostI;
|
|
|
|
if (((currenths->flags & HOST_UP) || o.resolve_all) && !o.noresolve) stat_actual++;
|
|
}
|
|
|
|
SPM = new ScanProgressMeter("System DNS resolution");
|
|
|
|
for(i=0, hostI = targets; hostI < targets+num_targets; hostI++, i++) {
|
|
currenths = *hostI;
|
|
|
|
if (keyWasPressed())
|
|
SPM->printStats((double) i / stat_actual, NULL);
|
|
|
|
if (((currenths->flags & HOST_UP) || o.resolve_all) && !o.noresolve) {
|
|
if (currenths->TargetSockAddr(&ss, &sslen) != 0)
|
|
fatal("Failed to get target socket address.");
|
|
if (getnameinfo((struct sockaddr *)&ss, sslen, hostname,
|
|
sizeof(hostname), NULL, 0, NI_NAMEREQD) == 0) {
|
|
stat_ok++;
|
|
currenths->setHostName(hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete SPM;
|
|
|
|
return;
|
|
}
|
|
|
|
// If necessary, set up the dns server list from resolv.conf
|
|
if (servs.size() == 0) {
|
|
if (o.dns_servers) add_dns_server(o.dns_servers);
|
|
else parse_resolvdotconf();
|
|
|
|
if (servs.size() == 0)
|
|
fatal("Unable to determine any DNS servers. Try using --system_dns or specify valid servers with --dns_servers");
|
|
}
|
|
|
|
|
|
// If necessary, set up the /etc/hosts hashtable
|
|
if (etchosts_filled == 0) {
|
|
#ifdef WIN32
|
|
char windows_dir[1024];
|
|
char tpbuf[2048];
|
|
int has_backslash;
|
|
|
|
if (!GetWindowsDirectory(windows_dir, sizeof(windows_dir)))
|
|
fatal("Failed to determine your windows directory");
|
|
|
|
// If it has a backslash it's C:\, otherwise something like C:\WINNT
|
|
has_backslash = (windows_dir[strlen(windows_dir)-1] == '\\');
|
|
|
|
// Windows 95/98/Me:
|
|
snprintf(tpbuf, sizeof(tpbuf), "%s%shosts", windows_dir, has_backslash ? "" : "\\");
|
|
parse_etchosts(tpbuf);
|
|
|
|
// Windows NT/2000/XP/2K3:
|
|
snprintf(tpbuf, sizeof(tpbuf), "%s%ssystem32\\drivers\\etc\\hosts", windows_dir, has_backslash ? "" : "\\");
|
|
parse_etchosts(tpbuf);
|
|
|
|
#else
|
|
parse_etchosts("/etc/hosts");
|
|
#endif
|
|
|
|
etchosts_filled = 1;
|
|
}
|
|
|
|
|
|
total_reqs = 0;
|
|
id_counter = get_random_u16();
|
|
|
|
// Set up the request structure
|
|
for(hostI = targets; hostI < targets+num_targets; hostI++) {
|
|
if (!((*hostI)->flags & HOST_UP) && !o.resolve_all) continue;
|
|
|
|
// See if it's in /etc/hosts
|
|
tpname = lookup_etchosts((u32) (*hostI)->v4hostip()->s_addr);
|
|
if (tpname) {
|
|
(*hostI)->setHostName(tpname);
|
|
continue;
|
|
}
|
|
|
|
tpreq = new request;
|
|
tpreq->targ = *hostI;
|
|
tpreq->tries = 0;
|
|
tpreq->servers_tried = 0;
|
|
tpreq->id = id_counter++;
|
|
|
|
new_reqs.push_back(tpreq);
|
|
|
|
stat_actual++;
|
|
total_reqs++;
|
|
}
|
|
|
|
if (total_reqs == 0) return;
|
|
|
|
// And finally, do it!
|
|
|
|
if ((dnspool = nsp_new(NULL)) == NULL)
|
|
fatal("Unable to create nsock pool in nmap_mass_rdns_core()");
|
|
|
|
if (o.packetTrace())
|
|
nsp_settrace(dnspool, 5, o.getStartTime());
|
|
|
|
connect_dns_servers();
|
|
|
|
cname_reqs.clear();
|
|
|
|
read_timeout_index = MIN(sizeof(read_timeouts)/sizeof(read_timeouts[0]), servs.size()) - 1;
|
|
|
|
SPM = new ScanProgressMeter("System DNS resolution");
|
|
|
|
while (total_reqs > 0) {
|
|
timeout = deal_with_timedout_reads();
|
|
|
|
do_possible_writes();
|
|
|
|
if (total_reqs <= 0) break;
|
|
|
|
nsock_loop(dnspool, timeout);
|
|
}
|
|
|
|
delete SPM;
|
|
|
|
close_dns_servers();
|
|
|
|
nsp_delete(dnspool);
|
|
|
|
if (cname_reqs.size() && o.debugging)
|
|
log_write(LOG_STDOUT, "Performing system_dns for %d domain names that use CNAMEs\n", cname_reqs.size());
|
|
|
|
SPM = new ScanProgressMeter("System CNAME DNS resolution");
|
|
|
|
for(i=0, reqI = cname_reqs.begin(); reqI != cname_reqs.end(); reqI++, i++) {
|
|
struct sockaddr_storage ss;
|
|
size_t sslen;
|
|
char hostname[MAXHOSTNAMELEN + 1] = "";
|
|
|
|
if (keyWasPressed())
|
|
SPM->printStats((double) i / cname_reqs.size(), NULL);
|
|
|
|
tpreq = *reqI;
|
|
|
|
if (tpreq->targ->TargetSockAddr(&ss, &sslen) != 0)
|
|
fatal("Failed to get target socket address.");
|
|
|
|
if (getnameinfo((struct sockaddr *)&ss, sslen, hostname,
|
|
sizeof(hostname), NULL, 0, NI_NAMEREQD) == 0) {
|
|
stat_ok++;
|
|
stat_cname++;
|
|
tpreq->targ->setHostName(hostname);
|
|
}
|
|
|
|
delete tpreq;
|
|
|
|
}
|
|
|
|
delete SPM;
|
|
|
|
cname_reqs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Publicly available function. Basically just a wrapper so we
|
|
// can record time information, restart statistics, etc.
|
|
void nmap_mass_rdns(Target **targets, int num_targets) {
|
|
|
|
struct timeval now;
|
|
|
|
gettimeofday(&starttv, NULL);
|
|
|
|
stat_actual = stat_ok = stat_nx = stat_sf = stat_trans = stat_dropped = stat_cname = 0;
|
|
|
|
nmap_mass_rdns_core(targets, num_targets);
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
if (o.verbose || o.debugging) {
|
|
if (o.mass_dns) {
|
|
// #: Number of DNS servers used
|
|
// OK: Number of fully reverse resolved queries
|
|
// NX: Number of confirmations of 'No such reverse domain eXists'
|
|
// SF: Number of IPs that got 'Server Failure's
|
|
// DR: Dropped IPs (no valid responses were received)
|
|
// TR: Total number of transmissions necessary. The number of domains is ideal, higher is worse
|
|
log_write(LOG_STDOUT, "DNS resolution of %d IPs took %.2fs. Mode: Async [#: %lu, OK: %d, NX: %d, SF: %d, DR: %d, TR: %d, CN: %d]\n",
|
|
stat_actual, TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0,
|
|
servs.size(), stat_ok, stat_nx, stat_sf, stat_dropped, stat_trans, stat_cname);
|
|
} else {
|
|
log_write(LOG_STDOUT, "DNS resolution of %d IPs took %.2fs. Mode: System [OK: %d, ??: %d]\n",
|
|
stat_actual, TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0,
|
|
stat_ok, stat_actual - stat_ok);
|
|
}
|
|
}
|
|
|
|
}
|