From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- scan_engine.cc | 2855 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2855 insertions(+) create mode 100644 scan_engine.cc (limited to 'scan_engine.cc') diff --git a/scan_engine.cc b/scan_engine.cc new file mode 100644 index 0000000..f779cf9 --- /dev/null +++ b/scan_engine.cc @@ -0,0 +1,2855 @@ + +/*************************************************************************** + * scan_engine.cc -- Includes much of the "engine" functions for scanning, * + * such as ultra_scan. It also includes dependent functions such as those * + * for collecting SYN/connect scan responses. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * + * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap + * Project"). Nmap is also a registered trademark of the Nmap Project. + * + * This program is distributed under the terms of the Nmap Public Source + * License (NPSL). The exact license text applying to a particular Nmap + * release or source code control revision is contained in the LICENSE + * file distributed with that version of Nmap or source code control + * revision. More Nmap copyright/legal information is available from + * https://nmap.org/book/man-legal.html, and further information on the + * NPSL license itself can be found at https://nmap.org/npsl/ . This + * header summarizes some key points from the Nmap license, but is no + * substitute for the actual license text. + * + * Nmap is generally free for end users to download and use themselves, + * including commercial use. It is available from https://nmap.org. + * + * The Nmap license generally prohibits companies from using and + * redistributing Nmap in commercial products, but we sell a special Nmap + * OEM Edition with a more permissive license and special features for + * this purpose. See https://nmap.org/oem/ + * + * If you have received a written Nmap license agreement or contract + * stating terms other than these (such as an Nmap OEM license), you may + * choose to use and redistribute Nmap under those terms instead. + * + * The official Nmap Windows builds include the Npcap software + * (https://npcap.com) for packet capture and transmission. It is under + * separate license terms which forbid redistribution without special + * permission. So the official Nmap Windows builds may not be redistributed + * without special permission (such as an Nmap OEM license). + * + * 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 submit your changes as a Github PR + * or by email to the dev@nmap.org mailing list for possible incorporation into + * the main distribution. Unless you specify otherwise, it is understood that + * you are offering us very broad rights to use your submissions as described in + * the Nmap Public Source License Contributor Agreement. This is important + * because we fund the project by selling licenses with various terms, and also + * because the inability to relicense code has caused devastating problems for + * other Free Software projects (such as KDE and NASM). + * + * The free version of Nmap 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. Warranties, + * indemnification and commercial support are all available through the + * Npcap OEM program--see https://nmap.org/oem/ + * + ***************************************************************************/ + +/* $Id$ */ + +#ifdef WIN32 +#include "nmap_winconfig.h" +#endif +#include "portreasons.h" +#include +#include "scan_engine.h" +#include "scan_engine_connect.h" +#include "scan_engine_raw.h" +#include "timing.h" +#include "tcpip.h" +#include "NmapOps.h" +#include "nmap_tty.h" +#include "payload.h" +#include "Target.h" +#include "targets.h" +#include "utils.h" +#include "nmap_error.h" +#include "output.h" + +#include "struct_ip.h" + +#ifndef IPPROTO_SCTP +#include "libnetutil/netutil.h" +#endif + +#include +#include +#include + +extern NmapOps o; +#ifdef WIN32 +/* from libdnet's intf-win32.c */ +extern "C" int g_has_npcap_loopback; +#endif + +/* How long extra to wait before retransmitting for rate-limit detection */ +#define RLD_TIME_MS 1000 +/* Keep a completed host around for a standard TCP MSL (2 min) */ +#define COMPL_HOST_LIFETIME_MS 120000 + +int HssPredicate::operator() (const HostScanStats *lhs, const HostScanStats *rhs) const { + const struct sockaddr_storage *lss, *rss; + lss = (lhs) ? lhs->target->TargetSockAddr() : ss; + rss = (rhs) ? rhs->target->TargetSockAddr() : ss; + return 0 > sockaddr_storage_cmp(lss, rss); +} +struct sockaddr_storage *HssPredicate::ss = NULL; + +void UltraScanInfo::log_overall_rates(int logt) const { + log_write(logt, "Overall sending rates: %.2f packets / s", send_rate_meter.getOverallPacketRate(&now)); + if (send_rate_meter.getNumBytes() > 0) + log_write(logt, ", %.2f bytes / s", send_rate_meter.getOverallByteRate(&now)); + log_write(logt, ".\n"); +} + +void UltraScanInfo::log_current_rates(int logt, bool update) { + log_write(logt, "Current sending rates: %.2f packets / s", send_rate_meter.getCurrentPacketRate(&now, update)); + if (send_rate_meter.getNumBytes() > 0) + log_write(logt, ", %.2f bytes / s", send_rate_meter.getCurrentByteRate(&now)); + log_write(logt, ".\n"); +} + +void ultra_scan_performance_vars::init() { + scan_performance_vars::init(); + ping_magnifier = 3; + pingtime = 1250000; + tryno_cap = o.getMaxRetransmissions(); +} + +const char *pspectype2ascii(int type) { + switch (type) { + case PS_NONE: + return "NONE"; + case PS_TCP: + return "TCP"; + case PS_UDP: + return "UDP"; + case PS_SCTP: + return "SCTP"; + case PS_PROTO: + return "IP Proto"; + case PS_ICMP: + return "ICMP"; + case PS_ARP: + return "ARP"; + case PS_ICMPV6: + return "ICMPv6"; + case PS_ND: + return "ND"; + case PS_CONNECTTCP: + return "connect"; + default: + fatal("%s: Unknown type: %d", __func__, type); + } + return ""; // Unreached +} + +/* Initialize the ultra_timing_vals structure timing. The utt must be + TIMING_HOST or TIMING_GROUP. If you happen to have the current + time handy, pass it as now, otherwise pass NULL */ +static void init_ultra_timing_vals(ultra_timing_vals *timing, + enum ultra_timing_type utt, + int num_hosts_in_group, + struct ultra_scan_performance_vars *perf, + struct timeval *now); + +/* Take a buffer, buf, of size bufsz (64 bytes is sufficient) and + writes a short description of the probe (arg1) into buf. It also returns + buf. */ +static char *probespec2ascii(const probespec *pspec, char *buf, unsigned int bufsz) { + char flagbuf[32]; + char *f; + switch (pspec->type) { + case PS_TCP: + if (!pspec->pd.tcp.flags) { + Strncpy(flagbuf, "(none)", sizeof(flagbuf)); + } else { + f = flagbuf; + if (pspec->pd.tcp.flags & TH_SYN) + *f++ = 'S'; + if (pspec->pd.tcp.flags & TH_FIN) + *f++ = 'F'; + if (pspec->pd.tcp.flags & TH_RST) + *f++ = 'R'; + if (pspec->pd.tcp.flags & TH_PUSH) + *f++ = 'P'; + if (pspec->pd.tcp.flags & TH_ACK) + *f++ = 'A'; + if (pspec->pd.tcp.flags & TH_URG) + *f++ = 'U'; + if (pspec->pd.tcp.flags & TH_ECE) + *f++ = 'E'; /* rfc 2481/3168 */ + if (pspec->pd.tcp.flags & TH_CWR) + *f++ = 'C'; /* rfc 2481/3168 */ + *f++ = '\0'; + } + Snprintf(buf, bufsz, "tcp to port %hu; flags: %s", pspec->pd.tcp.dport, flagbuf); + break; + case PS_UDP: + Snprintf(buf, bufsz, "udp to port %hu", pspec->pd.udp.dport); + break; + case PS_SCTP: + switch (pspec->pd.sctp.chunktype) { + case SCTP_INIT: + Strncpy(flagbuf, "INIT", sizeof(flagbuf)); + break; + case SCTP_COOKIE_ECHO: + Strncpy(flagbuf, "COOKIE-ECHO", sizeof(flagbuf)); + break; + default: + Strncpy(flagbuf, "(unknown)", sizeof(flagbuf)); + } + Snprintf(buf, bufsz, "sctp to port %hu; chunk: %s", pspec->pd.sctp.dport, + flagbuf); + break; + case PS_PROTO: + Snprintf(buf, bufsz, "protocol %u", (unsigned int) pspec->proto); + break; + case PS_ICMP: + Snprintf(buf, bufsz, "icmp type %d code %d", + pspec->pd.icmp.type, pspec->pd.icmp.code); + break; + case PS_ARP: + Snprintf(buf, bufsz, "ARP"); + break; + case PS_ICMPV6: + Snprintf(buf, bufsz, "icmpv6 type %d code %d", + pspec->pd.icmpv6.type, pspec->pd.icmpv6.code); + break; + case PS_ND: + Snprintf(buf, bufsz, "ND"); + break; + case PS_CONNECTTCP: + Snprintf(buf, bufsz, "connect to port %hu", pspec->pd.tcp.dport); + break; + default: + fatal("Unexpected %s type encountered", __func__); + break; + } + return buf; +} + +UltraProbe::UltraProbe() { + type = UP_UNSET; + tryno.opaque = 0; + timedout = false; + retransmitted = false; + mypspec.type = PS_NONE; + memset(&sent, 0, sizeof(prevSent)); + memset(&prevSent, 0, sizeof(prevSent)); +} + +UltraProbe::~UltraProbe() { + if (type == UP_CONNECT) + delete probes.CP; +} + +GroupScanStats::GroupScanStats(UltraScanInfo *UltraSI) { + memset(&latestip, 0, sizeof(latestip)); + memset(&timeout, 0, sizeof(timeout)); + USI = UltraSI; + init_ultra_timing_vals(&timing, TIMING_GROUP, USI->numIncompleteHosts(), &(USI->perf), &USI->now); + initialize_timeout_info(&to); + /* Default timout should be much lower for arp */ + if (USI->ping_scan_arp) + to.timeout = MAX(o.minRttTimeout(), MIN(o.initialRttTimeout(), INITIAL_ARP_RTT_TIMEOUT)) * 1000; + num_probes_active = 0; + numtargets = USI->numIncompleteHosts(); // They are all incomplete at the beginning + numprobes = USI->numProbesPerHost(); + + if (USI->scantype == CONNECT_SCAN || USI->ptech.connecttcpscan) + CSI = new ConnectScanInfo; + else CSI = NULL; + probes_sent = probes_sent_at_last_wait = 0; + lastping_sent = lastrcvd = USI->now; + send_no_earlier_than = USI->now; + send_no_later_than = USI->now; + lastping_sent_numprobes = 0; + pinghost = NULL; + gettimeofday(&last_wait, NULL); + num_hosts_timedout = 0; +} + +GroupScanStats::~GroupScanStats() { + delete CSI; +} + +/* Called whenever a probe is sent to any host. Should only be called by + HostScanStats::probeSent. */ +void GroupScanStats::probeSent(unsigned int nbytes) { + USI->send_rate_meter.update(nbytes, &USI->now); + + /* Find a new scheduling interval for minimum- and maximum-rate sending. + Recall that these have effect only when --min-rate or --max-rate is + given. */ + + if (o.max_packet_send_rate != 0.0) + TIMEVAL_ADD(send_no_earlier_than, send_no_earlier_than, + (time_t) (1000000.0 / o.max_packet_send_rate)); + /* Allow send_no_earlier_than to slip into the past. This allows the sending + scheduler to catch up and make up for delays in other parts of the scan + engine. If we were to update send_no_earlier_than to the present the + sending rate could be much less than the maximum requested, even if the + connection is capable of the maximum. */ + + if (o.min_packet_send_rate != 0.0) { + if (TIMEVAL_SUBTRACT(send_no_later_than, USI->now) > 0) { + /* The next scheduled send is in the future. That means there's slack time + during which the sending rate could drop. Pull the time back to the + present to prevent that. */ + send_no_later_than = USI->now; + } + TIMEVAL_ADD(send_no_later_than, send_no_later_than, + (time_t) (1000000.0 / o.min_packet_send_rate)); + } +} + +/* Returns true if the GLOBAL system says that sending is OK.*/ +bool GroupScanStats::sendOK(struct timeval *when) const { + int recentsends; + + /* In case it's not okay to send, arbitrarily say to check back in one + second. */ + if (when) + TIMEVAL_MSEC_ADD(*when, USI->now, 1000); + + if (CSI && !CSI->sendOK()) + return false; + + /* We need to stop sending if it has been a long time since + the last listen call, at least for systems such as Windows that + don't give us a proper pcap time. Also for connect scans, since + we don't get an exact response time with them either. */ + recentsends = USI->gstats->probes_sent - USI->gstats->probes_sent_at_last_wait; + if (recentsends > 0 && + (USI->scantype == CONNECT_SCAN || USI->ptech.connecttcpscan || !pcap_recv_timeval_valid())) { + int to_ms = (int) MAX(to.srtt * .75 / 1000, 50); + if (TIMEVAL_MSEC_SUBTRACT(USI->now, last_wait) > to_ms) + return false; + } + + /* Enforce a maximum scanning rate, if necessary. If it's too early to send, + return false. If not, mark now as a good time to send and allow the + congestion control to override it. */ + if (o.max_packet_send_rate != 0.0) { + if (TIMEVAL_SUBTRACT(send_no_earlier_than, USI->now) > 0) { + if (when) + *when = send_no_earlier_than; + return false; + } else { + if (when) + *when = USI->now; + } + } + + /* Enforce a minimum scanning rate, if necessary. If we're ahead of schedule, + record the time of the next scheduled send and submit to congestion + control. If we're behind schedule, return true to indicate that we need to + send right now. */ + if (o.min_packet_send_rate != 0.0) { + if (TIMEVAL_SUBTRACT(send_no_later_than, USI->now) > 0) { + if (when) + *when = send_no_later_than; + } else { + if (when) + *when = USI->now; + return true; + } + } + + /* There are good arguments for limiting the number of probes sent + between waits even when we do get appropriate receive times. For + example, overflowing the pcap receive buffer with responses is no + fun. On one of my Linux boxes, it seems to hold about 113 + responses when I scan localhost. And half of those are the @#$# + sends being received. I think I'll put a limit of 50 sends per + wait */ + if (recentsends >= 50) + return false; + + /* In case the user specifically asked for no group congestion control */ + if (o.nogcc) { + if (when) + *when = USI->now; + return true; + } + + /* When there is only one target left, let the host congestion + stuff deal with it. */ + if (USI->numIncompleteHostsLessThan(2)) { + if (when) + *when = USI->now; + return true; + } + + if (timing.cwnd >= num_probes_active + 0.5) { + if (when) + *when = USI->now; + return true; + } + + return false; +} + +/* Return true if pingprobe is an appropriate ping probe for the currently + running scan. Because ping probes persist between host discovery and port + scanning stages, it's possible to have a ping probe that is not relevant for + the scan type, or won't be caught by the pcap filters. Examples of + inappropriate ping probes are an ARP ping for a TCP scan, or a raw SYN ping + for a connect scan. */ +static bool pingprobe_is_appropriate(const UltraScanInfo *USI, + const probespec *pingprobe) { + switch (pingprobe->type) { + case(PS_NONE): + return true; + case(PS_CONNECTTCP): + return USI->scantype == CONNECT_SCAN || (USI->ping_scan && USI->ptech.connecttcpscan); + case(PS_TCP): + case(PS_UDP): + case(PS_SCTP): + return (USI->tcp_scan && USI->scantype != CONNECT_SCAN) || + USI->udp_scan || + USI->sctp_scan || + (USI->ping_scan && (USI->ptech.rawtcpscan || USI->ptech.rawudpscan || USI->ptech.rawsctpscan)); + case(PS_PROTO): + return USI->prot_scan || (USI->ping_scan && USI->ptech.rawprotoscan); + case(PS_ICMP): + return ((USI->ping_scan && !USI->ping_scan_arp ) || pingprobe->pd.icmp.type == 3); + case(PS_ARP): + return USI->ping_scan_arp; + case(PS_ND): + return USI->ping_scan_nd; + } + return false; +} + +HostScanStats::HostScanStats(Target *t, UltraScanInfo *UltraSI) { + target = t; + USI = UltraSI; + next_portidx = 0; + sent_arp = false; + next_ackportpingidx = 0; + next_synportpingidx = 0; + next_udpportpingidx = 0; + next_sctpportpingidx = 0; + next_protoportpingidx = 0; + sent_icmp_ping = false; + sent_icmp_mask = false; + sent_icmp_ts = false; + retry_capped_warned = false; + num_probes_active = 0; + num_probes_waiting_retransmit = 0; + lastping_sent = lastprobe_sent = lastrcvd = USI->now; + lastping_sent_numprobes = 0; + nxtpseq = 1; + max_successful_tryno = 0; + ports_finished = 0; + numprobes_sent = 0; + memset(&completiontime, 0, sizeof(completiontime)); + init_ultra_timing_vals(&timing, TIMING_HOST, 1, &(USI->perf), &USI->now); + bench_tryno = 0; + memset(&sdn, 0, sizeof(sdn)); + sdn.last_boost = USI->now; + sdn.delayms = o.scan_delay; + rld.max_tryno_sent = 0; + rld.rld_waiting = false; + rld.rld_waittime = USI->now; + if (!pingprobe_is_appropriate(USI, &target->pingprobe)) { + if (o.debugging > 1) + log_write(LOG_STDOUT, "%s pingprobe type %s is inappropriate for this scan type; resetting.\n", target->targetipstr(), pspectype2ascii(target->pingprobe.type)); + memset(&target->pingprobe, 0, sizeof(target->pingprobe)); + target->pingprobe_state = PORT_UNKNOWN; + } +} + +HostScanStats::~HostScanStats() { + std::list::iterator probeI, next; + + /* Move any hosts from the bench to probes_outstanding for easier deletion */ + for (probeI = probes_outstanding.begin(); probeI != probes_outstanding.end(); + probeI = next) { + next = probeI; + next++; + destroyOutstandingProbe(probeI); + } +} + +/* Called whenever a probe is sent to this host. Takes care of updating scan + delay and rate limiting variables. */ +void HostScanStats::probeSent(unsigned int nbytes) { + lastprobe_sent = USI->now; + + /* Update group variables. */ + USI->gstats->probeSent(nbytes); +} + +/* How long I am currently willing to wait for a probe response before + considering it timed out. Uses the host values from target if they + are available, otherwise from gstats. Results returned in + MICROseconds. */ +unsigned long HostScanStats::probeTimeout() const { + if (target->to.srtt > 0) { + /* We have at least one timing value to use. Good enough, I suppose */ + return target->to.timeout; + } else if (USI->gstats->to.srtt > 0) { + /* OK, we'll use this one instead */ + return USI->gstats->to.timeout; + } else { + return target->to.timeout; /* It comes with a default */ + } +} + +/* How long I'll wait until completely giving up on a probe. + Timedout probes are often marked as such (and sometimes + considered a drop), but kept in the list just in case they come + really late. But after probeExpireTime(), I don't waste time + keeping them around. Give in MICROseconds. The expiry time can + depend on the type of probe. Pass NULL to get the default time. */ +unsigned long HostScanStats::probeExpireTime(const UltraProbe *probe) const { + if (probe == NULL || probe->type == UltraProbe::UP_CONNECT) + /* timedout probes close socket -- late resp. impossible */ + return probeTimeout(); + else + /* Wait a bit longer after probeTimeout. */ + return MIN(10000000, probeTimeout() * 10); +} + +/* Returns OK if sending a new probe to this host is OK (to avoid + flooding). If when is non-NULL, fills it with the time that sending + will be OK assuming no pending probes are resolved by responses + (call it again if they do). when will become now if it returns + true. */ +bool HostScanStats::sendOK(struct timeval *when) const { + struct ultra_timing_vals tmng; + std::list::const_iterator probeI; + struct timeval probe_to, earliest_to, sendTime; + long tdiff; + + if ((!USI->ping_scan && target->timedOut(&USI->now)) || completed()) { + if (when) + *when = USI->now; + return false; + } + + /* If the group stats say we need to send a probe to enforce a minimum + scanning rate, then we need to step up and send a probe. */ + if (o.min_packet_send_rate != 0.0) { + if (TIMEVAL_SUBTRACT(USI->gstats->send_no_later_than, USI->now) <= 0) { + if (when) + *when = USI->now; + return true; + } + } + + if (rld.rld_waiting) { + if (TIMEVAL_AFTER(rld.rld_waittime, USI->now)) { + if (when) + *when = rld.rld_waittime; + return false; + } else { + if (when) + *when = USI->now; + return true; + } + } + + if (sdn.delayms) { + if (TIMEVAL_MSEC_SUBTRACT(USI->now, lastprobe_sent) < (int) sdn.delayms) { + if (when) { + TIMEVAL_MSEC_ADD(*when, lastprobe_sent, sdn.delayms); + } + return false; + } + } + + getTiming(&tmng); + if (tmng.cwnd >= num_probes_active + .5 && + (freshPortsLeft() || num_probes_waiting_retransmit || !retry_stack.empty())) { + if (when) + *when = USI->now; + return true; + } + + if (!when) + return false; + + TIMEVAL_MSEC_ADD(earliest_to, USI->now, 10000); + + // Any timeouts coming up? + for (probeI = probes_outstanding.begin(); probeI != probes_outstanding.end(); + probeI++) { + if (!(*probeI)->timedout) { + TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, probeTimeout() / 1000); + if (TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) { + earliest_to = probe_to; + } + } + } + + // Will any scan delay affect this? + if (sdn.delayms) { + TIMEVAL_MSEC_ADD(sendTime, lastprobe_sent, sdn.delayms); + if (TIMEVAL_BEFORE(sendTime, USI->now)) + sendTime = USI->now; + tdiff = TIMEVAL_MSEC_SUBTRACT(earliest_to, sendTime); + + /* Timeouts previous to the sendTime requirement are pointless, + and those later than sendTime are not needed if we can send a + new packet at sendTime */ + if (tdiff < 0) { + earliest_to = sendTime; + } else { + getTiming(&tmng); + if (tdiff > 0 && tmng.cwnd > num_probes_active + .5) { + earliest_to = sendTime; + } + } + } + + *when = earliest_to; + return false; +} + +/* If there are pending probe timeouts, fills in when with the time of + the earliest one and returns true. Otherwise returns false and + puts now in when. */ +bool HostScanStats::nextTimeout(struct timeval *when) const { + struct timeval earliest_to = USI->now; + std::list::const_iterator probeI; + bool pending_probes = false; + + assert(when); + + /* For any given invocation, the probe timeout is the same for all probes, so + * we can get the earliest-sent probe and then add the timeout to that. + */ + for (probeI = probes_outstanding.begin(); probeI != probes_outstanding.end(); + probeI++) { + UltraProbe *probe = *probeI; + if (!probe->timedout) { + pending_probes = true; + if (TIMEVAL_BEFORE(probe->sent, earliest_to)) { + earliest_to = probe->sent; + } + } + } + if (pending_probes) { + TIMEVAL_ADD(*when, earliest_to, probeTimeout()); + } + else { + *when = USI->now; + } + return pending_probes; +} + +/* gives the maximum try number (try numbers start at zero and + increments for each retransmission) that may be used, based on + the scan type, observed network reliability, timing mode, etc. + This may change during the scan based on network traffic. If + capped is not null, it will be filled with true if the tryno is + at its upper limit. That often calls for a warning to be issued, + and marking of remaining timedout ports firewalled or whatever is + appropriate. If mayincrease is non-NULL, it is set to whether + the allowedTryno may increase again. If it is false, any probes + which have reached the given limit may be dealt with. */ +unsigned int HostScanStats::allowedTryno(bool *capped, bool *mayincrease) const { + std::list::const_iterator probeI; + UltraProbe *probe = NULL; + bool allfinished = true; + bool tryno_mayincrease = true; + unsigned int maxval = 0; + + /* TODO: This should perhaps differ by scan type. */ + maxval = MAX(1, max_successful_tryno + 1); + if (maxval > USI->perf.tryno_cap) { + if (capped) + *capped = true; + maxval = USI->perf.tryno_cap; + tryno_mayincrease = false; /* It never exceeds the cap */ + } else if (capped) *capped = false; + + /* Decide if the tryno can possibly increase. */ + if (tryno_mayincrease && num_probes_active == 0 && !freshPortsLeft()) { + /* If every outstanding probe is timedout and at maxval, then no further + retransmits are necessary. */ + for (probeI = probes_outstanding.begin(); + probeI != probes_outstanding.end(); probeI++) { + probe = *probeI; + assert(probe->timedout); + if (!probe->retransmitted && !probe->isPing() && probe->get_tryno() < maxval) { + /* Needs at least one more retransmit. */ + allfinished = false; + break; + } + } + if (allfinished) + tryno_mayincrease = false; + } + + if (mayincrease) + *mayincrease = tryno_mayincrease; + + return maxval; +} + + +UltraScanInfo::UltraScanInfo() { +} + +UltraScanInfo::~UltraScanInfo() { + std::multiset::iterator hostI; + + for (hostI = incompleteHosts.begin(); hostI != incompleteHosts.end(); hostI++) { + delete *hostI; + } + + for (hostI = completedHosts.begin(); hostI != completedHosts.end(); hostI++) { + delete *hostI; + } + + incompleteHosts.clear(); + completedHosts.clear(); + + delete gstats; + delete SPM; + if (rawsd >= 0) { + close(rawsd); + rawsd = -1; + } + if (pd) { + pcap_close(pd); + pd = NULL; + } + if (ethsd) { + ethsd = NULL; /* NO need to eth_close it due to caching */ + } +} + +/* Returns true if this scan is a "raw" scan. A raw scan is ont that requires a + raw socket or ethernet handle to send, or a pcap sniffer to receive. + Basically, any scan type except pure TCP connect scans are raw. */ +bool UltraScanInfo::isRawScan() const { + return scantype != CONNECT_SCAN + && (tcp_scan || udp_scan || sctp_scan || prot_scan || ping_scan_arp || ping_scan_nd + || (ping_scan && (ptech.rawicmpscan || ptech.rawtcpscan || ptech.rawudpscan + || ptech.rawsctpscan || ptech.rawprotoscan))); +} + +/* A circular buffer of the incompleteHosts. nextIncompleteHost() gives + the next one. The first time it is called, it will give the + first host in the list. If incompleteHosts is empty, returns + NULL. */ +HostScanStats *UltraScanInfo::nextIncompleteHost() { + HostScanStats *nxt; + + if (incompleteHosts.empty()) + return NULL; + + nxt = *nextI; + nextI++; + if (nextI == incompleteHosts.end()) + nextI = incompleteHosts.begin(); + + return nxt; +} + +/* Return a number between 0.0 and 1.0 inclusive indicating how much of the scan + is done. */ +double UltraScanInfo::getCompletionFraction() const { + std::multiset::const_iterator hostI; + double total; + + /* Add 1 for each completed host. */ + total = gstats->numtargets - numIncompleteHosts(); + /* Get the completion fraction for each incomplete host. */ + for (hostI = incompleteHosts.begin(); hostI != incompleteHosts.end(); hostI++) { + const HostScanStats *host = *hostI; + int maxtries = host->allowedTryno(NULL, NULL) + 1; + double thishostpercdone; + + // This is inexact (maxtries - 1) because numprobes_sent includes + // at least one try of ports_finished. + thishostpercdone = host->ports_finished * (maxtries - 1) + host->numprobes_sent; + thishostpercdone /= maxtries * gstats->numprobes; + if (thishostpercdone >= 0.9999) + thishostpercdone = 0.9999; + total += thishostpercdone; + } + + return total / gstats->numtargets; +} + +/* Initialize the state for ports that don't receive a response in all the + targets. */ +static void set_default_port_state(std::vector &targets, stype scantype) { + std::vector::iterator target; + + for (target = targets.begin(); target != targets.end(); target++) { + switch (scantype) { + case SYN_SCAN: + case ACK_SCAN: + case WINDOW_SCAN: + case CONNECT_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_TCP, PORT_FILTERED); + break; + case SCTP_INIT_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_SCTP, PORT_FILTERED); + break; + case NULL_SCAN: + case FIN_SCAN: + case MAIMON_SCAN: + case XMAS_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_TCP, PORT_OPENFILTERED); + break; + case UDP_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_UDP, + o.defeat_icmp_ratelimit ? PORT_CLOSEDFILTERED : PORT_OPENFILTERED); + break; + case IPPROT_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_IP, PORT_OPENFILTERED); + break; + case SCTP_COOKIE_ECHO_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_SCTP, PORT_OPENFILTERED); + break; + case PING_SCAN: + case PING_SCAN_ARP: + case PING_SCAN_ND: + break; + default: + fatal("Unexpected scan type found in %s()", __func__); + } + } +} + +/* Order of initializations in this function CAN BE IMPORTANT, so be careful + mucking with it. */ +void UltraScanInfo::Init(std::vector &Targets, const struct scan_lists *pts, stype scantp) { + unsigned int targetno = 0; + HostScanStats *hss; + int num_timedout = 0; + + gettimeofday(&now, NULL); + + ports = pts; + + seqmask = get_random_u32(); + scantype = scantp; + SPM = new ScanProgressMeter(scantype2str(scantype)); + send_rate_meter.start(&now); + tcp_scan = udp_scan = sctp_scan = prot_scan = false; + ping_scan = noresp_open_scan = ping_scan_arp = ping_scan_nd = false; + memset((char *) &ptech, 0, sizeof(ptech)); + perf.init(); + switch (scantype) { + case FIN_SCAN: + case XMAS_SCAN: + case MAIMON_SCAN: + case NULL_SCAN: + noresp_open_scan = true; + case ACK_SCAN: + case CONNECT_SCAN: + case SYN_SCAN: + case WINDOW_SCAN: + tcp_scan = true; + break; + case UDP_SCAN: + noresp_open_scan = true; + udp_scan = true; + break; + case SCTP_INIT_SCAN: + case SCTP_COOKIE_ECHO_SCAN: + sctp_scan = true; + break; + case IPPROT_SCAN: + noresp_open_scan = true; + prot_scan = true; + break; + case PING_SCAN: + ping_scan = true; + /* What kind of pings are we doing? */ + if (o.pingtype & (PINGTYPE_ICMP_PING | PINGTYPE_ICMP_MASK | PINGTYPE_ICMP_TS)) + ptech.rawicmpscan = 1; + if (o.pingtype & PINGTYPE_UDP) + ptech.rawudpscan = 1; + if (o.pingtype & PINGTYPE_SCTP_INIT) + ptech.rawsctpscan = 1; + if (o.pingtype & PINGTYPE_TCP) { + if (o.isr00t) + ptech.rawtcpscan = 1; + else + ptech.connecttcpscan = 1; + } + if (o.pingtype & PINGTYPE_PROTO) + ptech.rawprotoscan = 1; + if (o.pingtype & PINGTYPE_CONNECTTCP) + ptech.connecttcpscan = 1; + break; + case PING_SCAN_ARP: + ping_scan = true; + ping_scan_arp = true; + /* For ARP and ND scan, we send pings more frequently. Otherwise we can't + * notice drops until we start sending retransmits after RLD_TIME_MS. */ + perf.pingtime = RLD_TIME_MS * 1000 / 4; + break; + case PING_SCAN_ND: + ping_scan = true; + ping_scan_nd = true; + perf.pingtime = RLD_TIME_MS * 1000 / 4; + break; + default: + break; + } + + set_default_port_state(Targets, scantype); + + memset(&lastCompletedHostRemoval, 0, sizeof(lastCompletedHostRemoval)); + + for (targetno = 0; targetno < Targets.size(); targetno++) { + if (Targets[targetno]->timedOut(&now)) { + num_timedout++; + continue; + } + + hss = new HostScanStats(Targets[targetno], this); + incompleteHosts.insert(hss); + } + numInitialTargets = Targets.size(); + nextI = incompleteHosts.begin(); + + gstats = new GroupScanStats(this); /* Peeks at several elements in USI - careful of order */ + gstats->num_hosts_timedout += num_timedout; + + pd = NULL; + rawsd = -1; + ethsd = NULL; + + /* See if we need an ethernet handle or raw socket. Basically, it's if we + aren't doing a TCP connect scan, or if we're doing a ping scan that + requires it. */ + if (isRawScan()) { + if (ping_scan_arp || (ping_scan_nd && o.sendpref != PACKET_SEND_IP_STRONG) || ((o.sendpref & PACKET_SEND_ETH) && + (Targets[0]->ifType() == devt_ethernet +#ifdef WIN32 + || (g_has_npcap_loopback && Targets[0]->ifType() == devt_loopback) +#endif + ))) { + /* We'll send ethernet packets with dnet */ + ethsd = eth_open_cached(Targets[0]->deviceName()); + if (ethsd == NULL) + fatal("dnet: Failed to open device %s", Targets[0]->deviceName()); + rawsd = -1; + } else { +#ifdef WIN32 + win32_fatal_raw_sockets(Targets[0]->deviceName()); +#endif + rawsd = nmap_raw_socket(); + if (rawsd < 0) + pfatal("Couldn't open a raw socket. " +#if defined(sun) && defined(__SVR4) + "In Solaris shared-IP non-global zones, this requires the PRIV_NET_RAWACCESS privilege. " +#endif + "Error" + ); + /* We do not want to unblock the socket since we want to wait + if kernel send buffers fill up rather than get ENOBUF, and + we won't be receiving on the socket anyway + unblock_socket(rawsd);*/ + ethsd = NULL; + } + /* Raw scan types also need to know the source IP. */ + Targets[0]->SourceSockAddr(&sourceSockAddr, NULL); + } + base_port = UltraScanInfo::increment_base_port(); +} + +/* Return the total number of probes that may be sent to each host. This never + changes after initialization. */ +unsigned int UltraScanInfo::numProbesPerHost() const { + unsigned int numprobes = 0; + + if (tcp_scan) { + numprobes = ports->tcp_count; + } else if (udp_scan) { + numprobes = ports->udp_count; + } else if (sctp_scan) { + numprobes = ports->sctp_count; + } else if (prot_scan) { + numprobes = ports->prot_count; + } else if (ping_scan_arp) { + numprobes = 1; + } else if (ping_scan_nd) { + numprobes = 1; + } else if (ping_scan) { + numprobes = 0; + if (ptech.rawtcpscan) { + if (o.pingtype & PINGTYPE_TCP_USE_ACK) + numprobes += ports->ack_ping_count; + if (o.pingtype & PINGTYPE_TCP_USE_SYN) + numprobes += ports->syn_ping_count; + } + if (ptech.rawudpscan) + numprobes += ports->udp_ping_count; + if (ptech.rawsctpscan) + numprobes += ports->sctp_ping_count; + if (ptech.rawicmpscan) { + if (o.pingtype & PINGTYPE_ICMP_PING) + numprobes++; + if (o.pingtype & PINGTYPE_ICMP_MASK) + numprobes++; + if (o.pingtype & PINGTYPE_ICMP_TS) + numprobes++; + } + if (ptech.rawprotoscan) + numprobes += ports->proto_ping_count; + if (ptech.connecttcpscan) + numprobes += ports->syn_ping_count; + } else assert(0); + + return numprobes; +} + +/* Consults with the group stats, and the hstats for every + incomplete hosts to determine whether any probes may be sent. + Returns true if they can be sent immediately. If when is + non-NULL, it is filled with the next possible time that probes + can be sent, assuming no probe responses are received (call it + again if they are). when will be now, if the function returns + true */ +bool UltraScanInfo::sendOK(struct timeval *when) const { + struct timeval lowhtime = {0}; + struct timeval tmptv; + std::multiset::const_iterator host; + bool ggood = false; + bool thisHostGood = false; + bool foundgood = false; + + ggood = gstats->sendOK(when); + + if (!ggood) { + if (when) { + lowhtime = *when; + // Can't do anything until global is OK - means packet receipt + // or probe timeout. + for (host = incompleteHosts.begin(); host != incompleteHosts.end(); + host++) { + if ((*host)->nextTimeout(&tmptv)) { + if (TIMEVAL_SUBTRACT(tmptv, lowhtime) < 0) + lowhtime = tmptv; + } + } + *when = lowhtime; + } + } else { + for (host = incompleteHosts.begin(); host != incompleteHosts.end(); host++) { + thisHostGood = (*host)->sendOK(&tmptv); + if (ggood && thisHostGood) { + lowhtime = tmptv; + foundgood = true; + break; + } + + if (!foundgood || TIMEVAL_SUBTRACT(lowhtime, tmptv) > 0) { + lowhtime = tmptv; + foundgood = true; + } + } + + assert(foundgood); + } + + /* Defer to the group stats if they need a shorter delay to enforce a minimum + packet sending rate. */ + if (o.min_packet_send_rate != 0.0) { + if (TIMEVAL_MSEC_SUBTRACT(gstats->send_no_later_than, lowhtime) < 0) + lowhtime = gstats->send_no_later_than; + } + + if (TIMEVAL_MSEC_SUBTRACT(lowhtime, now) < 0) + lowhtime = now; + + if (when) + *when = lowhtime; + + return (TIMEVAL_MSEC_SUBTRACT(lowhtime, now) == 0); +} + +/* Find a HostScanStats by its IP address in the incomplete and completed lists. + Returns NULL if none are found. */ +HostScanStats *UltraScanInfo::findHost(struct sockaddr_storage *ss) const { + std::multiset::const_iterator hss; + + HssPredicate::ss = ss; + HostScanStats *fakeHss = NULL; + + hss = incompleteHosts.find(fakeHss); + if (hss != incompleteHosts.end()) { + if (o.debugging > 2) + log_write(LOG_STDOUT, "Found %s in incomplete hosts list.\n", (*hss)->target->targetipstr()); + return *hss; + } + + hss = completedHosts.find(fakeHss); + if (hss != completedHosts.end()) { + if (o.debugging > 2) + log_write(LOG_STDOUT, "Found %s in completed hosts list.\n", (*hss)->target->targetipstr()); + return *hss; + } + + return NULL; +} + +/* Check if incompleteHosts list contains less than n elements. This function + is here to replace numIncompleteHosts() < n, which would have to walk + through the entire list. */ +bool UltraScanInfo::numIncompleteHostsLessThan(unsigned int n) const { + std::multiset::const_iterator hostI; + unsigned int count; + + count = 0; + hostI = incompleteHosts.begin(); + while (count < n && hostI != incompleteHosts.end()) { + hostI++; + count++; + } + + return count < n; +} + +static bool pingprobe_is_better(const probespec *new_probe, int new_state, + const probespec *old_probe, int old_state); + +/* Removes any hosts that have completed their scans from the incompleteHosts + list, and remove any hosts from completedHosts which have exceeded their + lifetime. Returns the number of hosts removed. */ +int UltraScanInfo::removeCompletedHosts() { + std::multiset::iterator hostI, nxt; + HostScanStats *hss = NULL; + int hostsRemoved = 0; + bool timedout = false; + struct timeval compare; + + /* We don't want to run this all of the time */ + TIMEVAL_MSEC_ADD(compare, lastCompletedHostRemoval, COMPL_HOST_LIFETIME_MS / 2); + if (TIMEVAL_AFTER(now, compare) ) { + /* Remove any that were completed before this time: */ + TIMEVAL_MSEC_ADD(compare, now, -COMPL_HOST_LIFETIME_MS); + for (hostI = completedHosts.begin(); hostI != completedHosts.end(); hostI = nxt) { + nxt = hostI; + nxt++; + hss = (*hostI); + + /* Keep it if it's our port scan ping host */ + if (hss == gstats->pinghost) + continue; + + if (TIMEVAL_BEFORE(hss->completiontime, compare) ) { + /* Any active probes in completed hosts count against our global + * cwnd, so be sure to remove them or we can run out of space. */ + hss->destroyAllOutstandingProbes(); + completedHosts.erase(hostI); + hostsRemoved++; + } + } + lastCompletedHostRemoval = now; + } + + for (hostI = incompleteHosts.begin(); hostI != incompleteHosts.end(); + hostI = nxt) { + nxt = hostI; + nxt++; + hss = *hostI; + assert(hss); + // Don't bother checking timedOut for discovery scans or if the target is already completed. + if (hss->completed() || (timedout = (!ping_scan) && hss->target->timedOut(&now)) != false) { + /* A host to remove! First adjust nextI appropriately */ + if (nextI == hostI && incompleteHosts.size() > 1) { + nextI++; + if (nextI == incompleteHosts.end()) + nextI = incompleteHosts.begin(); + } + if (o.verbose && gstats->numprobes > 50) { + int remain = incompleteHosts.size() - 1; + if (remain && !timedout) + log_write(LOG_STDOUT, "Completed %s against %s in %.2fs (%d %s)\n", + scantype2str(scantype), hss->target->targetipstr(), + TIMEVAL_MSEC_SUBTRACT(now, SPM->begin) / 1000.0, remain, + (remain == 1) ? "host left" : "hosts left"); + else if (timedout) + log_write(LOG_STDOUT, "%s timed out during %s (%d %s)\n", + hss->target->targetipstr(), scantype2str(scantype), remain, + (remain == 1) ? "host left" : "hosts left"); + } + if (o.debugging > 2) { + unsigned int num_outstanding_probes; + num_outstanding_probes = hss->num_probes_outstanding(); + log_write(LOG_PLAIN, "Moving %s to completed hosts list with %d outstanding %s.\n", + hss->target->targetipstr(), num_outstanding_probes, + num_outstanding_probes == 1 ? "probe" : "probes"); + if (o.debugging > 3) { + char tmpbuf[64]; + std::list::const_iterator iter; + for (iter = hss->probes_outstanding.begin(); iter != hss->probes_outstanding.end(); iter++) + log_write(LOG_PLAIN, "* %s\n", probespec2ascii((probespec *) (*iter)->pspec(), tmpbuf, sizeof(tmpbuf))); + } + } + hss->completiontime = now; + completedHosts.insert(hss); + incompleteHosts.erase(hostI); + hostsRemoved++; + /* Consider making this host the new global ping host during its + retirement in the completed hosts list. */ + HostScanStats *pinghost = gstats->pinghost; + if ((pinghost == NULL && hss->target->pingprobe.type != PS_NONE) + || (pinghost != NULL && pinghost->num_probes_active == 0 + && !pingprobe_is_better(&pinghost->target->pingprobe, pinghost->target->pingprobe_state, &hss->target->pingprobe, hss->target->pingprobe_state))) { + if (o.debugging > 1) + log_write(LOG_PLAIN, "Changing global ping host to %s.\n", hss->target->targetipstr()); + gstats->pinghost = hss; + } + if (timedout) + gstats->num_hosts_timedout++; + /* We may have received an ARP response before we sent a probe, which + * would mean the timeout clock is not running. Avoid an assertion + * failure here by checking first. */ + if (hss->target->timeOutClockRunning()) { + hss->target->stopTimeOutClock(&now); + } + } + } + return hostsRemoved; +} + +/* Determines an ideal number of hosts to be scanned (port scan, os + scan, version detection, etc.) in parallel after the ping scan is + completed. This is a balance between efficiency (more hosts in + parallel often reduces scan time per host) and results latency (you + need to wait for all hosts to finish before Nmap can spit out the + results). Memory consumption usually also increases with the + number of hosts scanned in parallel, though rarely to significant + levels. */ +int determineScanGroupSize(int hosts_scanned_so_far, + const struct scan_lists *ports) { + int groupsize = 16; + + if (o.UDPScan()) + groupsize = 128; + else if (o.SCTPScan()) + groupsize = 128; + else if (o.TCPScan()) { + groupsize = MAX(1024 / (ports->tcp_count ? ports->tcp_count : 1), 64); + if (ports->tcp_count > 1000 && o.timing_level <= 4) { + int quickgroupsz = 4; + if (o.timing_level == 4) + quickgroupsz = 8; + if (hosts_scanned_so_far == 0) + groupsize = quickgroupsz; // Give quick results for the very first batch + else if (hosts_scanned_so_far == quickgroupsz && + groupsize > quickgroupsz * 2) + /* account for initial quick-scan to keep us aligned + on common network boundaries (e.g. /24) */ + groupsize -= quickgroupsz; + } + } + + groupsize = box(o.minHostGroupSz(), o.maxHostGroupSz(), groupsize); + + if (o.max_ips_to_scan && (o.max_ips_to_scan - hosts_scanned_so_far) < (unsigned int)groupsize) + // don't scan more randomly generated hosts than was specified + groupsize = o.max_ips_to_scan - hosts_scanned_so_far; + + return groupsize; +} + +/* Initialize the ultra_timing_vals structure timing. The utt must be + TIMING_HOST or TIMING_GROUP. If you happen to have the current + time handy, pass it as now, otherwise pass NULL */ +static void init_ultra_timing_vals(ultra_timing_vals *timing, + enum ultra_timing_type utt, + int num_hosts_in_group, + struct ultra_scan_performance_vars *perf, + struct timeval *now) { + timing->cwnd = (utt == TIMING_HOST) ? perf->host_initial_cwnd : perf->group_initial_cwnd; + timing->ssthresh = perf->initial_ssthresh; /* Will be reduced if any packets are dropped anyway */ + timing->num_replies_expected = 0; + timing->num_replies_received = 0; + timing->num_updates = 0; + if (now) + timing->last_drop = *now; + else gettimeofday(&timing->last_drop, NULL); +} + +/* Returns the next probe to try against target. Supports many + different types of probes (see probespec structure). Returns 0 and + fills in pspec if there is a new probe, -1 if there are none + left. */ +static int get_next_target_probe(UltraScanInfo *USI, HostScanStats *hss, + probespec *pspec) { + assert(pspec); + + if (USI->tcp_scan) { + if (hss->next_portidx >= USI->ports->tcp_count) + return -1; + if (USI->scantype == CONNECT_SCAN) + pspec->type = PS_CONNECTTCP; + else + pspec->type = PS_TCP; + pspec->proto = IPPROTO_TCP; + + pspec->pd.tcp.dport = USI->ports->tcp_ports[hss->next_portidx++]; + if (USI->scantype == CONNECT_SCAN) + pspec->pd.tcp.flags = TH_SYN; + else if (o.scanflags != -1) + pspec->pd.tcp.flags = o.scanflags; + else { + switch (USI->scantype) { + case SYN_SCAN: + pspec->pd.tcp.flags = TH_SYN; + break; + case ACK_SCAN: + pspec->pd.tcp.flags = TH_ACK; + break; + case XMAS_SCAN: + pspec->pd.tcp.flags = TH_FIN | TH_URG | TH_PUSH; + break; + case NULL_SCAN: + pspec->pd.tcp.flags = 0; + break; + case FIN_SCAN: + pspec->pd.tcp.flags = TH_FIN; + break; + case MAIMON_SCAN: + pspec->pd.tcp.flags = TH_FIN | TH_ACK; + break; + case WINDOW_SCAN: + pspec->pd.tcp.flags = TH_ACK; + break; + default: + assert(0); + break; + } + } + return 0; + } else if (USI->udp_scan) { + if (hss->next_portidx >= USI->ports->udp_count) + return -1; + pspec->type = PS_UDP; + pspec->proto = IPPROTO_UDP; + pspec->pd.udp.dport = USI->ports->udp_ports[hss->next_portidx++]; + return 0; + } else if (USI->sctp_scan) { + if (hss->next_portidx >= USI->ports->sctp_count) + return -1; + pspec->type = PS_SCTP; + pspec->proto = IPPROTO_SCTP; + pspec->pd.sctp.dport = USI->ports->sctp_ports[hss->next_portidx++]; + switch (USI->scantype) { + case SCTP_INIT_SCAN: + pspec->pd.sctp.chunktype = SCTP_INIT; + break; + case SCTP_COOKIE_ECHO_SCAN: + pspec->pd.sctp.chunktype = SCTP_COOKIE_ECHO; + break; + default: + assert(0); + } + return 0; + } else if (USI->prot_scan) { + if (hss->next_portidx >= USI->ports->prot_count) + return -1; + pspec->type = PS_PROTO; + pspec->proto = USI->ports->prots[hss->next_portidx++]; + return 0; + } else if (USI->ping_scan_arp) { + if (hss->sent_arp) + return -1; + pspec->type = PS_ARP; + hss->sent_arp = true; + return 0; + } else if (USI->ping_scan_nd) { + if (hss->sent_arp) + return -1; + pspec->type = PS_ND; + hss->sent_arp = true; + return 0; + } else if (USI->ping_scan) { + /* This is ordered to try probes of higher effectiveness first: + -PE -PS -PA -PP -PU + -PA is slightly better than -PS when combined with -PE, but give -PS an + edge because it is less likely to be dropped by firewalls. */ + if (USI->ptech.rawicmpscan) { + if (hss->target->af() == AF_INET6) { + pspec->type = PS_ICMPV6; + pspec->proto = IPPROTO_ICMPV6; + if ((o.pingtype & PINGTYPE_ICMP_PING) && !hss->sent_icmp_ping) { + hss->sent_icmp_ping = true; + pspec->pd.icmp.type = ICMPV6_ECHO; + pspec->pd.icmp.code = 0; + return 0; + } + } + pspec->type = PS_ICMP; + pspec->proto = IPPROTO_ICMP; + if ((o.pingtype & PINGTYPE_ICMP_PING) && !hss->sent_icmp_ping) { + hss->sent_icmp_ping = true; + pspec->pd.icmp.type = ICMP_ECHO; + pspec->pd.icmp.code = 0; + return 0; + } + } + if (USI->ptech.rawtcpscan) { + pspec->type = PS_TCP; + pspec->proto = IPPROTO_TCP; + if ((o.pingtype & PINGTYPE_TCP_USE_SYN) + && hss->next_synportpingidx < USI->ports->syn_ping_count) { + pspec->pd.tcp.dport = USI->ports->syn_ping_ports[hss->next_synportpingidx++]; + pspec->pd.tcp.flags = TH_SYN; + return 0; + } + if ((o.pingtype & PINGTYPE_TCP_USE_ACK) + && hss->next_ackportpingidx < USI->ports->ack_ping_count) { + pspec->pd.tcp.dport = USI->ports->ack_ping_ports[hss->next_ackportpingidx++]; + pspec->pd.tcp.flags = TH_ACK; + return 0; + } + } + if (USI->ptech.rawicmpscan) { + pspec->type = PS_ICMP; + pspec->proto = IPPROTO_ICMP; + if ((o.pingtype & PINGTYPE_ICMP_TS) && !hss->sent_icmp_ts) { + hss->sent_icmp_ts = true; + pspec->pd.icmp.type = ICMP_TSTAMP; + pspec->pd.icmp.code = 0; + return 0; + } + } + if (USI->ptech.rawudpscan && hss->next_udpportpingidx < USI->ports->udp_ping_count) { + pspec->type = PS_UDP; + pspec->proto = IPPROTO_UDP; + pspec->pd.udp.dport = USI->ports->udp_ping_ports[hss->next_udpportpingidx++]; + return 0; + } + if (USI->ptech.rawsctpscan && hss->next_sctpportpingidx < USI->ports->sctp_ping_count) { + pspec->type = PS_SCTP; + pspec->proto = IPPROTO_SCTP; + pspec->pd.sctp.dport = USI->ports->sctp_ping_ports[hss->next_sctpportpingidx++]; + pspec->pd.sctp.chunktype = SCTP_INIT; + return 0; + } + if (USI->ptech.rawprotoscan && hss->next_protoportpingidx < USI->ports->proto_ping_count) { + pspec->type = PS_PROTO; + pspec->proto = USI->ports->proto_ping_ports[hss->next_protoportpingidx++]; + return 0; + } + if (USI->ptech.connecttcpscan && hss->next_synportpingidx < USI->ports->syn_ping_count) { + pspec->type = PS_CONNECTTCP; + pspec->proto = IPPROTO_TCP; + pspec->pd.tcp.dport = USI->ports->syn_ping_ports[hss->next_synportpingidx++]; + pspec->pd.tcp.flags = TH_SYN; + return 0; + } + if (USI->ptech.rawicmpscan) { + pspec->type = PS_ICMP; + pspec->proto = IPPROTO_ICMP; + if ((o.pingtype & PINGTYPE_ICMP_MASK) && !hss->sent_icmp_mask) { + hss->sent_icmp_mask = true; + pspec->pd.icmp.type = ICMP_MASK; + pspec->pd.icmp.code = 0; + return 0; + } + } + } + assert(0); /* TODO: need to handle other protocols */ + return -1; +} + +/* Returns whether there are ports remaining to probe */ +bool HostScanStats::freshPortsLeft() const { + if (USI->tcp_scan) { + return (next_portidx < USI->ports->tcp_count); + } else if (USI->udp_scan) { + return (next_portidx < USI->ports->udp_count); + } else if (USI->sctp_scan) { + return (next_portidx < USI->ports->sctp_count); + } else if (USI->prot_scan) { + return (next_portidx < USI->ports->prot_count); + } else if (USI->ping_scan_arp || USI->ping_scan_nd) { + return !sent_arp; + } else if (USI->ping_scan) { + if (USI->ptech.rawtcpscan) { + if (o.pingtype & PINGTYPE_TCP_USE_ACK && next_ackportpingidx < USI->ports->ack_ping_count) + return true; + if (o.pingtype & PINGTYPE_TCP_USE_SYN && next_synportpingidx < USI->ports->syn_ping_count) + return true; + } + if (USI->ptech.rawicmpscan) { + if ((o.pingtype & PINGTYPE_ICMP_PING) && !sent_icmp_ping) + return true; + if ((o.pingtype & PINGTYPE_ICMP_MASK) && !sent_icmp_mask) + return true; + if ((o.pingtype & PINGTYPE_ICMP_TS) && !sent_icmp_ts) + return true; + } + if (USI->ptech.connecttcpscan && next_synportpingidx < USI->ports->syn_ping_count) + return true; + if (USI->ptech.rawudpscan && next_udpportpingidx < USI->ports->udp_ping_count) + return true; + if (USI->ptech.rawsctpscan && next_sctpportpingidx < USI->ports->sctp_ping_count) + return true; + if (USI->ptech.rawprotoscan && next_protoportpingidx < USI->ports->proto_ping_count) + return true; + return false; + } + assert(0); + return false; +} + +/* Returns the number of ports remaining to probe */ +int HostScanStats::numFreshPortsLeft() const { + if (USI->tcp_scan) { + if (next_portidx >= USI->ports->tcp_count) + return 0; + return USI->ports->tcp_count - next_portidx; + } else if (USI->udp_scan) { + if (next_portidx >= USI->ports->udp_count) + return 0; + return USI->ports->udp_count - next_portidx; + } else if (USI->sctp_scan) { + if (next_portidx >= USI->ports->sctp_count) + return 0; + return USI->ports->sctp_count - next_portidx; + } else if (USI->prot_scan) { + if (next_portidx >= USI->ports->prot_count) + return 0; + return USI->ports->prot_count - next_portidx; + } else if (USI->ping_scan_arp) { + if (sent_arp) + return 0; + return 1; + } else if (USI->ping_scan_nd) { + if (sent_arp) + return 0; + return 1; + } else if (USI->ping_scan) { + unsigned int num_probes = 0; + if (USI->ptech.rawtcpscan) { + if (o.pingtype & PINGTYPE_TCP_USE_ACK) + num_probes += USI->ports->ack_ping_count - next_ackportpingidx; + if (o.pingtype & PINGTYPE_TCP_USE_SYN) + num_probes += USI->ports->syn_ping_count - next_synportpingidx; + } + if (USI->ptech.rawudpscan) + num_probes += USI->ports->udp_ping_count - next_udpportpingidx; + if (USI->ptech.rawsctpscan) + num_probes += USI->ports->sctp_ping_count - next_sctpportpingidx; + if (USI->ptech.rawicmpscan) { + if ((o.pingtype & PINGTYPE_ICMP_PING) && !sent_icmp_ping) + num_probes++; + if ((o.pingtype & PINGTYPE_ICMP_MASK) && !sent_icmp_mask) + num_probes++; + if ((o.pingtype & PINGTYPE_ICMP_TS) && !sent_icmp_ts) + num_probes++; + } + if (USI->ptech.rawprotoscan) + num_probes += USI->ports->proto_ping_count - next_protoportpingidx; + if (USI->ptech.connecttcpscan) + num_probes += USI->ports->syn_ping_count - next_synportpingidx; + return num_probes; + } + assert(0); + return 0; +} + +/* Removes a probe from probes_outstanding, adjusts HSS and USS + active probe stats accordingly, then deletes the probe. */ +void HostScanStats::destroyOutstandingProbe(std::list::iterator probeI) { + UltraProbe *probe = *probeI; + assert(!probes_outstanding.empty()); + if (!probe->timedout) { + assert(num_probes_active > 0); + num_probes_active--; + assert(USI->gstats->num_probes_active > 0); + USI->gstats->num_probes_active--; + } + + if (!probe->isPing() && probe->timedout && !probe->retransmitted) { + assert(num_probes_waiting_retransmit > 0); + num_probes_waiting_retransmit--; + } + + /* Remove it from scan watch lists, if it exists on them. */ + if (probe->type == UltraProbe::UP_CONNECT && probe->CP()->sd > 0) + USI->gstats->CSI->clearSD(probe->CP()->sd); + + probes_outstanding.erase(probeI); + delete probe; +} + +/* Removes all probes from probes_outstanding using + destroyOutstandingProbe. This is used in ping scan to quit waiting + for responses once a host is known to be up. Invalidates iterators + pointing into probes_outstanding. */ +void HostScanStats::destroyAllOutstandingProbes() { + while (!probes_outstanding.empty()) + destroyOutstandingProbe(probes_outstanding.begin()); +} + +/* Adjust host and group timeouts (struct timeout_info) based on a received + packet. If rcvdtime is NULL, nothing is updated. + + This function is called for every probe response received, in order to keep + an accurate timeout estimate. ultrascan_adjust_timing, on the other hand, is + not called when a response is not useful for adjusting other timing + variables. */ +static void ultrascan_adjust_timeouts(UltraScanInfo *USI, HostScanStats *hss, + UltraProbe *probe, + struct timeval *rcvdtime) { + if (rcvdtime == NULL) + return; + + adjust_timeouts2(&(probe->sent), rcvdtime, &(hss->target->to)); + adjust_timeouts2(&(probe->sent), rcvdtime, &(USI->gstats->to)); + + USI->gstats->lastrcvd = hss->lastrcvd = *rcvdtime; +} + +/* Adjust host and group congestion control variables (struct ultra_timing_vals) + and host send delay (struct send_delay_nfo) based on a received packet. Use + rcvdtime == NULL to indicate that you have given up on a probe and want to + count this as a DROPPED PACKET. */ +static void ultrascan_adjust_timing(UltraScanInfo *USI, HostScanStats *hss, + UltraProbe *probe, + struct timeval *rcvdtime) { + int ping_magnifier = (probe->isPing()) ? USI->perf.ping_magnifier : 1; + + USI->gstats->timing.num_replies_expected++; + USI->gstats->timing.num_updates++; + + hss->timing.num_replies_expected++; + hss->timing.num_updates++; + + /* Notice a drop if + 1) We get a response to a retransmitted probe (meaning the first reply was + dropped), or + 2) We got no response to a timing ping. */ + bool is_drop = (!probe->isPing() && probe->get_tryno() > 0 && rcvdtime != NULL) + || (probe->isPing() && rcvdtime == NULL); + if (is_drop) { + if (o.debugging > 1) + log_write(LOG_PLAIN, "Ultrascan DROPPED %sprobe packet to %s detected\n", probe->isPing() ? "PING " : "", hss->target->targetipstr()); + // Drops often come in big batches, but we only want one decrease per batch. + if (TIMEVAL_AFTER(probe->sent, hss->timing.last_drop)) + hss->timing.drop(hss->num_probes_active, &USI->perf, &USI->now); + if (TIMEVAL_AFTER(probe->sent, USI->gstats->timing.last_drop)) + USI->gstats->timing.drop_group(USI->gstats->num_probes_active, &USI->perf, &USI->now); + } + /* If !probe->isPing() and rcvdtime == NULL, do nothing. */ + + /* Increase the window for a positive reply. This can overlap with case (1) + above. */ + if (rcvdtime != NULL) { + USI->gstats->timing.ack(&USI->perf, ping_magnifier); + hss->timing.ack(&USI->perf, ping_magnifier); + } + + /* If packet drops are particularly bad, enforce a delay between + packet sends (useful for cases such as UDP scan where responses + are frequently rate limited by dest machines or firewalls) */ + + /* First we decide whether this packet counts as a drop for send + delay calculation purposes. This statement means if (a ping since last boost failed, or the previous packet was both sent after the last boost and dropped) */ + if (is_drop && TIMEVAL_AFTER(probe->sent, hss->sdn.last_boost)) { + hss->sdn.droppedRespSinceDelayChanged++; + // printf("SDELAY: increasing drops to %d (good: %d; tryno: %d, sent: %.4fs; prevSent: %.4fs, last_boost: %.4fs\n", hss->sdn.droppedRespSinceDelayChanged, hss->sdn.goodRespSinceDelayChanged, probe->tryno, o.TimeSinceStartMS(&probe->sent) / 1000.0, o.TimeSinceStartMS(&probe->prevSent) / 1000.0, o.TimeSinceStartMS(&hss->sdn.last_boost) / 1000.0); + } else if (rcvdtime) { + hss->sdn.goodRespSinceDelayChanged++; + // printf("SDELAY: increasing good to %d (bad: %d)\n", hss->sdn.goodRespSinceDelayChanged, hss->sdn.droppedRespSinceDelayChanged); + } + + /* Now change the send delay if necessary */ + unsigned int oldgood = hss->sdn.goodRespSinceDelayChanged; + unsigned int oldbad = hss->sdn.droppedRespSinceDelayChanged; + double threshold = (o.timing_level >= 4) ? 0.40 : 0.30; + if (oldbad > 10 && (oldbad / ((double) oldbad + oldgood) > threshold)) { + unsigned int olddelay = hss->sdn.delayms; + hss->boostScanDelay(); + if (o.verbose && hss->sdn.delayms != olddelay) + log_write(LOG_PLAIN, "Increasing send delay for %s from %d to %d due to %d out of %d dropped probes since last increase.\n", + hss->target->targetipstr(), olddelay, hss->sdn.delayms, oldbad, + oldbad + oldgood); + } +} + +/* Mark an outstanding probe as timedout. Adjusts stats + accordingly. For connect scans, this closes the socket. */ +void HostScanStats::markProbeTimedout(std::list::iterator probeI) { + UltraProbe *probe = *probeI; + assert(!probe->timedout); + assert(!probe->retransmitted); + probe->timedout = true; + assert(num_probes_active > 0); + num_probes_active--; + assert(USI->gstats->num_probes_active > 0); + USI->gstats->num_probes_active--; + ultrascan_adjust_timing(USI, this, probe, NULL); + if (!probe->isPing()) + /* I'll leave it in the queue in case some response ever does come */ + num_probes_waiting_retransmit++; + + if (probe->type == UltraProbe::UP_CONNECT && probe->CP()->sd >= 0 ) { + /* Free the socket as that is a valuable resource, though it is a shame + late responses will not be permitted */ + USI->gstats->CSI->clearSD(probe->CP()->sd); + close(probe->CP()->sd); + probe->CP()->sd = -1; + } +} + +bool HostScanStats::completed() const { + /* If there are probes active or awaiting retransmission, we are not done. */ + if (num_probes_active != 0 || num_probes_waiting_retransmit != 0 + || !probe_bench.empty() || !retry_stack.empty()) { + return false; + } + + /* With ping scan, we are done once we know the host is up or down. */ + if (USI->ping_scan && ((target->flags & HOST_UP) + || (target->flags & HOST_DOWN) || target->weird_responses)) { + return true; + } + + /* With other types of scan, we are done when there are no more ports to + probe. */ + return !freshPortsLeft(); +} + +/* This function provides the proper cwnd and ssthresh to use. It may + differ from versions in timing member var because when no responses + have been received for this host, may look at others in the group. + For CHANGING this host's timing, use the timing memberval + instead. */ +void HostScanStats::getTiming(struct ultra_timing_vals *tmng) const { + assert(tmng); + + /* Use the per-host value if a pingport has been found or very few probes + have been sent */ + if (target->pingprobe.type != PS_NONE || numprobes_sent < 80) { + *tmng = timing; + return; + } + + /* Otherwise, use the global cwnd stats if it has sufficient responses */ + if (USI->gstats->timing.num_updates > 1) { + *tmng = USI->gstats->timing; + return; + } + + /* Last resort is to use canned values */ + tmng->cwnd = USI->perf.host_initial_cwnd; + tmng->ssthresh = USI->perf.initial_ssthresh; + tmng->num_updates = 0; + return; +} + +/* Define a score for a ping probe, for the purposes of deciding whether one + probe should be preferred to another. The order, from most preferred to least + preferred, is + Raw TCP/SCTP (not filtered, not SYN/INIT to an open port) + ICMP information queries (echo request, timestamp request, netmask req) + ARP/ND + Raw TCP/SCTP (SYN/INIT to an open port) + UDP, IP protocol, or other ICMP (including filtered TCP/SCTP) + TCP connect + Anything else + Raw TCP SYN / SCTP INIT to an open port is given a low preference because of the + risk of SYN flooding (this is the only case where the port state is considered). + The probe passed to this function is assumed to have received a positive + response, that is, it should not have set a port state just by timing out. */ +static unsigned int pingprobe_score(const probespec *pspec, int state) { + unsigned int score; + + switch (pspec->type) { + case PS_TCP: + if (state == PORT_FILTERED) /* Received an ICMP error. */ + score = 20; + else if (pspec->pd.tcp.flags == TH_SYN && (state == PORT_OPEN || state == PORT_UNKNOWN)) + score = 30; + else if (pspec->pd.tcp.dport == 25 || + pspec->pd.tcp.dport == 113 || + pspec->pd.tcp.dport == 135 || + pspec->pd.tcp.dport == 139 || + pspec->pd.tcp.dport == 445) + /* Frequently spoofed port numbers */ + score = 50; + else + score = 60; + break; + case PS_SCTP: + if (state == PORT_FILTERED) /* Received an ICMP error. */ + score = 20; + else if (state == PORT_OPEN || state == PORT_UNKNOWN) + score = 30; + else + score = 60; + break; + case PS_ICMP: + if (pspec->pd.icmp.type == ICMP_ECHO || pspec->pd.icmp.type == ICMP_MASK || pspec->pd.icmp.type == ICMP_TSTAMP) + score = 50; + else + score = 20; + break; + case PS_ARP: + case PS_ND: + score = 40; + break; + case PS_UDP: + // Penalize ports with many payloads, since we can't be sure which one responded. + score = 20 - udp_payload_count(pspec->pd.udp.dport); + // But one payload is ok + if (score == 19) + score = 20; + break; + case PS_PROTO: + score = 20; + break; + case PS_CONNECTTCP: + score = 10; + break; + case PS_NONE: + default: + score = 0; + break; + } + + return score; +} + +/* Return true if new_probe and new_state define a better ping probe, as defined + by pingprobe_score, than do old_probe and old_state. */ +static bool pingprobe_is_better(const probespec *new_probe, int new_state, + const probespec *old_probe, int old_state) { + return pingprobe_score(new_probe, new_state) > pingprobe_score(old_probe, old_state); +} + +static bool ultrascan_host_pspec_update(UltraScanInfo *USI, HostScanStats *hss, + const probespec *pspec, int newstate); + +/* Like ultrascan_port_probe_update(), except it is called with just a + probespec rather than a whole UltraProbe. Returns true if the port + was added or at least the state was changed. */ +static bool ultrascan_port_pspec_update(UltraScanInfo *USI, + HostScanStats *hss, + const probespec *pspec, + int newstate) { + u16 portno = 0; + u8 proto = 0; + int oldstate = PORT_TESTING; + /* Whether no response means a port is open */ + bool noresp_open_scan = USI->noresp_open_scan; + + if (USI->prot_scan) { + proto = IPPROTO_IP; + portno = pspec->proto; + } else if (pspec->type == PS_TCP || pspec->type == PS_CONNECTTCP) { + proto = IPPROTO_TCP; + portno = pspec->pd.tcp.dport; + } else if (pspec->type == PS_UDP) { + proto = IPPROTO_UDP; + portno = pspec->pd.udp.dport; + } else if (pspec->type == PS_SCTP) { + proto = IPPROTO_SCTP; + portno = pspec->pd.sctp.dport; + } else assert(0); + + if (hss->target->ports.portIsDefault(portno, proto)) { + oldstate = PORT_TESTING; + hss->ports_finished++; + } else { + oldstate = hss->target->ports.getPortState(portno, proto); + } + + /* printf("TCP port %hu has changed from state %s to %s!\n", portno, statenum2str(oldstate), statenum2str(newstate)); */ + switch (oldstate) { + /* TODO: I need more code here to determine when a state should + be overridden, for example PORT_OPEN trumps PORT_FILTERED + in a SYN scan, but not necessarily for UDP scan */ + case PORT_TESTING: + /* Brand new port -- add it to the list */ + hss->target->ports.setPortState(portno, proto, newstate); + break; + case PORT_OPEN: + if (newstate != PORT_OPEN) { + if (noresp_open_scan) { + hss->target->ports.setPortState(portno, proto, newstate); + } /* Otherwise The old open takes precedence */ + } + break; + case PORT_CLOSED: + if (newstate != PORT_CLOSED) { + if (!noresp_open_scan && newstate != PORT_FILTERED) + hss->target->ports.setPortState(portno, proto, newstate); + } + break; + case PORT_FILTERED: + if (newstate != PORT_FILTERED) { + if (!noresp_open_scan || newstate != PORT_OPEN) + hss->target->ports.setPortState(portno, proto, newstate); + } + break; + case PORT_UNFILTERED: + /* This could happen in an ACK scan if I receive a RST and then an + ICMP filtered message. I'm gonna stick with unfiltered in that + case. I'll change it if the new state is open or closed, + though I don't expect that to ever happen */ + if (newstate == PORT_OPEN || newstate == PORT_CLOSED) + hss->target->ports.setPortState(portno, proto, newstate); + break; + case PORT_OPENFILTERED: + if (newstate != PORT_OPENFILTERED) { + hss->target->ports.setPortState(portno, proto, newstate); + } + break; + default: + fatal("Unexpected port state: %d\n", oldstate); + break; + } + + return oldstate != newstate; +} + +/* Boost the scan delay for this host, usually because too many packet + drops were detected. */ +void HostScanStats::boostScanDelay() { + unsigned int maxAllowed = USI->tcp_scan ? o.maxTCPScanDelay() : + USI->udp_scan ? o.maxUDPScanDelay() : + o.maxSCTPScanDelay(); + if (sdn.delayms == 0) + sdn.delayms = (USI->udp_scan) ? 50 : 5; // In many cases, a pcap wait takes a minimum of 80ms, so this matters little :( + else sdn.delayms = MIN(sdn.delayms * 2, MAX(sdn.delayms, 1000)); + sdn.delayms = MIN(sdn.delayms, maxAllowed); + sdn.last_boost = USI->now; + sdn.droppedRespSinceDelayChanged = 0; + sdn.goodRespSinceDelayChanged = 0; +} + +/* Dismiss all probe attempts on bench -- hosts are marked down and ports will + be set to whatever the default port state is for the scan. */ +void HostScanStats::dismissBench() { + if (probe_bench.empty()) + return; + while (!probe_bench.empty()) { + if (USI->ping_scan) + ultrascan_host_pspec_update(USI, this, &probe_bench.back(), HOST_DOWN); + /* Nothing to do if !USI->ping_scan. ultrascan_port_pspec_update would + allocate a Port object but we rely on the default port state to save + memory. */ + probe_bench.pop_back(); + } + bench_tryno = 0; +} + +/* Move all members of bench to retry_stack for probe retransmission */ +void HostScanStats::retransmitBench() { + if (probe_bench.empty()) + return; + + /* Move all contents of probe_bench to the end of retry_stack, updating retry_stack_tries accordingly */ + retry_stack.insert(retry_stack.end(), probe_bench.begin(), probe_bench.end()); + retry_stack_tries.insert(retry_stack_tries.end(), probe_bench.size(), + bench_tryno); + assert(retry_stack.size() == retry_stack_tries.size()); + probe_bench.erase(probe_bench.begin(), probe_bench.end()); + bench_tryno = 0; +} + +/* Moves the given probe from the probes_outstanding list, to + probe_bench, and decrements num_probes_waiting_retransmit + accordingly */ +void HostScanStats::moveProbeToBench(std::list::iterator probeI) { + UltraProbe *probe = *probeI; + if (!probe_bench.empty()) + assert(bench_tryno == probe->get_tryno()); + else { + bench_tryno = probe->get_tryno(); + probe_bench.reserve(128); + } + probe_bench.push_back(*probe->pspec()); + probes_outstanding.erase(probeI); + num_probes_waiting_retransmit--; + delete probe; +} + +/* Called when a ping response is discovered. If adjust_timing is false, timing + stats are not updated. */ +void ultrascan_ping_update(UltraScanInfo *USI, HostScanStats *hss, + std::list::iterator probeI, + struct timeval *rcvdtime, + bool adjust_timing) { + ultrascan_adjust_timeouts(USI, hss, *probeI, rcvdtime); + if (adjust_timing) + ultrascan_adjust_timing(USI, hss, *probeI, rcvdtime); + hss->destroyOutstandingProbe(probeI); +} + +static const char *readhoststate(int state) { + switch (state) { + case HOST_UNKNOWN: + return "UNKNOWN"; + case HOST_UP: + return "HOST_UP"; + case HOST_DOWN: + return "HOST_DOWN"; + default: + return "COMBO"; + } + + return NULL; +} + +/* Update state of the host in hss based on its current state and newstate. + Returns true if the state was changed. */ +static bool ultrascan_host_pspec_update(UltraScanInfo *USI, HostScanStats *hss, + const probespec *pspec, int newstate) { + int oldstate = hss->target->flags; + /* If the host is already up, ignore any further updates. */ + if (hss->target->flags != HOST_UP) { + // don't allow HOST_UNKNOWN to override a known state. + hss->target->flags = (newstate == HOST_UNKNOWN ? oldstate : newstate); + /* For port scans (not -sn) where output may be delayed until more scan + * phases are done, emit a hosthint element during host discovery when a + * target is found to be up. */ + if (oldstate != newstate && newstate == HOST_UP && + !o.noportscan && USI->ping_scan) { + write_xml_hosthint(hss->target); + } + } + return hss->target->flags != oldstate; +} + +static void ultrascan_host_timeout_init(UltraScanInfo *USI, HostScanStats *hss) { + // Don't count host discovery time against host timeout clock. For large + // numbers of targets, we might be busy sending lots of new probes to new + // targets, and that time shouldn't count against the individual target. + if (!USI->ping_scan && !hss->target->timeOutClockRunning() && !hss->target->timedOut(NULL)) { + if (o.debugging > 2) { + log_write(LOG_STDOUT, "Ultrascan timeout init for %s at %.6f\n", hss->target->targetipstr(), TIMEVAL_SECS(USI->now)); + } + hss->target->startTimeOutClock(&USI->now); + } +} + +/* Called when a new status is determined for host in hss (eg. it is + found to be up or down by a ping/ping_arp scan. The probe that led + to this new decision is in probeI. This function needs to update + timing information and other stats as appropriate. If + adjust_timing_hint is false, packet stats are not updated. */ +void ultrascan_host_probe_update(UltraScanInfo *USI, HostScanStats *hss, + std::list::iterator probeI, + int newstate, struct timeval *rcvdtime, + bool adjust_timing_hint) { + UltraProbe *probe = *probeI; + + if (o.debugging > 1) { + struct timeval tv; + + gettimeofday(&tv, NULL); + log_write(LOG_STDOUT, "%s called for machine %s state %s -> %s (trynum %d time: %ld)\n", __func__, hss->target->targetipstr(), readhoststate(hss->target->flags), readhoststate(newstate), probe->get_tryno(), (long) TIMEVAL_SUBTRACT(tv, probe->sent)); + } + + ultrascan_host_pspec_update(USI, hss, probe->pspec(), newstate); + + ultrascan_adjust_timeouts(USI, hss, probe, rcvdtime); + + /* Decide whether to adjust timing. We and together a bunch of conditions. + First, don't adjust timing if adjust_timing_hint is false. */ + bool adjust_timing = adjust_timing_hint; + bool adjust_ping = adjust_timing_hint; + + /* If we got a response that meant "down" or "unknown", then it was an ICMP error. These + are often rate-limited (RFC 1812) or generated by a different host. We only + allow such responses to increase, not decrease, scanning speed by + disallowing drops (probe->get_tryno() > 0), and we don't allow changing the ping + probe to something that's likely to get dropped. */ + if (rcvdtime != NULL && newstate != HOST_UP) { + if (probe->get_tryno() > 0) { + if (adjust_timing && o.debugging > 1) + log_write(LOG_PLAIN, "Response for %s means new state is down; not adjusting timing.\n", hss->target->targetipstr()); + adjust_timing = false; + } + adjust_ping = false; + } + + if (adjust_timing) + ultrascan_adjust_timing(USI, hss, probe, rcvdtime); + + /* If this probe received a positive response, consider making it the new + timing ping probe. */ + if (rcvdtime != NULL && adjust_ping + && pingprobe_is_better(probe->pspec(), PORT_UNKNOWN, &hss->target->pingprobe, hss->target->pingprobe_state)) { + if (o.debugging > 1) { + char buf[64]; + probespec2ascii(probe->pspec(), buf, sizeof(buf)); + log_write(LOG_PLAIN, "Changing ping technique for %s to %s\n", hss->target->targetipstr(), buf); + } + hss->target->pingprobe = *probe->pspec(); + hss->target->pingprobe_state = PORT_UNKNOWN; + } + + hss->destroyOutstandingProbe(probeI); +} + +/* This function is called when a new status is determined for a port. + the port in the probeI of host hss is now in newstate. This + function needs to update timing information, other stats, and the + Nmap port state table as appropriate. If rcvdtime is NULL or we got + unimportant packet, packet stats are not updated. If you don't have an + UltraProbe list iterator, you may need to call ultrascan_port_psec_update() + instead. If adjust_timing_hint is false, packet stats are not + updated. */ +void ultrascan_port_probe_update(UltraScanInfo *USI, HostScanStats *hss, + std::list::iterator probeI, + int newstate, struct timeval *rcvdtime, + bool adjust_timing_hint) { + UltraProbe *probe = *probeI; + const probespec *pspec = probe->pspec(); + + ultrascan_port_pspec_update(USI, hss, pspec, newstate); + + ultrascan_adjust_timeouts(USI, hss, probe, rcvdtime); + + /* Decide whether to adjust timing. We and together a bunch of conditions. + First, don't adjust timing if adjust_timing_hint is false. */ + bool adjust_timing = adjust_timing_hint; + bool adjust_ping = adjust_timing_hint; + + /* If we got a response that meant "filtered", then it was an ICMP error. + These are often rate-limited (RFC 1812) or generated by a different host. + We only allow such responses to increase, not decrease, scanning speed by + not considering drops (probe->get_tryno() > 0), and we don't allow changing the + ping probe to something that's likely to get dropped. */ + if (rcvdtime != NULL && newstate == PORT_FILTERED && !USI->noresp_open_scan) { + if (probe->get_tryno() > 0) { + if (adjust_timing && o.debugging > 1) + log_write(LOG_PLAIN, "Response for %s means new state is filtered; not adjusting timing.\n", hss->target->targetipstr()); + adjust_timing = false; + } + adjust_ping = false; + } + /* Do not slow down if + 1) we are in --defeat-rst-ratelimit mode + 2) the new state is closed + 3) this is not a UDP scan (other scans where noresp_open_scan is true + aren't possible with the --defeat-rst-ratelimit option) + We don't care if it's closed because of a RST or a timeout + because they both mean the same thing. */ + if (rcvdtime != NULL + && o.defeat_rst_ratelimit && newstate == PORT_CLOSED + && !USI->noresp_open_scan) { + if (probe->get_tryno() > 0) + adjust_timing = false; + adjust_ping = false; + } + /* Do not slow down if + 1) we are in --defeat-icmp-ratelimit mode + 2) the new state is closed or filtered + 3) this is a UDP scan + We don't want to adjust timing when we get ICMP response, as the host might + be ratelimiting them. E.g. the port is actually closed, but the host ratelimiting + ICMP responses so we had to retransmit the probe several times in order to + match the (slow) rate limit that the target is using for responses. We + do not want to waste time on such ports. + On the other hand if the port is detected to be open it is a good idea to + adjust timing as we could have done retransmissions due to conjested network */ + if (rcvdtime != NULL + && o.defeat_icmp_ratelimit + && (newstate == PORT_CLOSED || newstate == PORT_FILTERED) + && USI->udp_scan) { + if (probe->get_tryno() > 0) + adjust_timing = false; + adjust_ping = false; + } + + if (adjust_timing) { + ultrascan_adjust_timing(USI, hss, probe, rcvdtime); + + if (rcvdtime != NULL && probe->get_tryno() > hss->max_successful_tryno) { + /* We got a positive response to a higher tryno than we've seen so far. */ + hss->max_successful_tryno = probe->get_tryno(); + if (o.debugging) + log_write(LOG_STDOUT, "Increased max_successful_tryno for %s to %d (packet drop)\n", hss->target->targetipstr(), hss->max_successful_tryno); + if (hss->max_successful_tryno > ((o.timing_level >= 4) ? 4 : 3)) { + unsigned int olddelay = hss->sdn.delayms; + hss->boostScanDelay(); + if (o.verbose && hss->sdn.delayms != olddelay) + log_write(LOG_STDOUT, "Increasing send delay for %s from %d to %d due to max_successful_tryno increase to %d\n", + hss->target->targetipstr(), olddelay, hss->sdn.delayms, + hss->max_successful_tryno); + } + } + } + + /* If this probe received a positive response, consider making it the new + timing ping probe. */ + if (rcvdtime != NULL && adjust_ping + && pingprobe_is_better(probe->pspec(), newstate, &hss->target->pingprobe, hss->target->pingprobe_state)) { + if (o.debugging > 1) { + char buf[64]; + probespec2ascii(probe->pspec(), buf, sizeof(buf)); + log_write(LOG_PLAIN, "Changing ping technique for %s to %s\n", hss->target->targetipstr(), buf); + } + hss->target->pingprobe = *probe->pspec(); + hss->target->pingprobe_state = newstate; + } + + hss->destroyOutstandingProbe(probeI); +} + +static void sendNextScanProbe(UltraScanInfo *USI, HostScanStats *hss) { + probespec pspec; + tryno_t tryno = {0}; + + if (get_next_target_probe(USI, hss, &pspec) == -1) { + fatal("%s: No more probes! Error in Nmap.", __func__); + } + hss->numprobes_sent++; + USI->gstats->probes_sent++; + if (pspec.type == PS_ARP) + sendArpScanProbe(USI, hss, tryno); + else if (pspec.type == PS_ND) + sendNDScanProbe(USI, hss, tryno); + else if (pspec.type == PS_CONNECTTCP) + sendConnectScanProbe(USI, hss, pspec.pd.tcp.dport, tryno); + else if (pspec.type == PS_TCP || pspec.type == PS_UDP + || pspec.type == PS_SCTP || pspec.type == PS_PROTO + || pspec.type == PS_ICMP || pspec.type == PS_ICMPV6) + sendIPScanProbe(USI, hss, &pspec, tryno); + else + assert(0); +} + +static void sendNextRetryStackProbe(UltraScanInfo *USI, HostScanStats *hss) { + assert(!hss->retry_stack.empty()); + probespec pspec; + u8 pspec_tries; + hss->numprobes_sent++; + USI->gstats->probes_sent++; + + pspec = hss->retry_stack.back(); + hss->retry_stack.pop_back(); + pspec_tries = hss->retry_stack_tries.back(); + hss->retry_stack_tries.pop_back(); + + tryno_t tryno = {0}; + tryno.fields.seqnum = pspec_tries + 1; + + if (pspec.type == PS_CONNECTTCP) + sendConnectScanProbe(USI, hss, pspec.pd.tcp.dport, tryno); + else { + assert(pspec.type != PS_ARP && pspec.type != PS_ND); + sendIPScanProbe(USI, hss, &pspec, tryno); + } +} + +static void doAnyNewProbes(UltraScanInfo *USI) { + HostScanStats *hss, *unableToSend; + + gettimeofday(&USI->now, NULL); + + /* Loop around the list of incomplete hosts and send a probe to each if + appropriate. Stop once we've been all the way through the list without + sending a probe. */ + unableToSend = NULL; + hss = USI->nextIncompleteHost(); + while (hss != NULL && hss != unableToSend && USI->gstats->sendOK(NULL)) { + if (hss->freshPortsLeft() && hss->sendOK(NULL)) { + ultrascan_host_timeout_init(USI, hss); + sendNextScanProbe(USI, hss); + unableToSend = NULL; + } else if (unableToSend == NULL) { + /* Mark this as the first host we were not able to send to so we can break + when we see it again. */ + unableToSend = hss; + } + hss = USI->nextIncompleteHost(); + } +} + +static void doAnyRetryStackRetransmits(UltraScanInfo *USI) { + HostScanStats *hss, *unableToSend; + + gettimeofday(&USI->now, NULL); + + /* Loop around the list of incomplete hosts and send a probe to each if + appropriate. Stop once we've been all the way through the list without + sending a probe. */ + unableToSend = NULL; + hss = USI->nextIncompleteHost(); + while (hss != NULL && hss != unableToSend && USI->gstats->sendOK(NULL)) { + if (!hss->retry_stack.empty() && hss->sendOK(NULL)) { + sendNextRetryStackProbe(USI, hss); + unableToSend = NULL; + } else if (unableToSend == NULL) { + /* Mark this as the first host we were not able to send to so we can break + when we see it again. */ + unableToSend = hss; + } + hss = USI->nextIncompleteHost(); + } +} + +/* Sends a ping probe to the host. Assumes that caller has already + checked that sending is OK w/congestion control and that pingprobe is + available */ +static void sendPingProbe(UltraScanInfo *USI, HostScanStats *hss) { + tryno_t tryno = {0}; + tryno.fields.isPing = 1; + tryno.fields.seqnum = hss->nextPingSeq(); + + probespec *pingprobe = &hss->target->pingprobe; + switch (pingprobe->type) { + case PS_CONNECTTCP: + sendConnectScanProbe(USI, hss, pingprobe->pd.tcp.dport, tryno); + break; + case PS_TCP: + case PS_UDP: + case PS_SCTP: + case PS_PROTO: + case PS_ICMP: + sendIPScanProbe(USI, hss, pingprobe, tryno); + break; + case PS_ARP: + sendArpScanProbe(USI, hss, tryno); + break; + case PS_ND: + sendNDScanProbe(USI, hss, tryno); + break; + default: + assert(0); + } + if (o.debugging > 1) { + char tmpbuf[64]; + log_write(LOG_PLAIN, "Ultrascan PING SENT to %s [%s]\n", hss->target->targetipstr(), + probespec2ascii(pingprobe, tmpbuf, sizeof(tmpbuf))); + } + USI->gstats->probes_sent++; +} + +static void sendGlobalPingProbe(UltraScanInfo *USI) { + HostScanStats *hss; + + hss = USI->gstats->pinghost; + assert(hss != NULL); + + if (o.debugging > 1) { + char tmpbuf[64]; + log_write(LOG_PLAIN, "Ultrascan GLOBAL PING SENT to %s [%s]\n", hss->target->targetipstr(), + probespec2ascii(&hss->target->pingprobe, tmpbuf, sizeof(tmpbuf))); + } + sendPingProbe(USI, hss); +} + +static void doAnyPings(UltraScanInfo *USI) { + std::multiset::iterator hostI; + HostScanStats *hss = NULL; + + gettimeofday(&USI->now, NULL); + /* First single host pings */ + for (hostI = USI->incompleteHosts.begin(); + hostI != USI->incompleteHosts.end(); hostI++) { + hss = *hostI; + if (hss->target->pingprobe.type != PS_NONE && + hss->rld.rld_waiting == false && + hss->numprobes_sent >= hss->lastping_sent_numprobes + 10 && + TIMEVAL_SUBTRACT(USI->now, hss->lastrcvd) > USI->perf.pingtime && + TIMEVAL_SUBTRACT(USI->now, hss->lastping_sent) > USI->perf.pingtime && + USI->gstats->sendOK(NULL) && hss->sendOK(NULL)) { + sendPingProbe(USI, hss); + hss->lastping_sent = USI->now; + hss->lastping_sent_numprobes = hss->numprobes_sent; + } + } + + /* Next come global pings. We never send more than one of these at at time. */ + if (USI->gstats->pinghost != NULL && + USI->gstats->pinghost->target->pingprobe.type != PS_NONE && + USI->gstats->pinghost->num_probes_active == 0 && + USI->gstats->probes_sent >= USI->gstats->lastping_sent_numprobes + 20 && + TIMEVAL_SUBTRACT(USI->now, USI->gstats->lastrcvd) > USI->perf.pingtime && + TIMEVAL_SUBTRACT(USI->now, USI->gstats->lastping_sent) > USI->perf.pingtime && + USI->gstats->sendOK(NULL)) { + sendGlobalPingProbe(USI); + USI->gstats->lastping_sent = USI->now; + USI->gstats->lastping_sent_numprobes = USI->gstats->probes_sent; + } +} + +/* Retransmit one probe that has presumably been timed out. Only does + retransmission, does not mark the probe timed out and such. */ +static void retransmitProbe(UltraScanInfo *USI, HostScanStats *hss, + UltraProbe *probe) { + UltraProbe *newProbe = NULL; + tryno_t tryno = probe->tryno; + tryno.fields.seqnum++; + if (probe->type == UltraProbe::UP_IP) { + if (USI->prot_scan || USI->ptech.rawprotoscan) + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), tryno); + else if (probe->protocol() == IPPROTO_TCP) { + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), tryno); + } else if (probe->protocol() == IPPROTO_UDP) { + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), tryno); + } else if (probe->protocol() == IPPROTO_SCTP) { + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), tryno); + } else if (probe->protocol() == IPPROTO_ICMP || probe->protocol() == IPPROTO_ICMPV6) { + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), tryno); + } else { + assert(0); + } + } else if (probe->type == UltraProbe::UP_CONNECT) { + newProbe = sendConnectScanProbe(USI, hss, probe->pspec()->pd.tcp.dport, tryno); + } else if (probe->type == UltraProbe::UP_ARP) { + newProbe = sendArpScanProbe(USI, hss, tryno); + } else if (probe->type == UltraProbe::UP_ND) { + newProbe = sendNDScanProbe(USI, hss, tryno); + } else { + /* TODO: Support any other probe types */ + fatal("%s: unsupported probe type %d", __func__, probe->type); + } + if (newProbe) + newProbe->prevSent = probe->sent; + probe->retransmitted = true; + assert(hss->num_probes_waiting_retransmit > 0); + hss->num_probes_waiting_retransmit--; + hss->numprobes_sent++; + USI->gstats->probes_sent++; +} + +struct ProbeCacheNode { + HostScanStats *hss; + std::list::iterator probeI; +}; + +/* Go through the ProbeQueue of each host, identify any + timed out probes, then try to retransmit them as appropriate */ +static void doAnyOutstandingRetransmits(UltraScanInfo *USI) { + std::multiset::iterator hostI; + /* A cache of the last processed probe from each host, to avoid re-examining a + bunch of probes to find the next one that needs to be retransmitted. */ + std::vector probe_cache; + HostScanStats *host = NULL; + UltraProbe *probe = NULL; + int retrans = 0; /* Number of retransmissions during a loop */ + unsigned int maxtries; + + struct timeval tv_start = {0}; + + gettimeofday(&USI->now, NULL); + + if (o.debugging) + tv_start = USI->now; + + probe_cache.reserve(USI->numIncompleteHosts()); + for (hostI = USI->incompleteHosts.begin(); + hostI != USI->incompleteHosts.end(); + hostI++) { + struct ProbeCacheNode pcn; + pcn.hss = *hostI; + /* Skip this host if it has nothing to send. */ + if (pcn.hss->num_probes_active == 0 + && pcn.hss->num_probes_waiting_retransmit == 0) + continue; + assert(!pcn.hss->probes_outstanding.empty()); + pcn.probeI = pcn.hss->probes_outstanding.end(); + probe_cache.push_back(pcn); + } + /* Loop until we get through all the hosts without a retransmit or we're not + OK to send any more. */ + do { + retrans = 0; + for (std::vector::iterator pci = probe_cache.begin(); + pci != probe_cache.end() && USI->gstats->sendOK(NULL); + pci++) { + host = pci->hss; + std::list::iterator &probeI = pci->probeI; + /* Skip this host if it has nothing to send. */ + if ((host->num_probes_active == 0 + && host->num_probes_waiting_retransmit == 0)) + continue; + if (!host->sendOK(NULL)) + continue; + assert(!host->probes_outstanding.empty()); + + maxtries = host->allowedTryno(NULL, NULL); + do { + probeI--; + probe = *probeI; + if (probe->retransmitted || probe->isPing()) { + // Don't retransmit these + continue; + } + // Retransmit if timed out and there are still tries remaining + if (probe->timedout && maxtries > probe->get_tryno()) { + /* For rate limit detection, we delay the first time a new tryno + is seen, as long as we are scanning at least 2 ports */ + if (probe->get_tryno() + 1 > (int) host->rld.max_tryno_sent && + (USI->gstats->numprobes > 1 || USI->ping_scan_arp || USI->ping_scan_nd)) { + host->rld.max_tryno_sent = probe->get_tryno() + 1; + host->rld.rld_waiting = true; + TIMEVAL_MSEC_ADD(host->rld.rld_waittime, USI->now, RLD_TIME_MS); + } else { + host->rld.rld_waiting = false; + retransmitProbe(USI, host, probe); + retrans++; + } + break; /* I only do one probe per host for now to spread load */ + } + } while (probeI != host->probes_outstanding.begin()); + + /* Wrap the probe iterator around. */ + if (probeI == host->probes_outstanding.begin()) + probeI = host->probes_outstanding.end(); + } + } while (USI->gstats->sendOK(NULL) && retrans != 0); + + gettimeofday(&USI->now, NULL); + if (o.debugging) { + long tv_diff = TIMEVAL_MSEC_SUBTRACT(USI->now, tv_start); + if (tv_diff > 30) + log_write(LOG_PLAIN, "%s took %lims\n", __func__, tv_diff); + } +} + +/* Print occasional remaining time estimates, as well as + debugging information */ +static void printAnyStats(UltraScanInfo *USI) { + std::multiset::const_iterator hostI; + const HostScanStats *hss; + struct ultra_timing_vals hosttm; + + /* Print debugging states for each host being scanned */ + if (o.debugging > 2) { + log_write(LOG_PLAIN, "**TIMING STATS** (%.4fs): IP, probes active/freshportsleft/retry_stack/outstanding/retranwait/onbench, cwnd/ssthresh/delay, timeout/srtt/rttvar/\n", o.TimeSinceStart()); + log_write(LOG_PLAIN, " Groupstats (%d/%d incomplete): %d/*/*/*/*/* %.2f/%d/* %d/%d/%d\n", + USI->numIncompleteHosts(), USI->numInitialHosts(), + USI->gstats->num_probes_active, USI->gstats->timing.cwnd, + USI->gstats->timing.ssthresh, USI->gstats->to.timeout, + USI->gstats->to.srtt, USI->gstats->to.rttvar); + + if (o.debugging > 3) { + for (hostI = USI->incompleteHosts.begin(); + hostI != USI->incompleteHosts.end(); hostI++) { + hss = *hostI; + hss->getTiming(&hosttm); + log_write(LOG_PLAIN, " %s: %d/%d/%d/%d/%d/%d %.2f/%d/%d %li/%d/%d\n", hss->target->targetipstr(), + hss->num_probes_active, hss->numFreshPortsLeft(), + (int) hss->retry_stack.size(), + hss->num_probes_outstanding(), + hss->num_probes_waiting_retransmit, (int) hss->probe_bench.size(), + hosttm.cwnd, hosttm.ssthresh, hss->sdn.delayms, + hss->probeTimeout(), hss->target->to.srtt, + hss->target->to.rttvar); + } + } + + USI->log_current_rates(LOG_PLAIN); + USI->log_overall_rates(LOG_PLAIN); + } + + if (USI->SPM->mayBePrinted(&USI->now)) + USI->SPM->printStatsIfNecessary(USI->getCompletionFraction(), &USI->now); +} + +static void waitForResponses(UltraScanInfo *USI) { + struct timeval stime; + bool gotone; + gettimeofday(&USI->now, NULL); + USI->gstats->last_wait = USI->now; + USI->gstats->probes_sent_at_last_wait = USI->gstats->probes_sent; + + do { + gotone = false; + USI->sendOK(&stime); + if (USI->ping_scan_arp) { + gotone = get_arp_result(USI, &stime); + } else if (USI->ping_scan_nd) { + gotone = get_ns_result(USI, &stime); + } else if (USI->ping_scan) { + if (USI->pd) + gotone = get_ping_pcap_result(USI, &stime); + if (!gotone && USI->ptech.connecttcpscan) + gotone = do_one_select_round(USI, &stime); + } else if (USI->pd) { + gotone = get_pcap_result(USI, &stime); + } else if (USI->scantype == CONNECT_SCAN) { + gotone = do_one_select_round(USI, &stime); + } else assert(0); + } while (gotone && USI->gstats->num_probes_active > 0); + + gettimeofday(&USI->now, NULL); + USI->gstats->last_wait = USI->now; +} + +/* Go through the data structures, making appropriate changes (such as expiring + probes, noting when hosts are complete, etc. */ +static void processData(UltraScanInfo *USI) { + std::multiset::iterator hostI; + std::list::iterator probeI, nextProbeI; + HostScanStats *host = NULL; + UltraProbe *probe = NULL; + unsigned int maxtries = 0; + int expire_us = 0; + + bool tryno_capped = false, tryno_mayincrease = false; + struct timeval tv_start = {0}; + + gettimeofday(&USI->now, NULL); + + if (o.debugging) + tv_start = USI->now; + + /* First go through hosts and remove any completed ones from incompleteHosts */ + USI->removeCompletedHosts(); + if (USI->incompleteHostsEmpty()) + return; + + /* Run through probe lists to: + 1) Mark timedout entries as such + 2) Remove long-expired and retransmitted entries + 3) Detect if we are done (we may just have a bunch of probes + sitting around waiting to see if another round of + retransmissions will be required). + */ + for (hostI = USI->incompleteHosts.begin(); + hostI != USI->incompleteHosts.end(); hostI++) { + host = *hostI; + /* Look for timedout or long expired entries */ + maxtries = host->allowedTryno(&tryno_capped, &tryno_mayincrease); + + /* Should we dump everyone off the bench? */ + if (!host->probe_bench.empty()) { + if (maxtries == host->bench_tryno && !tryno_mayincrease) { + /* We'll never need to retransmit these suckers! So they can + be treated as done */ + host->dismissBench(); + } else if (maxtries > host->bench_tryno) { + // These fellows may be retransmitted now that maxtries has increased + host->retransmitBench(); + } + } + + for (probeI = host->probes_outstanding.begin(); + probeI != host->probes_outstanding.end(); probeI = nextProbeI) { + nextProbeI = probeI; + nextProbeI++; + probe = *probeI; + + // give up completely after this long + expire_us = host->probeExpireTime(probe); + + if (!probe->timedout && TIMEVAL_SUBTRACT(USI->now, probe->sent) > + (long) host->probeTimeout()) { + host->markProbeTimedout(probeI); + /* Once we've timed out a probe, skip it for this round of processData. + We don't want it to move to the bench or anything until the other + functions have had a chance to see that it's timed out. In + particular, timing out a probe may mean that the tryno can no longer + increase, which would make the logic below incorrect. */ + continue; + } + + if (!probe->isPing() && probe->timedout && !probe->retransmitted) { + if (!tryno_mayincrease && probe->get_tryno() >= maxtries) { + if (tryno_capped && !host->retry_capped_warned) { + log_write(LOG_PLAIN, "Warning: %s giving up on port because" + " retransmission cap hit (%d).\n", host->target->targetipstr(), + probe->get_tryno()); + host->retry_capped_warned = true; + } + if (USI->ping_scan) { + ultrascan_host_probe_update(USI, host, probeI, HOST_DOWN, NULL); + if (host->target->reason.reason_id == ER_UNKNOWN) + host->target->reason.reason_id = ER_NORESPONSE; + } else { + /* No ultrascan_port_probe_update because that allocates a Port + object; the default port state as set by setDefaultPortState + handles these no-response ports. */ + host->destroyOutstandingProbe(probeI); + } + continue; + } else if (probe->get_tryno() >= maxtries && + TIMEVAL_SUBTRACT(USI->now, probe->sent) > expire_us) { + assert(probe->get_tryno() == maxtries); + /* Move it to the bench until it is needed (maxtries + increases or is capped */ + host->moveProbeToBench(probeI); + continue; + } + } + + if ((probe->isPing() || (probe->timedout && probe->retransmitted)) && + TIMEVAL_SUBTRACT(USI->now, probe->sent) > expire_us) { + host->destroyOutstandingProbe(probeI); + continue; + } + } + } + + /* In case any hosts were completed during this run */ + USI->removeCompletedHosts(); + + /* Check for expired global pings. */ + HostScanStats *pinghost = USI->gstats->pinghost; + if (pinghost != NULL) { + for (probeI = pinghost->probes_outstanding.begin(); + probeI != pinghost->probes_outstanding.end(); + probeI = nextProbeI) { + nextProbeI = probeI; + nextProbeI++; + /* If a global ping probe times out, we want to get rid of it so a new + host can take its place. */ + if ((*probeI)->isPing() + && TIMEVAL_SUBTRACT(USI->now, (*probeI)->sent) > (long) pinghost->probeTimeout()) { + if (o.debugging) + log_write(LOG_STDOUT, "Destroying timed-out global ping from %s.\n", pinghost->target->targetipstr()); + /* ultrascan_ping_update destroys the probe. */ + ultrascan_ping_update(USI, pinghost, probeI, NULL); + } + } + } + + gettimeofday(&USI->now, NULL); + if (o.debugging) { + long tv_diff = TIMEVAL_MSEC_SUBTRACT(USI->now, tv_start); + if (tv_diff > 30) + log_write(LOG_PLAIN, "%s took %lims\n", __func__, tv_diff); + } +} + +/* 3rd generation Nmap scanning function. Handles most Nmap port scan types. + + The parameter to gives group timing information, and if it is not NULL, + changed timing information will be stored in it when the function returns. It + exists so timing can be shared across invocations of this function. If to is + NULL (its default value), a default timeout_info will be used. */ +void ultra_scan(std::vector &Targets, const struct scan_lists *ports, + stype scantype, struct timeout_info *to) { + o.current_scantype = scantype; + + if (Targets.size() == 0) { + return; + } + +#ifdef WIN32 + if (g_has_npcap_loopback == 0 && scantype != CONNECT_SCAN && Targets[0]->ifType() == devt_loopback) { + log_write(LOG_STDOUT, "Skipping %s against %s because Windows does not support scanning your own machine (localhost) this way.\n", scantype2str(scantype), Targets[0]->NameIP()); + return; + } +#endif + + // Set the variable for status printing + o.numhosts_scanning = Targets.size(); + + UltraScanInfo USI(Targets, ports, scantype); + + /* Load up _all_ payloads into a mapped table. Only needed for raw scans. */ + if (USI.udp_scan || (USI.ping_scan && USI.ptech.rawudpscan) ) { + init_payloads(); + } + + if (USI.gstats->numprobes <= 0) { + if (o.debugging) { + log_write(LOG_STDOUT, "Skipping %s: no probes to send\n", scantype2str(scantype)); + } + return; + } + + /* Use the requested timeouts. */ + if (to != NULL) + USI.gstats->to = *to; + + if (o.verbose) { + char targetstr[128]; + bool plural = (Targets.size() != 1); + if (!plural) { + (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr)); + } else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) Targets.size()); + log_write(LOG_STDOUT, "Scanning %s [%d port%s%s]\n", targetstr, USI.gstats->numprobes, (USI.gstats->numprobes != 1) ? "s" : "", plural ? "/host" : ""); + } + + if (USI.isRawScan()) + begin_sniffer(&USI, Targets); + /* Otherwise, no sniffer needed! */ + + while (!USI.incompleteHostsEmpty()) { +#ifdef WIN32 + // Reset system idle timer to avoid going to sleep + SetThreadExecutionState(ES_SYSTEM_REQUIRED); +#endif + doAnyPings(&USI); + doAnyOutstandingRetransmits(&USI); // Retransmits from probes_outstanding + /* Retransmits from retry_stack -- goes after OutstandingRetransmits for + memory consumption reasons */ + doAnyRetryStackRetransmits(&USI); + doAnyNewProbes(&USI); + gettimeofday(&USI.now, NULL); + // printf("TRACE: Finished doAnyNewProbes() at %.4fs\n", o.TimeSinceStartMS(&USI.now) / 1000.0); + printAnyStats(&USI); + waitForResponses(&USI); + gettimeofday(&USI.now, NULL); + // printf("TRACE: Finished waitForResponses() at %.4fs\n", o.TimeSinceStartMS(&USI.now) / 1000.0); + processData(&USI); + + if (keyWasPressed()) { + // This prints something like + // SYN Stealth Scan Timing: About 1.14% done; ETC: 15:01 (0:43:23 remaining); + USI.SPM->printStats(USI.getCompletionFraction(), NULL); + if (o.debugging) { + /* Don't update when getting the current rates, otherwise we can get + anomalies (rates are too low) from having just done a potentially + long waitForResponses without sending any packets. */ + USI.log_current_rates(LOG_STDOUT, false); + } + + log_flush(LOG_STDOUT); + } + } + + USI.send_rate_meter.stop(&USI.now); + + /* Save the computed timeouts. */ + if (to != NULL) + *to = USI.gstats->to; + + if (o.verbose) { + char additional_info[128]; + if (USI.gstats->num_hosts_timedout == 0) + if (USI.ping_scan) { + Snprintf(additional_info, sizeof(additional_info), "%lu total hosts", + (unsigned long) Targets.size()); + } else { + Snprintf(additional_info, sizeof(additional_info), "%lu total ports", + (unsigned long) USI.gstats->numprobes * Targets.size()); + } + else Snprintf(additional_info, sizeof(additional_info), "%d %s timed out", + USI.gstats->num_hosts_timedout, + (USI.gstats->num_hosts_timedout == 1) ? "host" : "hosts"); + USI.SPM->endTask(NULL, additional_info); + } + if (o.debugging) + USI.log_overall_rates(LOG_STDOUT); + + if (o.debugging > 2 && USI.pd != NULL) + pcap_print_stats(LOG_PLAIN, USI.pd); +} -- cgit v1.2.3