diff -urNb old.nmap.rev/nmap.cc new.nmap.rev/nmap.cc --- old.nmap.rev/nmap.cc 2005-12-30 20:06:09.000000000 -0800 +++ new.nmap.rev/nmap.cc 2005-12-30 20:06:09.000000000 -0800 @@ -203,7 +203,7 @@ size_t j, argvlen; FILE *inputfd = NULL, *excludefd = NULL; char *host_spec = NULL, *exclude_spec = NULL; - short fastscan=0, randomize=1, resolve_all=0; + short fastscan=0, randomize=1; short quashargv = 0; int numhosts_scanned = 0; char **host_exp_group; @@ -303,6 +303,8 @@ {"version_intensity", required_argument, 0, 0}, {"version_light", no_argument, 0, 0}, {"version_all", no_argument, 0, 0}, + {"system_dns", no_argument, 0, 0}, + {"dns_servers", required_argument, 0, 0}, {0, 0, 0, 0} }; @@ -450,6 +452,10 @@ o.setXSLStyleSheet(optarg); } else if (strcmp(long_options[option_index].name, "no_stylesheet") == 0) { o.setXSLStyleSheet(NULL); + } else if (strcmp(long_options[option_index].name, "system_dns") == 0) { + o.mass_dns = false; + } else if (strcmp(long_options[option_index].name, "dns_servers") == 0) { + o.dns_servers = strdup(optarg); } else if (strcmp(long_options[option_index].name, "webxml") == 0) { o.setXSLStyleSheet("http://www.insecure.org/nmap/data/nmap.xsl"); } else if (strcmp(long_options[option_index].name, "oN") == 0) { @@ -684,7 +690,7 @@ fatal("Your port specification string is not parseable"); break; case 'q': quashargv++; break; - case 'R': resolve_all++; break; + case 'R': o.resolve_all++; break; case 'r': randomize = 0; break; @@ -1099,20 +1105,10 @@ if (currenths->flags & HOST_UP && !o.listscan) numhosts_up++; - /* Lookup the IP */ - if (((currenths->flags & HOST_UP) || 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) { - currenths->setHostName(hostname); - } - } - if (o.pingscan || o.listscan) { /* We're done with the hosts */ log_write(LOG_XML, ""); - write_host_status(currenths, resolve_all); + write_host_status(currenths, o.resolve_all); printmacinfo(currenths); // if (currenths->flags & HOST_UP) // log_write(LOG_NORMAL|LOG_SKID|LOG_STDOUT,"\n"); @@ -1239,7 +1235,7 @@ /* Now I can do the output and such for each host */ log_write(LOG_XML, ""); - write_host_status(currenths, resolve_all); + write_host_status(currenths, o.resolve_all); if (currenths->timedOut(NULL)) { log_write(LOG_NORMAL|LOG_SKID|LOG_STDOUT,"Skipping host %s due to host timeout\n", currenths->NameIP(hostname, sizeof(hostname))); @@ -1592,6 +1588,8 @@ " -PS/PA/PU [portlist]: TCP SYN/ACK or UDP discovery to given ports\n" " -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes\n" " -n/-R: Never do DNS resolution/Always resolve [default: sometimes]\n" + " --dns_servers : Specify custom DNS servers\n" + " --system_dns: Use OS's DNS resolver\n" "SCAN TECHNIQUES:\n" " -sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans\n" " -sN/sF/sX: TCP Null, FIN, and Xmas scans\n" diff -urNb old.nmap.rev/nmap_dns.cc new.nmap.rev/nmap_dns.cc --- old.nmap.rev/nmap_dns.cc 1969-12-31 16:00:00.000000000 -0800 +++ new.nmap.rev/nmap_dns.cc 2005-12-30 20:06:09.000000000 -0800 @@ -0,0 +1,1014 @@ +// 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 (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: c:\windows\hosts +// for NT/2000/XP Pro: c:\winnt\system32\drivers\etc\hosts +// for XP Home: c:\windows\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 +// +// * Resolve IPs that resolve to CNAMES: +// (very rare - only affects some misconfigured servers) +// $ host 217.31.174.62 +// 62.174.31.217.in-addr.arpa is an alias for 62.0-25.174.31.217.in-addr.arpa. +// 62.0-25.174.31.217.in-addr.arpa domain name pointer bitnetcomp62.bitnet.nu. + + +#include +#include +#include +#include + +#include "nmap.h" +#include "NmapOps.h" +#include "nmap_dns.h" +#include "nsock.h" +#include "utils.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 5 +#define CAPACITY_MAX 200 +#define CAPACITY_UP_STEP 2 +#define CAPACITY_MINOR_DOWN_SCALE 0.9 +#define CAPACITY_MAJOR_DOWN_SCALE 0.5 + +// 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 *ipaddr; + nsock_iod nsd; + int connected; + int reqs_on_wire; + int capacity; + int write_busy; + std::list to_process; + std::list 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 --------------------- + +std::list servs; +std::list new_reqs; +int total_reqs; +nsock_pool dnspool=NULL; + +int etchosts_filled=0; +std::list etchosts[HASH_TABLE_SIZE]; + +int stat_actual, stat_ok, stat_nx, stat_sf, stat_trans, stat_dropped; +struct timeval starttv; +int read_timeout_index; +u16 id_counter; + + +//------------------- 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 [#: %d, 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->ipaddr, 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) { + std::list::iterator servI; + std::list::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--; + + 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. +// ASSUMES NAME LENGTH/VALIDITY HAS ALREADY BEEN VERIFIED +u32 parse_inaddr_arpa(char *buf) { + u32 ip=0; + int i, j; + + for (i=3; i>=0; i--) { + if (buf[0] < 1 || buf[0] > 3) return 0; + for (j=1; j<=buf[0]; j++) if (!isdigit(buf[j])) return 0; + + ip |= atoi(buf+1) << (8*i); + buf += buf[0] + 1; + } + + if (strcasecmp(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(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(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) { + 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 = 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= buflen) return; + curbuf += 4; + + if (errcode) { + struct in_addr ia; + ia.s_addr = parse_inaddr_arpa(buf+nameloc); + int found = process_result(ia.s_addr, NULL); + + 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= 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) { + struct in_addr ia; + char outbuf[512]; + + ia.s_addr = parse_inaddr_arpa(buf+nameloc); + + curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc); + if (curbuf == -1 || curbuf >= buflen) return; + + encoded_name_to_normal(buf+nameloc, outbuf, sizeof(outbuf)); + + if (process_result(ia.s_addr, outbuf)) { + 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++; + } + } + + 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::iterator servI; + std::list::iterator servItemp; + std::list::iterator reqI; + dns_server *tpserv; + request *tpreq; + struct timeval now; + int tp, min_timeout = INT_MAX; + + memcpy(&now, nsock_gettimeofday(), sizeof(struct timeval)); + + for(servI = servs.begin(); servI != servs.end(); servI++) { + tpserv = *servI; + + restart_loop: + + for(reqI = tpserv->in_process.begin(); reqI != tpserv->in_process.end(); reqI++) { + 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.remove(tpreq); + 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); + } + + goto restart_loop; + } + } + + } + + if (min_timeout == INT_MAX) return 200; + else return min_timeout; + +} + + +// Puts as many packets on the line as capacity will allow +void do_possible_writes() { + std::list::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->ipaddr); + 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::iterator servI; + dns_server *tpserv; + char *ipaddr; + struct in_addr junk; + + ipaddr = strtok(ipaddrs, " ,"); + + while (ipaddr != NULL) { + + for(servI = servs.begin(); servI != servs.end(); servI++) { + tpserv = *servI; + + // Already added! + if (strcmp(ipaddr, tpserv->ipaddr) == 0) break; + } + + // If it hasn't already been added and the address seems valid, add it! + if (servI == servs.end() && inet_aton(ipaddr, &junk)) { + tpserv = new dns_server; + + tpserv->ipaddr = strdup(ipaddr); + + servs.push_front(tpserv); + } + + ipaddr = strtok(NULL, " ,"); + } + +} + + +// Creates a new nsi for each DNS server +void connect_dns_servers() { + std::list::iterator serverI; + dns_server *s; + struct sockaddr_in ss; + int ss_len; + + 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; + + ss_len = sizeof(struct sockaddr_in); + ss.sin_family = AF_INET; + inet_aton(s->ipaddr, &ss.sin_addr); + + nsock_connect_udp(dnspool, s->nsd, connect_evt_handler, NULL, (struct sockaddr *) &ss, ss_len, 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::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::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; + request *tpreq; + int timeout; + char *tpname; + + 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) { + if (currenths->TargetSockAddr(&ss, &sslen) != 0) + fatal("Failed to get target socket address."); + stat_actual++; + if (getnameinfo((struct sockaddr *)&ss, sslen, hostname, + sizeof(hostname), NULL, 0, NI_NAMEREQD) == 0) { + stat_ok++; + currenths->setHostName(hostname); + } + } + } + + 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 + // Windows 95/98/Me: + parse_etchosts("c:\\windows\\hosts"); + + // Windows NT/2000/XP Pro: + parse_etchosts("c:\\winnt\\system32\\drivers\\etc\\hosts"); + + // Windows XP Home: + parse_etchosts("c:\\windows\\system32\\drivers\\etc\\hosts"); + #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()"); + + connect_dns_servers(); + + read_timeout_index = MIN(sizeof(read_timeouts)/sizeof(read_timeouts[0]), servs.size()) - 1; + + while (total_reqs > 0) { + timeout = deal_with_timedout_reads(); + + do_possible_writes(); + + if (total_reqs <= 0) break; + + nsock_loop(dnspool, timeout); + } + + close_dns_servers(); + + nsp_delete(dnspool); + +} + + + +// 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 = 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 [#: %d, OK: %d, NX: %d, SF: %d, DR: %d, TR: %d]\n", + stat_actual, TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0, + servs.size(), stat_ok, stat_nx, stat_sf, stat_dropped, stat_trans); + } 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); + } + } + +} diff -urNb old.nmap.rev/nmap_dns.h new.nmap.rev/nmap_dns.h --- old.nmap.rev/nmap_dns.h 1969-12-31 16:00:00.000000000 -0800 +++ new.nmap.rev/nmap_dns.h 2005-12-30 20:06:09.000000000 -0800 @@ -0,0 +1,3 @@ +#include "Target.h" + +void nmap_mass_rdns(Target ** targets, int num_targets); diff -urNb old.nmap.rev/NmapOps.cc new.nmap.rev/NmapOps.cc --- old.nmap.rev/NmapOps.cc 2005-12-30 20:06:09.000000000 -0800 +++ new.nmap.rev/NmapOps.cc 2005-12-30 20:06:09.000000000 -0800 @@ -247,6 +247,9 @@ if (xsl_stylesheet) free(xsl_stylesheet); xsl_stylesheet = strdup(tmpxsl); spoof_mac_set = false; + mass_dns = true; + resolve_all = 0; + dns_servers = NULL; } bool NmapOps::TCPScan() { @@ -428,6 +431,9 @@ if (af() == AF_INET6 && (numdecoys|osscan|bouncescan|fragscan|ackscan|finscan|idlescan|ipprotscan|maimonscan|nullscan|rpcscan|synscan|udpscan|windowscan|xmasscan)) { fatal("Sorry -- IPv6 support is currently only available for connect() scan (-sT), ping scan (-sP), and list scan (-sL). Further support is under consideration."); } + + if (af() != AF_INET) mass_dns = false; + } void NmapOps::setMaxRttTimeout(int rtt) diff -urNb old.nmap.rev/NmapOps.h new.nmap.rev/NmapOps.h --- old.nmap.rev/NmapOps.h 2005-12-30 20:06:09.000000000 -0800 +++ new.nmap.rev/NmapOps.h 2005-12-30 20:06:09.000000000 -0800 @@ -282,6 +282,9 @@ FILE *nmap_stdout; /* Nmap standard output */ int ttl; // Time to live char *datadir; + bool mass_dns; + int resolve_all; + char *dns_servers; private: int max_rtt_timeout; int min_rtt_timeout; diff -urNb old.nmap.rev/targets.cc new.nmap.rev/targets.cc --- old.nmap.rev/targets.cc 2005-12-30 20:06:09.000000000 -0800 +++ new.nmap.rev/targets.cc 2005-12-30 20:06:09.000000000 -0800 @@ -108,6 +108,7 @@ #include "TargetGroup.h" #include "Target.h" #include "scan_engine.h" +#include "nmap_dns.h" using namespace std; extern NmapOps o; @@ -433,6 +434,8 @@ else massping(hs->hostbatch, hs->current_batch_sz, ports, *pingtype); + if (!o.noresolve) nmap_mass_rdns(hs->hostbatch, hs->current_batch_sz); + return hs->hostbatch[hs->next_batch_no++]; }