diff -urNb old.nmap.rev/Makefile.in new.nmap.rev/Makefile.in --- old.nmap.rev/Makefile.in Thu Nov 10 04:04:43 2005 +++ new.nmap.rev/Makefile.in Thu Nov 10 04:04:44 2005 @@ -46,11 +46,11 @@ TARGETNMAPFE=@TARGETNMAPFE@ INSTALLNMAPFE=@INSTALLNMAPFE@ -export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc @COMPAT_SRCS@ +export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc nmap_dns.cc @COMPAT_SRCS@ -OBJS = main.o nmap.o targets.o tcpip.o nmap_error.o utils.o idle_scan.o osscan.o output.o scan_engine.o timing.o charpool.o services.o protocols.o nmap_rpc.o portlist.o NmapOps.o TargetGroup.o Target.o FingerPrintResults.o service_scan.o NmapOutputTable.o MACLookup.o @COMPAT_OBJS@ +OBJS = main.o nmap.o targets.o tcpip.o nmap_error.o utils.o idle_scan.o osscan.o output.o scan_engine.o timing.o charpool.o services.o protocols.o nmap_rpc.o portlist.o NmapOps.o TargetGroup.o Target.o FingerPrintResults.o service_scan.o NmapOutputTable.o MACLookup.o nmap_dns.o @COMPAT_OBJS@ -export DEPS = nmap.h nmap_amigaos.h nmap_error.h targets.h idle_scan.h osscan.h output.h scan_engine.h timing.h tcpip.h utils.h global_structures.h charpool.h services.h protocols.h nmap_rpc.h portlist.h NmapOps.h TargetGroup.h Target.h FingerPrintResults.h service_scan.h NmapOutputTable.h MACLookup.h +export DEPS = nmap.h nmap_amigaos.h nmap_error.h targets.h idle_scan.h osscan.h output.h scan_engine.h timing.h tcpip.h utils.h global_structures.h charpool.h services.h protocols.h nmap_rpc.h portlist.h NmapOps.h TargetGroup.h Target.h FingerPrintResults.h service_scan.h NmapOutputTable.h MACLookup.h nmap_dns.h # %.o : %.cc -- nope this is a GNU extension diff -urNb old.nmap.rev/NmapOps.cc new.nmap.rev/NmapOps.cc --- old.nmap.rev/NmapOps.cc Thu Nov 10 04:04:42 2005 +++ new.nmap.rev/NmapOps.cc Thu Nov 10 04:04:43 2005 @@ -246,6 +246,7 @@ if (xsl_stylesheet) free(xsl_stylesheet); xsl_stylesheet = strdup(tmpxsl); spoof_mac_set = false; + mass_dns = true; } bool NmapOps::TCPScan() { diff -urNb old.nmap.rev/NmapOps.h new.nmap.rev/NmapOps.h --- old.nmap.rev/NmapOps.h Thu Nov 10 04:04:43 2005 +++ new.nmap.rev/NmapOps.h Thu Nov 10 04:04:44 2005 @@ -280,6 +280,7 @@ FILE *nmap_stdout; /* Nmap standard output */ int ttl; // Time to live char *datadir; + bool mass_dns; private: int max_rtt_timeout; int min_rtt_timeout; diff -urNb old.nmap.rev/nmap.cc new.nmap.rev/nmap.cc --- old.nmap.rev/nmap.cc Thu Nov 10 04:04:42 2005 +++ new.nmap.rev/nmap.cc Thu Nov 10 04:04:43 2005 @@ -107,6 +107,7 @@ #include "timing.h" #include "NmapOps.h" #include "MACLookup.h" +#include "nmap_dns.h" #ifdef WIN32 #include "winfix.h" #endif @@ -299,6 +300,7 @@ {"version_intensity", required_argument, 0, 0}, {"version_light", no_argument, 0, 0}, {"version_all", no_argument, 0, 0}, + {"system_dns", no_argument, 0, 0}, {0, 0, 0, 0} }; @@ -443,6 +445,8 @@ 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, "oN") == 0) { normalfilename = optarg; } else if (strcmp(long_options[option_index].name, "oG") == 0 || @@ -1083,26 +1087,9 @@ 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); - printmacinfo(currenths); - // if (currenths->flags & HOST_UP) - // log_write(LOG_NORMAL|LOG_SKID|LOG_STDOUT,"\n"); - log_write(LOG_XML, "\n"); - log_flush_all(); - delete currenths; + /* We're done with the hosts except for reverse DNS lookup */ + Targets.push_back(currenths); continue; } @@ -1154,8 +1141,31 @@ Targets.push_back(currenths); } - if (Targets.size() == 0) + + if (Targets.size() > 0) { + + // Do the parallel reverse DNS and print out the host info + // if this is a ping/list scan + if (!o.noresolve) nmap_mass_rdns(Targets, resolve_all); + + if (o.pingscan || o.listscan) { + vector::iterator hI; + + for(hI = Targets.begin(); hI != Targets.end(); hI++) { + log_write(LOG_XML, ""); + write_host_status(*hI, resolve_all); + printmacinfo(*hI); + log_write(LOG_XML, "\n"); + log_flush_all(); + delete *hI; + } + + Targets.clear(); + continue; + } + } else { break; /* Couldn't find any more targets */ + } // Our source must be set in decoy list because nexthost() call can // change it (that issue really should be fixed when possible) diff -urNb old.nmap.rev/nmap.h new.nmap.rev/nmap.h --- old.nmap.rev/nmap.h Thu Nov 10 04:04:43 2005 +++ new.nmap.rev/nmap.h Thu Nov 10 04:04:44 2005 @@ -396,6 +396,8 @@ #define MAXHOSTNAMELEN 64 #endif +#define DNS_PARALLELISM 10 + #ifndef recvfrom6_t # define recvfrom6_t int #endif diff -urNb old.nmap.rev/nmap_dns.cc new.nmap.rev/nmap_dns.cc --- old.nmap.rev/nmap_dns.cc Wed Dec 31 16:00:00 1969 +++ new.nmap.rev/nmap_dns.cc Thu Nov 10 04:04:43 2005 @@ -0,0 +1,626 @@ +#include +#include +#include +#include + +#include "nmap.h" +#include "NmapOps.h" +#include "nmap_dns.h" +#include "nsock.h" +#include "utils.h" + +extern NmapOps o; + + +//------------------- Configuration Parameters --------------------- + +// The amount of time we wait for nsock_write() to complete before +// retransmission. This should almost never happen. (in milliseconds) +#define WRITE_TIMEOUT 100 + +// This is the default max number of DNS requests to let each +// server handle. +#define DEFAULT_CAPACITY 10 + +// These are the various retransmission intervals for each server +// before we move on to the next DNS server. (in milliseconds) +int read_timeouts[] = { 300, 500, 800, 2000 }; + + + +//------------------- Internal Structures --------------------- + +typedef struct dns_server_s dns_server; +typedef struct request_s request; + +struct dns_server_s { + char *ipaddr; + nsock_iod nsd; + 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; + dns_server *first_server; + dns_server *curr_server; +}; + + +//------------------- Globals --------------------- + +std::list servs; +int total_reqs; +nsock_pool dnspool=NULL; + + +//------------------- Prototypes --------------------- + +void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v); +void do_possible_writes(); + + + + +//------------------- Read handling code --------------------- + +// After processing a DNS response, we search through the IPs we're +// looking for and update their results as necessary. +void process_result(unsigned long 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; reqI++) { + for(reqI = tpserv->in_process.begin(); reqI != tpserv->in_process.end(); reqI++) { + tpreq = *reqI; + + if ((unsigned long) (tpreq->targ->v4host().s_addr) == ia) { + reqI--; + if (result) tpreq->targ->setHostName(result); + tpserv->in_process.remove(tpreq); + tpserv->reqs_on_wire--; + total_reqs--; + + delete tpreq; + do_possible_writes(); + return; + } + } + } +} + + +// 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 +unsigned long parse_inaddr_arpa(char *buf) { + unsigned long 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) 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 nxdomain=0; + int queries, answers; + + if (total_reqs > 1) + nsock_read(nsp, nse_iod(evt), read_evt_handler, -1, NULL); + + if (nse_status(evt) != NSE_STATUS_SUCCESS) 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) == 3) nxdomain = 1; + 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) we should have 1+ queries and + // 0 answers: + if (nxdomain) { + 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 (nxdomain) { + struct in_addr ia; + ia.s_addr = parse_inaddr_arpa(buf+nameloc); + + //printf("NXDOMAIN <%s>\n", inet_ntoa(ia)); + process_result(ia.s_addr, NULL); + } + } + + // No answers if NXDOMAIN + if (nxdomain) 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); + encoded_name_to_normal(buf+curbuf, outbuf, sizeof(outbuf)); + + //printf("MATCHED <%s> to <%s>\n", inet_ntoa(ia), outbuf); + process_result(ia.s_addr, outbuf); + } + + 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, unsigned long c) { + char tpnum[4]; + int tplen; + + sprintf(tpnum, "%ld", 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; + unsigned long ip; + struct timeval now, timeout; + + ip = (unsigned long) req->targ->v4host().s_addr; + + memcpy(packet, "\x99\x99\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00", 12); + plen = 12; + + 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[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; + + 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) { + reqI--; + tpserv->in_process.remove(tpreq); + tpserv->reqs_on_wire--; + + // If we've tried this server enough times, move to the next one + if (tpreq->tries >= (int) (sizeof(read_timeouts) / sizeof(int))) { + servItemp = servI; + servItemp++; + + if (servItemp == servs.end()) servItemp = servs.begin(); + + tpreq->curr_server = *servItemp; + tpreq->tries = 0; + + if (tpreq->curr_server == tpreq->first_server) { + // We've already tried all servers... give up + total_reqs--; + delete tpreq; + } else { + (*servItemp)->to_process.push_back(tpreq); + } + } else { + tpserv->to_process.push_back(tpreq); + } + + continue; + } + } + + } + + 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->to_process.empty() && tpserv->write_busy == 0 && tpserv->reqs_on_wire < tpserv->capacity) { + tpreq = tpserv->to_process.front(); + tpserv->to_process.pop_front(); + 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 a DNS server to the dns_server list - Doesn't actually +// do any connecting! +void add_dns_server(char *ipaddr) { + dns_server *s; + + s = new dns_server; + + s->ipaddr = strdup(ipaddr); + + servs.push_front(s); +} + + +// 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 = DEFAULT_CAPACITY; + 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); + } + +} + + +// Closes all nsis created in connect_dns_servers() +void close_dns_servers() { + std::list::iterator serverI; + + for(serverI = servs.begin(); serverI != servs.end(); serverI++) { + nsi_delete((*serverI)->nsd, NSOCK_PENDING_SILENT); + (*serverI)->to_process.clear(); + (*serverI)->in_process.clear(); + } + +} + + +// Parses /etc/resolv.conf and adds all "nameserver"s with the +// add_dns_server() function +void parse_resolvdotconf() { + 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"); + } + + while (fgets(buf, sizeof(buf), fp)) { + tp = buf; + + // Clip off comments #, \r, \n + while (*tp != '\r' && *tp != '\n' && *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); +} + + + + +//------------------- Main loops --------------------- + + +// Actual main loop +void nmap_mass_rdns_core(std::vector Targets, int resolve_all) { + + std::vector::iterator hostI; + std::list::iterator serverI; + request *tpreq; + int timeout; + + if (o.mass_dns == false) { + Target *currenths; + struct sockaddr_storage ss; + size_t sslen; + char hostname[MAXHOSTNAMELEN + 1] = ""; + + for(hostI = Targets.begin(); hostI != Targets.end(); hostI++) { + currenths = *hostI; + + 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); + } + } + } + + return; + } + + // If necessary, set up the dns server list from resolv.conf + // ... + if (servs.size() == 0) parse_resolvdotconf(); + + + // If necessary, set up the /etc/hosts hashtable + // ... + + + + total_reqs = 0; + + // Set up the request structure + serverI = servs.begin(); + for(hostI = Targets.begin(); hostI != Targets.end(); hostI++) { + if (!((*hostI)->flags & HOST_UP) && !resolve_all) continue; + + tpreq = new request; + tpreq->targ = *hostI; + tpreq->tries = 0; + + tpreq->first_server = tpreq->curr_server = *serverI; + (*serverI)->to_process.push_back(tpreq); + + serverI++; + if (serverI == servs.end()) serverI = servs.begin(); + + total_reqs++; + } + + if (total_reqs == 0) return; + + // And finally, do it! + + //FIXME: error check + if ((dnspool = nsp_new(NULL)) == NULL) exit(1); + + connect_dns_servers(); + + 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 +void nmap_mass_rdns(std::vector Targets, int resolve_all) { + + struct timeval starttv, now; + + gettimeofday(&starttv, NULL); + + nmap_mass_rdns_core(Targets, resolve_all); + + gettimeofday(&now, NULL); + + if (o.verbose) { + log_write(LOG_STDOUT, "DNS resolution of %d IPs took %.2fs. Mode: %s\n", + Targets.size(), + TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0, + o.mass_dns ? "Async" : "System"); + } + +} diff -urNb old.nmap.rev/nmap_dns.h new.nmap.rev/nmap_dns.h --- old.nmap.rev/nmap_dns.h Wed Dec 31 16:00:00 1969 +++ new.nmap.rev/nmap_dns.h Thu Nov 10 04:04:44 2005 @@ -0,0 +1,4 @@ +#include "Target.h" +#include + +void nmap_mass_rdns(std::vector Targets, int resolve_all);