mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
1710 lines
54 KiB
C++
1710 lines
54 KiB
C++
|
|
/***************************************************************************
|
|
* nmap_dns.cc -- Handles parallel reverse DNS resolution for target IPs *
|
|
* *
|
|
***********************IMPORTANT NMAP LICENSE TERMS************************
|
|
* *
|
|
* The Nmap Security Scanner is (C) 1996-2019 Insecure.Com LLC ("The Nmap *
|
|
* Project"). Nmap is also a registered trademark of the Nmap Project. *
|
|
* 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 ("GPL"), BUT ONLY WITH ALL OF THE *
|
|
* CLARIFICATIONS AND EXCEPTIONS DESCRIBED HEREIN. 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 sell alternative licenses (contact sales@nmap.com). *
|
|
* Dozens of software vendors already license Nmap technology such as *
|
|
* host discovery, port scanning, OS detection, version detection, and *
|
|
* the Nmap Scripting Engine. *
|
|
* *
|
|
* Note that the GPL places important restrictions on "derivative works", *
|
|
* yet it does not provide a detailed definition of that term. To avoid *
|
|
* misunderstandings, we interpret that term as broadly as copyright law *
|
|
* allows. For example, we consider an application to constitute a *
|
|
* derivative work for the purpose of this license if it does any of the *
|
|
* following with any software or content covered by this license *
|
|
* ("Covered Software"): *
|
|
* *
|
|
* o Integrates source code from Covered Software. *
|
|
* *
|
|
* o Reads or includes copyrighted data files, such as Nmap's nmap-os-db *
|
|
* or nmap-service-probes. *
|
|
* *
|
|
* o Is designed specifically to execute Covered Software and parse the *
|
|
* results (as opposed to typical shell or execution-menu apps, which will *
|
|
* execute anything you tell them to). *
|
|
* *
|
|
* o Includes Covered Software in a proprietary executable installer. The *
|
|
* installers produced by InstallShield are an example of this. Including *
|
|
* Nmap with other software in compressed or archival form does not *
|
|
* trigger this provision, provided appropriate open source decompression *
|
|
* or de-archiving software is widely available for no charge. For the *
|
|
* purposes of this license, an installer is considered to include Covered *
|
|
* Software even if it actually retrieves a copy of Covered Software from *
|
|
* another source during runtime (such as by downloading it from the *
|
|
* Internet). *
|
|
* *
|
|
* o Links (statically or dynamically) to a library which does any of the *
|
|
* above. *
|
|
* *
|
|
* o Executes a helper program, module, or script to do any of the above. *
|
|
* *
|
|
* This list is not exclusive, but is meant to clarify our interpretation *
|
|
* of derived works with some common examples. Other people may interpret *
|
|
* the plain GPL differently, so we consider this a special exception to *
|
|
* the GPL that we apply to Covered Software. Works which meet any of *
|
|
* these conditions must conform to all of the terms of this license, *
|
|
* particularly including the GPL Section 3 requirements of providing *
|
|
* source code and allowing free redistribution of the work as a whole. *
|
|
* *
|
|
* As another special exception to the GPL terms, the Nmap Project 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 docs/licenses/OpenSSL.txt file, and distribute *
|
|
* linked combinations including the two. *
|
|
* *
|
|
* The Nmap Project has permission to redistribute Npcap, a packet *
|
|
* capturing driver and library for the Microsoft Windows platform. *
|
|
* Npcap is a separate work with it's own license rather than this Nmap *
|
|
* license. Since the Npcap license does not permit redistribution *
|
|
* without special permission, our Nmap Windows binary packages which *
|
|
* contain Npcap may not be redistributed without special permission. *
|
|
* *
|
|
* Any redistribution of Covered Software, including any derived works, *
|
|
* must obey and carry forward all of the terms of this license, including *
|
|
* obeying all GPL rules and restrictions. For example, source code of *
|
|
* the whole work must be provided and free redistribution must be *
|
|
* allowed. All GPL references to "this License", are to be treated as *
|
|
* including the terms and conditions of this license text as well. *
|
|
* *
|
|
* Because this license imposes special exceptions to the GPL, Covered *
|
|
* Work may not be combined (even as part of a larger work) with plain GPL *
|
|
* software. The terms, conditions, and exceptions of this license must *
|
|
* be included as well. This license is incompatible with some other open *
|
|
* source licenses as well. In some cases we can relicense portions of *
|
|
* Nmap or grant special permissions to use it in other open source *
|
|
* software. Please contact fyodor@nmap.org with any such requests. *
|
|
* Similarly, we don't incorporate incompatible open source software into *
|
|
* Covered Software without special permission from the copyright holders. *
|
|
* *
|
|
* If you have any questions about the licensing restrictions on using *
|
|
* Nmap in other works, we are happy to help. As mentioned above, we also *
|
|
* offer an alternative license to integrate Nmap into proprietary *
|
|
* applications and appliances. These contracts have been sold to dozens *
|
|
* of software vendors, and generally include a perpetual license as well *
|
|
* as providing support and updates. They also fund the continued *
|
|
* development of Nmap. Please email sales@nmap.com for further *
|
|
* information. *
|
|
* *
|
|
* If you have received a written license agreement or contract for *
|
|
* Covered Software stating terms other than these, you may choose to use *
|
|
* and redistribute Covered Software under those terms instead of these. *
|
|
* *
|
|
* 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. *
|
|
* *
|
|
* 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 the dev@nmap.org mailing list for possible incorporation into the *
|
|
* main distribution. By sending these changes to Fyodor or one of the *
|
|
* Insecure.Org development mailing lists, or checking them into the Nmap *
|
|
* source code repository, it is understood (unless you specify *
|
|
* otherwise) that you are offering the Nmap Project 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 Nmap *
|
|
* license file for more details (it's in a COPYING file included with *
|
|
* Nmap, and also available from https://svn.nmap.org/nmap/COPYING) *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
// 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
|
|
// DNS Caching and aging added by Eddie Bell ejlbell@gmail.com 2007
|
|
// IPv6 and improved DNS cache by Gioacchino Mazzurco <gmazzurco89@gmail.com> 2015
|
|
|
|
|
|
// TODO:
|
|
//
|
|
// * Tune performance parameters
|
|
//
|
|
// * Figure out best way to estimate completion time
|
|
// and display it in a ScanProgressMeter
|
|
|
|
#ifdef WIN32
|
|
#include "nmap_winconfig.h"
|
|
/* Need DnetName2PcapName */
|
|
#include "libnetutil/netutil.h"
|
|
#endif
|
|
|
|
#include "nmap.h"
|
|
#include "NmapOps.h"
|
|
#include "nmap_dns.h"
|
|
#include "nsock.h"
|
|
#include "nmap_error.h"
|
|
#include "nmap_tty.h"
|
|
#include "tcpip.h"
|
|
#include "timing.h"
|
|
#include "Target.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <list>
|
|
#include <vector>
|
|
|
|
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
|
|
static 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.9
|
|
#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
|
|
|
|
|
|
//------------------- Internal Structures ---------------------
|
|
|
|
struct dns_server;
|
|
struct request;
|
|
typedef struct sockaddr_storage sockaddr_storage;
|
|
|
|
struct dns_server {
|
|
std::string hostname;
|
|
sockaddr_storage addr;
|
|
size_t addr_len;
|
|
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 {
|
|
Target *targ;
|
|
struct timeval timeout;
|
|
int tries;
|
|
int servers_tried;
|
|
dns_server *first_server;
|
|
dns_server *curr_server;
|
|
u16 id;
|
|
};
|
|
|
|
/*keeps record of a request going through a particular DNS server
|
|
helps in attaining faster lookup based on ID */
|
|
struct info{
|
|
dns_server *server;
|
|
request *tpreq;
|
|
};
|
|
|
|
class HostElem
|
|
{
|
|
public:
|
|
HostElem(const std::string & name_, const sockaddr_storage & ip) :
|
|
name(name_), addr(ip), cache_hits(0) {}
|
|
~HostElem() {}
|
|
|
|
/* Ages entries and return true with a cache hit of 0 (the least used) */
|
|
static bool isTimeToClean(HostElem he)
|
|
{
|
|
if(he.cache_hits)
|
|
{
|
|
he.cache_hits >>= 1;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const std::string name;
|
|
const sockaddr_storage addr;
|
|
u8 cache_hits;
|
|
};
|
|
|
|
class HostCacheLine : public std::list<HostElem>{};
|
|
|
|
class HostCache
|
|
{
|
|
public:
|
|
// TODO: avoid hardcode this constant
|
|
HostCache() : lines_count(256), hash_mask(lines_count-1),
|
|
hosts_storage(new HostCacheLine[lines_count]), elements_count(0)
|
|
{}
|
|
~HostCache()
|
|
{
|
|
delete[] hosts_storage;
|
|
}
|
|
|
|
u32 hash(sockaddr_storage ip)
|
|
{
|
|
u32 ret = 0;
|
|
|
|
switch (ip.ss_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
u8 * ipv4 = (u8 *) &((const struct sockaddr_in *) &ip)->sin_addr;
|
|
// Shuffle bytes a little so we avoid awful performances in commons
|
|
// usages patterns like 10.0.1-255.1 and lines_count 256
|
|
ret = ipv4[0] + (ipv4[1]<<3) + (ipv4[2]<<5) + (ipv4[3]<<7);
|
|
break;
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *) &ip;
|
|
u32 * ipv6 = (u32 *) sa6->sin6_addr.s6_addr;
|
|
ret = ipv6[0] + ipv6[1] + ipv6[2] + ipv6[3];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret & hash_mask;
|
|
}
|
|
|
|
/* Add to the dns cache. If there are too many entries
|
|
* we age and remove the least frequently used ones to
|
|
* make more space. */
|
|
bool add( const sockaddr_storage & ip, const std::string & hname)
|
|
{
|
|
std::string discard;
|
|
if(lookup(ip, discard)) return false;
|
|
|
|
if(elements_count >= lines_count) prune();
|
|
|
|
HostElem he(hname, ip);
|
|
hosts_storage[hash(ip)].push_back(he);
|
|
++elements_count;
|
|
return true;
|
|
}
|
|
|
|
u32 prune()
|
|
{
|
|
u32 original_count = elements_count;
|
|
for(u32 i = 0; i < lines_count; ++i)
|
|
{
|
|
std::list<HostElem>::iterator it = find_if(hosts_storage[i].begin(),
|
|
hosts_storage[i].end(),
|
|
HostElem::isTimeToClean);
|
|
while ( it != hosts_storage[i].end() )
|
|
{
|
|
it = hosts_storage[i].erase(it);
|
|
assert(elements_count > 0);
|
|
--elements_count;
|
|
}
|
|
}
|
|
|
|
return original_count - elements_count;
|
|
}
|
|
|
|
/* Search for a hostname in the cache and increment
|
|
* its cache hit counter if found */
|
|
bool lookup(const sockaddr_storage & ip, std::string & name)
|
|
{
|
|
std::list<HostElem>::iterator hostI;
|
|
u32 ip_hash = hash(ip);
|
|
for( hostI = hosts_storage[ip_hash].begin();
|
|
hostI != hosts_storage[ip_hash].end();
|
|
++hostI)
|
|
{
|
|
if (sockaddr_storage_equal(&hostI->addr, &ip))
|
|
{
|
|
if(hostI->cache_hits < UCHAR_MAX)
|
|
hostI->cache_hits++;
|
|
name = hostI->name;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
const u32 lines_count;
|
|
const u32 hash_mask;
|
|
HostCacheLine * const hosts_storage;
|
|
u32 elements_count;
|
|
};
|
|
|
|
//------------------- Globals ---------------------
|
|
|
|
u16 DNS::Factory::progressiveId = get_random_u16();
|
|
static std::list<dns_server> servs;
|
|
static std::list<request *> new_reqs;
|
|
static std::list<request *> deferred_reqs;
|
|
static std::map<u16, info> records;
|
|
static int total_reqs;
|
|
static nsock_pool dnspool=NULL;
|
|
|
|
/* The DNS cache, not just for entries from /etc/hosts. */
|
|
static HostCache host_cache;
|
|
|
|
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 int firstrun=1;
|
|
static ScanProgressMeter *SPM;
|
|
|
|
|
|
//------------------- Prototypes and macros ---------------------
|
|
static void read_evt_handler(nsock_pool, nsock_event, void *);
|
|
static void put_dns_packet_on_wire(request *req);
|
|
|
|
#define ACTION_FINISHED 0
|
|
#define ACTION_SYSTEM_RESOLVE 1
|
|
#define ACTION_TIMEOUT 2
|
|
|
|
//------------------- Misc code ---------------------
|
|
|
|
static void output_summary() {
|
|
int tp = stat_ok + stat_nx + 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, DR: %d, SF: %d, TR: %d]\n",
|
|
TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0,
|
|
tp, stat_actual,
|
|
(unsigned long) servs.size(), stat_ok, stat_nx, stat_dropped, stat_sf, stat_trans);
|
|
}
|
|
|
|
static 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.c_str(), tpserv->capacity);
|
|
}
|
|
|
|
// Closes all nsis created in connect_dns_servers()
|
|
static void close_dns_servers() {
|
|
std::list<dns_server>::iterator serverI;
|
|
|
|
for(serverI = servs.begin(); serverI != servs.end(); serverI++) {
|
|
if (serverI->connected) {
|
|
nsock_iod_delete(serverI->nsd, NSOCK_PENDING_SILENT);
|
|
serverI->connected = 0;
|
|
serverI->to_process.clear();
|
|
serverI->in_process.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Puts as many packets on the line as capacity will allow
|
|
static void do_possible_writes() {
|
|
std::list<dns_server>::iterator servI;
|
|
request *tpreq;
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
if (servI->write_busy == 0 && servI->reqs_on_wire < servI->capacity) {
|
|
tpreq = NULL;
|
|
if (!servI->to_process.empty()) {
|
|
tpreq = servI->to_process.front();
|
|
servI->to_process.pop_front();
|
|
} else if (!new_reqs.empty()) {
|
|
tpreq = new_reqs.front();
|
|
assert(tpreq != NULL);
|
|
tpreq->first_server = tpreq->curr_server = &*servI;
|
|
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() , servI->hostname.c_str());
|
|
stat_trans++;
|
|
put_dns_packet_on_wire(tpreq);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// nsock write handler
|
|
static void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v) {
|
|
info record;
|
|
request *req = (request *) req_v;
|
|
|
|
req->curr_server->write_busy = 0;
|
|
|
|
req->curr_server->in_process.push_front(req);
|
|
record.tpreq = req;
|
|
record.server = req->curr_server;
|
|
records[req->id] = record;
|
|
|
|
do_possible_writes();
|
|
}
|
|
|
|
// 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.
|
|
static void put_dns_packet_on_wire(request *req) {
|
|
const size_t maxlen = 512;
|
|
u8 packet[maxlen];
|
|
size_t plen=0;
|
|
|
|
struct timeval now, timeout;
|
|
|
|
req->id = DNS::Factory::progressiveId;
|
|
req->curr_server->write_busy = 1;
|
|
req->curr_server->reqs_on_wire++;
|
|
|
|
plen = DNS::Factory::buildReverseRequest(*req->targ->TargetSockAddr(), packet, maxlen);
|
|
|
|
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, reinterpret_cast<const char *>(packet), plen);
|
|
}
|
|
|
|
// Processes DNS packets that have timed out
|
|
// Returns time until next read timeout
|
|
static 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;
|
|
std::map<u16, info>::iterator infoI;
|
|
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_dropped) / stat_actual, &now);
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
nextI = servI->in_process.begin();
|
|
if (nextI == servI->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) {
|
|
servI->capacity = (int) (servI->capacity * CAPACITY_MINOR_DOWN_SCALE);
|
|
check_capacities(&*servI);
|
|
servI->in_process.erase(reqI);
|
|
std::map<u16, info>::iterator it = records.find(tpreq->id);
|
|
if ( it != records.end() )
|
|
records.erase(it);
|
|
servI->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) {
|
|
servI->capacity = (int) (servI->capacity * CAPACITY_MAJOR_DOWN_SCALE);
|
|
check_capacities(&*servI);
|
|
|
|
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--;
|
|
infoI = records.find(tpreq->id);
|
|
if ( infoI != records.end() )
|
|
records.erase(infoI);
|
|
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 {
|
|
servI->to_process.push_back(tpreq);
|
|
}
|
|
|
|
}
|
|
|
|
} while (nextI != servI->in_process.end());
|
|
|
|
}
|
|
|
|
if (min_timeout > 500) return 500;
|
|
else return min_timeout;
|
|
|
|
}
|
|
|
|
// 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
|
|
static int process_result(const sockaddr_storage &ip, const std::string &result, int action, u16 id)
|
|
{
|
|
request *tpreq;
|
|
std::map<u16, info>::iterator infoI;
|
|
dns_server *server;
|
|
|
|
infoI = records.find(id);
|
|
|
|
if( infoI != records.end() ){
|
|
|
|
tpreq = infoI->second.tpreq;
|
|
server = infoI->second.server;
|
|
|
|
if( !result.empty() && !sockaddr_storage_equal(&ip, tpreq->targ->TargetSockAddr()) )
|
|
return 0;
|
|
|
|
if (action == ACTION_SYSTEM_RESOLVE || action == ACTION_FINISHED)
|
|
{
|
|
server->capacity += CAPACITY_UP_STEP;
|
|
check_capacities(&*server);
|
|
|
|
if(!result.empty())
|
|
{
|
|
tpreq->targ->setHostName(result.c_str());
|
|
host_cache.add(* tpreq->targ->TargetSockAddr(), result);
|
|
}
|
|
|
|
records.erase(infoI);
|
|
server->in_process.remove(tpreq);
|
|
server->reqs_on_wire--;
|
|
|
|
total_reqs--;
|
|
|
|
if (action == ACTION_SYSTEM_RESOLVE) deferred_reqs.push_back(tpreq);
|
|
if (action == ACTION_FINISHED) delete tpreq;
|
|
}
|
|
else
|
|
{
|
|
memcpy(&tpreq->timeout, nsock_gettimeofday(), sizeof(struct timeval));
|
|
deal_with_timedout_reads();
|
|
}
|
|
|
|
do_possible_writes();
|
|
|
|
// Close DNS servers if we're all done so that we kill
|
|
// all events and return from nsock_loop immediateley
|
|
if (total_reqs == 0)
|
|
close_dns_servers();
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Nsock read handler. One nsock read for each DNS server exists at each
|
|
// time. This function uses various helper functions as defined above.
|
|
static void read_evt_handler(nsock_pool nsp, nsock_event evt, void *) {
|
|
u8 *buf;
|
|
int buflen;
|
|
|
|
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 %s()\n",
|
|
nse_type2str(nse_type(evt)),
|
|
nse_status2str(nse_status(evt)), __func__);
|
|
return;
|
|
}
|
|
|
|
buf = (unsigned char *) nse_readbuf(evt, &buflen);
|
|
|
|
DNS::Packet p;
|
|
size_t readed_bytes = p.parseFromBuffer(buf, buflen);
|
|
if(readed_bytes < DNS::DATA) return;
|
|
|
|
// We should have 1+ queries:
|
|
u16 &f = p.flags;
|
|
if(p.queries.empty() || !DNS_HAS_FLAG(f, DNS::RESPONSE) ||
|
|
!DNS_HAS_FLAG(f, DNS::OP_STANDARD_QUERY) ||
|
|
(f & DNS::ZERO) || DNS_HAS_ERR(f, DNS::ERR_FORMAT) ||
|
|
DNS_HAS_ERR(f, DNS::ERR_NOT_IMPLEMENTED) || DNS_HAS_ERR(f, DNS::ERR_REFUSED))
|
|
return;
|
|
|
|
if (DNS_HAS_ERR(f, DNS::ERR_NAME))
|
|
{
|
|
sockaddr_storage discard;
|
|
if(process_result(discard, "", ACTION_FINISHED, p.id))
|
|
{
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL)
|
|
log_write(LOG_STDOUT, "mass_rdns: NXDOMAIN <id = %d>\n", p.id);
|
|
output_summary();
|
|
stat_nx++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (DNS_HAS_ERR(f, DNS::ERR_SERVFAIL))
|
|
{
|
|
sockaddr_storage discard;
|
|
if (process_result(discard, "", ACTION_TIMEOUT, p.id))
|
|
{
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL)
|
|
log_write(LOG_STDOUT, "mass_rdns: SERVFAIL <id = %d>\n", p.id);
|
|
stat_sf++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool processing_successful = false;
|
|
|
|
sockaddr_storage ip;
|
|
ip.ss_family = AF_UNSPEC;
|
|
std::string alias;
|
|
|
|
for(std::list<DNS::Answer>::const_iterator it = p.answers.begin();
|
|
it != p.answers.end() && !processing_successful; ++it )
|
|
{
|
|
const DNS::Answer &a = *it;
|
|
if(a.record_class == DNS::CLASS_IN)
|
|
{
|
|
switch(a.record_type)
|
|
{
|
|
case DNS::PTR:
|
|
{
|
|
DNS::PTR_Record * ptr = static_cast<DNS::PTR_Record *>(a.record);
|
|
|
|
if(
|
|
// If CNAME answer filled in ip with a matching alias
|
|
(ip.ss_family != AF_UNSPEC && a.name == alias )
|
|
// Or if we can get an IP from reversing the .arpa PTR address
|
|
|| DNS::Factory::ptrToIp(a.name, ip))
|
|
{
|
|
if ((processing_successful = process_result(ip, ptr->value, ACTION_FINISHED, p.id)))
|
|
{
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL)
|
|
{
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
sockaddr_storage_iptop(&ip, ipstr);
|
|
log_write(LOG_STDOUT, "mass_rdns: OK MATCHED <%s> to <%s>\n",
|
|
ipstr,
|
|
ptr->value.c_str());
|
|
}
|
|
output_summary();
|
|
stat_ok++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case DNS::CNAME:
|
|
{
|
|
if(DNS::Factory::ptrToIp(a.name, ip))
|
|
{
|
|
DNS::CNAME_Record * cname = static_cast<DNS::CNAME_Record *>(a.record);
|
|
alias = cname->value;
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL)
|
|
{
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
sockaddr_storage_iptop(&ip, ipstr);
|
|
log_write(LOG_STDOUT, "mass_rdns: CNAME found for <%s> to <%s>\n", ipstr, alias.c_str());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!processing_successful) {
|
|
if (DNS_HAS_FLAG(f, DNS::TRUNCATED)) {
|
|
// TODO: TCP fallback, or only use system resolver if user didn't specify --dns-servers
|
|
process_result(ip, "", ACTION_SYSTEM_RESOLVE, p.id);
|
|
}
|
|
else if (!alias.empty()) {
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL)
|
|
{
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
sockaddr_storage_iptop(&ip, ipstr);
|
|
log_write(LOG_STDOUT, "mass_rdns: CNAME for <%s> not processed.\n", ipstr);
|
|
}
|
|
// TODO: Send a PTR request for alias instead. Meanwhile, we'll just fall
|
|
// back to using system resolver. Alternative: report the canonical name
|
|
// (alias), but that's not very useful.
|
|
process_result(ip, "", ACTION_SYSTEM_RESOLVE, p.id);
|
|
}
|
|
else {
|
|
if (o.debugging >= TRACE_DEBUG_LEVEL) {
|
|
log_write(LOG_STDOUT, "mass_rdns: Unable to process the response\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// nsock connect handler - Empty because it doesn't really need to do anything...
|
|
static void connect_evt_handler(nsock_pool, nsock_event, void *) {}
|
|
|
|
|
|
// Adds DNS servers to the dns_server list. They can be separated by
|
|
// commas or spaces - NOTE this doesn't actually do any connecting!
|
|
static void add_dns_server(char *ipaddrs) {
|
|
std::list<dns_server>::iterator servI;
|
|
char *hostname;
|
|
struct sockaddr_storage addr;
|
|
size_t addr_len = sizeof(addr);
|
|
|
|
for (hostname = strtok(ipaddrs, " ,"); hostname != NULL; hostname = strtok(NULL, " ,")) {
|
|
|
|
if (resolve(hostname, 0, (struct sockaddr_storage *) &addr, &addr_len,
|
|
o.spoofsource ? o.af() : PF_UNSPEC) != 0)
|
|
continue;
|
|
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
// Already added!
|
|
if (memcmp(&addr, &servI->addr, sizeof(addr)) == 0) break;
|
|
}
|
|
|
|
// If it hasn't already been added, add it!
|
|
if (servI == servs.end()) {
|
|
dns_server tpserv;
|
|
|
|
tpserv.hostname = hostname;
|
|
memcpy(&tpserv.addr, &addr, sizeof(addr));
|
|
tpserv.addr_len = addr_len;
|
|
|
|
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
|
|
static void connect_dns_servers() {
|
|
std::list<dns_server>::iterator serverI;
|
|
for(serverI = servs.begin(); serverI != servs.end(); serverI++) {
|
|
serverI->nsd = nsock_iod_new(dnspool, NULL);
|
|
if (o.spoofsource) {
|
|
struct sockaddr_storage ss;
|
|
size_t sslen;
|
|
o.SourceSockAddr(&ss, &sslen);
|
|
nsock_iod_set_localaddr(serverI->nsd, &ss, sslen);
|
|
}
|
|
if (o.ipoptionslen)
|
|
nsock_iod_set_ipoptions(serverI->nsd, o.ipoptions, o.ipoptionslen);
|
|
serverI->reqs_on_wire = 0;
|
|
serverI->capacity = CAPACITY_MIN;
|
|
serverI->write_busy = 0;
|
|
|
|
nsock_connect_udp(dnspool, serverI->nsd, connect_evt_handler, NULL, (struct sockaddr *) &serverI->addr, serverI->addr_len, 53);
|
|
nsock_read(dnspool, serverI->nsd, read_evt_handler, -1, NULL);
|
|
serverI->connected = 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#ifdef WIN32
|
|
static bool interface_is_known_by_guid(const char *guid) {
|
|
struct interface_info *iflist;
|
|
int i, n;
|
|
|
|
iflist = getinterfaces(&n, NULL, 0);
|
|
if (iflist == NULL)
|
|
return false;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
char pcap_name[1024];
|
|
char *pcap_guid;
|
|
|
|
if (!DnetName2PcapName(iflist[i].devname, pcap_name, sizeof(pcap_name)))
|
|
continue;
|
|
pcap_guid = strchr(pcap_name, '{');
|
|
if (pcap_guid == NULL)
|
|
continue;
|
|
if (strcasecmp(guid, pcap_guid) == 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Reads the Windows registry and adds all the nameservers found via the
|
|
// add_dns_server() function.
|
|
void win32_read_registry() {
|
|
HKEY hKey;
|
|
HKEY hKey2;
|
|
char keybasebuf[2048];
|
|
char buf[2048], keyname[2048], *p;
|
|
DWORD sz, i;
|
|
|
|
Snprintf(keybasebuf, sizeof(keybasebuf), "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters");
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keybasebuf,
|
|
0, KEY_READ, &hKey) != ERROR_SUCCESS) {
|
|
if (firstrun) error("mass_dns: warning: Error opening registry to read DNS servers. Try using --system-dns or specify valid servers with --dns-servers");
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
Snprintf(keybasebuf, sizeof(keybasebuf), "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces");
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keybasebuf,
|
|
0, KEY_ENUMERATE_SUB_KEYS, &hKey) == ERROR_SUCCESS) {
|
|
|
|
for (i=0; sz = sizeof(buf), RegEnumKeyEx(hKey, i, buf, &sz, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; i++) {
|
|
|
|
// If we don't have pcap, interface_is_known_by_guid will crash. Just use any servers we can find.
|
|
if (o.have_pcap && !interface_is_known_by_guid(buf)) {
|
|
if (o.debugging > 1)
|
|
log_write(LOG_PLAIN, "Interface %s is not known; ignoring its nameservers.\n", buf);
|
|
continue;
|
|
}
|
|
|
|
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 // WIN32
|
|
|
|
|
|
|
|
// Parses /etc/resolv.conf (unix) and adds all the nameservers found via the
|
|
// add_dns_server() function.
|
|
static void parse_resolvdotconf() {
|
|
FILE *fp;
|
|
char buf[2048], *tp;
|
|
char fmt[32];
|
|
char ipaddr[INET6_ADDRSTRLEN+1];
|
|
|
|
fp = fopen("/etc/resolv.conf", "r");
|
|
if (fp == NULL) {
|
|
if (firstrun) error("mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers");
|
|
return;
|
|
}
|
|
|
|
Snprintf(fmt, sizeof(fmt), "nameserver %%%us", INET6_ADDRSTRLEN);
|
|
|
|
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, fmt, ipaddr) == 1) add_dns_server(ipaddr);
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
static void parse_etchosts(const char *fname) {
|
|
FILE *fp;
|
|
char buf[2048], hname[256], ipaddrstr[INET6_ADDRSTRLEN+1], *tp;
|
|
sockaddr_storage ia;
|
|
|
|
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++;
|
|
|
|
std::stringstream pattern;
|
|
pattern << "%" << INET6_ADDRSTRLEN << "s %255s";
|
|
if (sscanf(tp, pattern.str().c_str(), ipaddrstr, hname) == 2)
|
|
if (sockaddr_storage_inet_pton(ipaddrstr, &ia))
|
|
{
|
|
const std::string hname_ = hname;
|
|
host_cache.add(ia, hname_);
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
static void etchosts_init(void) {
|
|
static int initialized = 0;
|
|
if (initialized) return;
|
|
initialized = 1;
|
|
|
|
#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 // WIN32
|
|
}
|
|
|
|
/* Initialize the global servs list of DNS servers. If the --dns-servers option
|
|
* was given, use the listed servers; otherwise get the list from resolv.conf or
|
|
* the Windows registry. If o.mass_dns is false, the list of servers is empty.
|
|
* This function caches the results from the first time it is run. */
|
|
static void init_servs(void) {
|
|
static bool initialized = false;
|
|
|
|
if (initialized)
|
|
return;
|
|
|
|
initialized = true;
|
|
|
|
if (!o.mass_dns)
|
|
return;
|
|
|
|
if (o.dns_servers) {
|
|
add_dns_server(o.dns_servers);
|
|
} else {
|
|
#ifndef WIN32
|
|
parse_resolvdotconf();
|
|
#else
|
|
win32_read_registry();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//------------------- Main loops ---------------------
|
|
|
|
|
|
// Actual main loop
|
|
static void nmap_mass_rdns_core(Target **targets, int num_targets) {
|
|
|
|
Target **hostI;
|
|
std::list<request *>::iterator reqI;
|
|
request *tpreq;
|
|
int timeout;
|
|
const char *tpname;
|
|
int i;
|
|
char spmobuf[1024];
|
|
|
|
// If necessary, set up the dns server list
|
|
init_servs();
|
|
|
|
if (servs.size() == 0 && firstrun) error("mass_dns: warning: Unable to "
|
|
"determine any DNS servers. Reverse"
|
|
" DNS is disabled. Try using "
|
|
"--system-dns or specify valid "
|
|
"servers with --dns-servers");
|
|
|
|
|
|
// If necessary, read /etc/hosts and put entries into the hashtable
|
|
etchosts_init();
|
|
|
|
|
|
total_reqs = 0;
|
|
|
|
// Set up the request structure
|
|
for(hostI = targets; hostI < targets+num_targets; hostI++)
|
|
{
|
|
if (!((*hostI)->flags & HOST_UP) && !o.always_resolve) continue;
|
|
|
|
// See if it's cached
|
|
std::string res;
|
|
if (host_cache.lookup(*(*hostI)->TargetSockAddr(), res)) {
|
|
tpname = res.c_str();
|
|
(*hostI)->setHostName(tpname);
|
|
continue;
|
|
}
|
|
|
|
tpreq = new request;
|
|
tpreq->targ = *hostI;
|
|
tpreq->tries = 0;
|
|
tpreq->servers_tried = 0;
|
|
|
|
new_reqs.push_back(tpreq);
|
|
|
|
stat_actual++;
|
|
total_reqs++;
|
|
}
|
|
|
|
if (total_reqs == 0 || servs.size() == 0) return;
|
|
|
|
// And finally, do it!
|
|
|
|
if ((dnspool = nsock_pool_new(NULL)) == NULL)
|
|
fatal("Unable to create nsock pool in %s()", __func__);
|
|
|
|
nmap_set_nsock_logger();
|
|
nmap_adjust_loglevel(o.packetTrace());
|
|
|
|
nsock_pool_set_device(dnspool, o.device);
|
|
|
|
if (o.proxy_chain)
|
|
nsock_pool_set_proxychain(dnspool, o.proxy_chain);
|
|
|
|
connect_dns_servers();
|
|
|
|
deferred_reqs.clear();
|
|
|
|
read_timeout_index = MIN(sizeof(read_timeouts)/sizeof(read_timeouts[0]), servs.size()) - 1;
|
|
|
|
Snprintf(spmobuf, sizeof(spmobuf), "Parallel DNS resolution of %d host%s.", stat_actual, stat_actual-1 ? "s" : "");
|
|
SPM = new ScanProgressMeter(spmobuf);
|
|
|
|
while (total_reqs > 0) {
|
|
timeout = deal_with_timedout_reads();
|
|
|
|
do_possible_writes();
|
|
|
|
if (total_reqs <= 0) break;
|
|
|
|
/* Because this can change with runtime interaction */
|
|
nmap_adjust_loglevel(o.packetTrace());
|
|
|
|
nsock_loop(dnspool, timeout);
|
|
}
|
|
|
|
SPM->endTask(NULL, NULL);
|
|
delete SPM;
|
|
|
|
close_dns_servers();
|
|
|
|
nsock_pool_delete(dnspool);
|
|
|
|
if (deferred_reqs.size() && o.debugging)
|
|
log_write(LOG_STDOUT, "Performing system-dns for %d domain names that were deferred\n", (int) deferred_reqs.size());
|
|
|
|
if (deferred_reqs.size()) {
|
|
Snprintf(spmobuf, sizeof(spmobuf), "System DNS resolution of %u host%s.", (unsigned) deferred_reqs.size(), deferred_reqs.size()-1 ? "s" : "");
|
|
SPM = new ScanProgressMeter(spmobuf);
|
|
|
|
for(i=0, reqI = deferred_reqs.begin(); reqI != deferred_reqs.end(); reqI++, i++) {
|
|
struct sockaddr_storage ss;
|
|
size_t sslen;
|
|
char hostname[FQDN_LEN + 1] = "";
|
|
|
|
if (keyWasPressed())
|
|
SPM->printStats((double) i / deferred_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;
|
|
|
|
}
|
|
|
|
SPM->endTask(NULL, NULL);
|
|
delete SPM;
|
|
}
|
|
|
|
deferred_reqs.clear();
|
|
|
|
}
|
|
|
|
static void nmap_system_rdns_core(Target **targets, int num_targets) {
|
|
Target **hostI;
|
|
Target *currenths;
|
|
struct sockaddr_storage ss;
|
|
size_t sslen;
|
|
char hostname[FQDN_LEN + 1] = "";
|
|
char spmobuf[1024];
|
|
int i;
|
|
|
|
for(hostI = targets; hostI < targets+num_targets; hostI++) {
|
|
currenths = *hostI;
|
|
|
|
if (((currenths->flags & HOST_UP) || o.always_resolve) && !o.noresolve) stat_actual++;
|
|
}
|
|
|
|
Snprintf(spmobuf, sizeof(spmobuf), "System DNS resolution of %d host%s.", stat_actual, stat_actual-1 ? "s" : "");
|
|
SPM = new ScanProgressMeter(spmobuf);
|
|
|
|
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.always_resolve) && !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);
|
|
}
|
|
}
|
|
}
|
|
|
|
SPM->endTask(NULL, NULL);
|
|
delete SPM;
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
|
if (o.mass_dns)
|
|
nmap_mass_rdns_core(targets, num_targets);
|
|
else
|
|
nmap_system_rdns_core(targets, num_targets);
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
if (stat_actual > 0) {
|
|
if (o.debugging || o.verbose >= 3) {
|
|
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'
|
|
// DR: Dropped IPs (no valid responses were received)
|
|
// SF: Number of IPs that got 'Server Failure's
|
|
// 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, DR: %d, SF: %d, TR: %d, CN: %d]\n",
|
|
stat_actual, TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0,
|
|
(unsigned long) servs.size(), stat_ok, stat_nx, stat_dropped, stat_sf, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
firstrun=0;
|
|
}
|
|
|
|
|
|
// Returns a list of known DNS servers
|
|
std::list<std::string> get_dns_servers() {
|
|
init_servs();
|
|
|
|
// If the user said --system-dns (!o.mass_dns), we should never return a list
|
|
// of servers.
|
|
assert(o.mass_dns || servs.empty());
|
|
|
|
std::list<dns_server>::iterator servI;
|
|
std::list<std::string> serverList;
|
|
for(servI = servs.begin(); servI != servs.end(); servI++) {
|
|
serverList.push_back(inet_socktop((struct sockaddr_storage *) &servI->addr));
|
|
}
|
|
return serverList;
|
|
}
|
|
|
|
bool DNS::Factory::ipToPtr(const sockaddr_storage &ip, std::string &ptr)
|
|
{
|
|
switch (ip.ss_family) {
|
|
case AF_INET:
|
|
{
|
|
ptr.clear();
|
|
char ipv4_c[INET_ADDRSTRLEN];
|
|
if(!sockaddr_storage_iptop(&ip, ipv4_c)) return false;
|
|
|
|
std::string ipv4 = ipv4_c;
|
|
std::string octet;
|
|
std::string::const_reverse_iterator crend = ipv4.rend();
|
|
for (std::string::const_reverse_iterator c=ipv4.rbegin(); c != crend; ++c)
|
|
if((*c)=='.')
|
|
{
|
|
ptr += octet + ".";
|
|
octet.clear();
|
|
}
|
|
else
|
|
octet = (*c) + octet;
|
|
|
|
ptr += octet + IPV4_PTR_DOMAIN;
|
|
|
|
break;
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
ptr.clear();
|
|
const struct sockaddr_in6 &s6 = (const struct sockaddr_in6 &) ip;
|
|
const u8 * ipv6 = s6.sin6_addr.s6_addr;
|
|
for (short i=15; i>=0; --i)
|
|
{
|
|
char tmp[3];
|
|
sprintf(tmp, "%02x", ipv6[i]);
|
|
ptr += '.';
|
|
ptr += tmp[1];
|
|
ptr += '.';
|
|
ptr += tmp[0];
|
|
}
|
|
ptr.erase(ptr.begin());
|
|
ptr += IPV6_PTR_DOMAIN;
|
|
break;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DNS::Factory::ptrToIp(const std::string &ptr, sockaddr_storage &ip)
|
|
{
|
|
std::string ip_str;
|
|
|
|
size_t pos = ptr.rfind(IPV6_PTR_DOMAIN);
|
|
if(pos != std::string::npos)
|
|
{
|
|
u8 counter = 0;
|
|
for (std::string::const_reverse_iterator it = ptr.rend()-pos; it != ptr.rend(); ++it)
|
|
{
|
|
const char &c = *it;
|
|
if(c != '.')
|
|
{
|
|
ip_str += c;
|
|
if(++counter==4) counter=0, ip_str+=':';
|
|
}
|
|
}
|
|
|
|
std::string::iterator it = ip_str.end()-1;
|
|
if( *it == ':') ip_str.erase(it);
|
|
}
|
|
|
|
std::string mptr = '.' + ptr;
|
|
pos = mptr.rfind(IPV4_PTR_DOMAIN);
|
|
if(pos != std::string::npos)
|
|
{
|
|
|
|
std::string octet;
|
|
std::string::const_reverse_iterator crend = mptr.rend();
|
|
for (std::string::const_reverse_iterator it = crend-pos; it != crend; ++it)
|
|
{
|
|
const char &c = *it;
|
|
if(c == '.')
|
|
{
|
|
std::reverse(octet.begin(), octet.end());
|
|
ip_str += octet + '.';
|
|
octet.clear();
|
|
}
|
|
else octet += c;
|
|
}
|
|
|
|
std::string::iterator it = ip_str.end()-1;
|
|
if( *it == '.') ip_str.erase(it);
|
|
}
|
|
|
|
if(ip_str.empty())
|
|
return false;
|
|
|
|
return sockaddr_storage_inet_pton(ip_str.c_str(), &ip);
|
|
}
|
|
|
|
size_t DNS::Factory::buildSimpleRequest(const std::string &name, RECORD_TYPE rt, u8 *buf, size_t maxlen)
|
|
{
|
|
size_t ret=0 , tmp=0;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(progressiveId++, buf, ID, maxlen)); // Postincrement inmportant here
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(OP_STANDARD_QUERY | RECURSION_DESIRED, buf, FLAGS_OFFSET, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(1, buf, QDCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(0, buf, ANCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(0, buf, NSCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(0, buf, ARCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putDomainName(name, buf, DATA, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(rt, buf, ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(CLASS_IN, buf, ret, maxlen));
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t DNS::Factory::buildReverseRequest(const sockaddr_storage &ip, u8 *buf, size_t maxlen)
|
|
{
|
|
std::string name;
|
|
if(ipToPtr(ip,name))
|
|
return buildSimpleRequest(name, PTR, buf, maxlen);
|
|
return 0;
|
|
}
|
|
|
|
size_t DNS::Factory::putUnsignedShort(u16 num, u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t max_access = offset+1;
|
|
if(buf && (maxlen > max_access))
|
|
{
|
|
buf[offset] = (num >> 8) & 0xFF;
|
|
buf[max_access] = num & 0xFF;
|
|
return 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t DNS::Factory::putDomainName(const std::string &name, u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t ret=0;
|
|
if( !( buf && (maxlen > (offset + name.length() + 1))) ) return ret;
|
|
|
|
std::string namew = name + ".";
|
|
std::string accumulator;
|
|
for (std::string::const_iterator c=namew.begin(); c != namew.end(); ++c)
|
|
{
|
|
if((*c)=='.')
|
|
{
|
|
u8 length = accumulator.length();
|
|
*(buf+offset+ret) = length;
|
|
ret += 1;
|
|
|
|
memcpy(buf+offset+ret, accumulator.c_str(), length);
|
|
ret += length;
|
|
accumulator.clear();
|
|
}
|
|
else
|
|
accumulator += (*c);
|
|
}
|
|
|
|
*(buf+offset+ret) = 0;
|
|
ret += 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t DNS::Factory::parseUnsignedShort(u16 &num, const u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t max_access = offset+1;
|
|
if(buf && (maxlen > max_access))
|
|
{
|
|
const u8 * n = buf + offset;
|
|
num = n[1] + (n[0]<<8);
|
|
return 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t DNS::Factory::parseUnsignedInt(u32 &num, const u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t max_access = offset+3;
|
|
if(buf && (maxlen > max_access))
|
|
{
|
|
const u8 * n = buf + offset;
|
|
num = n[3] + (n[2]<<8) + (n[1]<<16) + (n[0]<<24);
|
|
return 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t DNS::Factory::parseDomainName(std::string &name, const u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t tmp, ret = 0;
|
|
|
|
name.clear();
|
|
while(u8 label_length = buf[offset+ret++]) // Postincrement important here
|
|
{
|
|
if((label_length & COMPRESSED_NAME) == COMPRESSED_NAME)
|
|
{
|
|
--ret; // The byte it's part of the pointer, wasn't really consumed yet
|
|
u16 real_offset;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, parseUnsignedShort(real_offset, buf, offset+ret, maxlen));
|
|
real_offset -= COMPRESSED_NAME<<8;
|
|
if( real_offset < offset)
|
|
{
|
|
std::string val;
|
|
DNS_CHECK_ACCUMLATE(tmp, tmp, parseDomainName(val, buf, real_offset, maxlen));
|
|
name+=val;
|
|
return ret;
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
for(u8 i=0; i<label_length; ++i)
|
|
{
|
|
size_t index = offset+ret++; // Postincrement important here
|
|
DNS_CHECK_UPPER_BOUND(index, maxlen);
|
|
name += buf[index];
|
|
}
|
|
name += '.';
|
|
}
|
|
|
|
std::string::iterator it = name.end()-1;
|
|
if( *it == '.') name.erase(it);
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t DNS::A_Record::parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t tmp, ret = 0;
|
|
u32 num;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedInt(num, buf, offset, maxlen));
|
|
|
|
memset(&value, 0, sizeof(value));
|
|
struct sockaddr_in * ip4addr = (sockaddr_in *) &value;
|
|
ip4addr->sin_family = AF_INET;
|
|
ip4addr->sin_addr.s_addr = htonl(num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t DNS::Query::parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t ret=0;
|
|
|
|
if (buf && ((maxlen - offset) > 5))
|
|
{
|
|
size_t tmp=0;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseDomainName(name, buf, offset+ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_type, buf, offset+ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_class, buf, offset+ret, maxlen));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t DNS::Answer::parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
|
|
{
|
|
size_t ret=0;
|
|
|
|
if (buf && ((maxlen - offset) > 7))
|
|
{
|
|
size_t tmp;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseDomainName(name, buf, offset+ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_type, buf, offset+ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_class, buf, offset+ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedInt(ttl, buf, offset+ret, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(length, buf, offset+ret, maxlen));
|
|
|
|
DNS_CHECK_UPPER_BOUND(offset+ret+length, maxlen);
|
|
|
|
switch(record_type)
|
|
{
|
|
case A:
|
|
{
|
|
record = new A_Record();
|
|
break;
|
|
}
|
|
case CNAME:
|
|
{
|
|
record = new CNAME_Record();
|
|
break;
|
|
}
|
|
case PTR:
|
|
{
|
|
record = new PTR_Record();
|
|
break;
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, record->parseFromBuffer(buf, offset+ret, maxlen));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
DNS::Answer& DNS::Answer::operator=(const Answer &r)
|
|
{
|
|
name = r.name;
|
|
record_type = r.record_type;
|
|
record_class = r.record_class;
|
|
ttl = r.ttl;
|
|
length = r.length;
|
|
record = r.record->clone();
|
|
return *this;
|
|
}
|
|
|
|
size_t DNS::Packet::parseFromBuffer(const u8 *buf, size_t maxlen)
|
|
{
|
|
if( !buf || maxlen < DATA) return 0;
|
|
|
|
size_t tmp, ret = 0;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(id, buf, ID, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(flags, buf, FLAGS_OFFSET, maxlen));
|
|
|
|
u16 queries_counter, answers_counter, authorities_counter, additionals_counter;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(queries_counter, buf, QDCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(answers_counter, buf, ANCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(authorities_counter, buf, NSCOUNT, maxlen));
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(additionals_counter, buf, ARCOUNT, maxlen));
|
|
|
|
queries.clear();
|
|
for(u16 i=0; i<queries_counter; ++i)
|
|
{
|
|
Query q;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, q.parseFromBuffer(buf, ret, maxlen));
|
|
queries.push_back(q);
|
|
}
|
|
|
|
answers.clear();
|
|
for(u16 i=0; i<answers_counter; ++i)
|
|
{
|
|
Answer a;
|
|
DNS_CHECK_ACCUMLATE(ret, tmp, a.parseFromBuffer(buf, ret, maxlen));
|
|
answers.push_back(a);
|
|
};
|
|
|
|
return ret;
|
|
}
|