diff options
Diffstat (limited to 'osscan2.cc')
-rw-r--r-- | osscan2.cc | 3548 |
1 files changed, 3548 insertions, 0 deletions
diff --git a/osscan2.cc b/osscan2.cc new file mode 100644 index 0000000..c9cae03 --- /dev/null +++ b/osscan2.cc @@ -0,0 +1,3548 @@ + +/*************************************************************************** + * osscan2.cc -- Routines used for 2nd Generation OS detection via * + * TCP/IP fingerprinting. * For more information on how this works in * + * Nmap, see https://nmap.org/osdetect/ * + * * + ***********************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$ */ + +#include "osscan.h" +#include "osscan2.h" +#include "timing.h" +#include "NmapOps.h" +#include "tcpip.h" +#include "Target.h" +#include "utils.h" +#include "nmap_error.h" +#include "FPEngine.h" +#include "FingerPrintResults.h" +#include <dnet.h> + +#include "struct_ip.h" + +#include <list> +#include <math.h> + +extern NmapOps o; +#ifdef WIN32 +/* from libdnet's intf-win32.c */ +extern "C" int g_has_npcap_loopback; +#endif + +/* 8 options: + * 0~5: six options for SEQ/OPS/WIN/T1 probes. + * 6: ECN probe. + * 7-12: T2~T7 probes. + * + * option 0: WScale (10), Nop, MSS (1460), Timestamp, SackP + * option 1: MSS (1400), WScale (0), SackP, T(0xFFFFFFFF,0x0), EOL + * option 2: T(0xFFFFFFFF, 0x0), Nop, Nop, WScale (5), Nop, MSS (640) + * option 3: SackP, T(0xFFFFFFFF,0x0), WScale (10), EOL + * option 4: MSS (536), SackP, T(0xFFFFFFFF,0x0), WScale (10), EOL + * option 5: MSS (265), SackP, T(0xFFFFFFFF,0x0) + * option 6: WScale (10), Nop, MSS (1460), SackP, Nop, Nop + * option 7-11: WScale (10), Nop, MSS (265), T(0xFFFFFFFF,0x0), SackP + * option 12: WScale (15), Nop, MSS (265), T(0xFFFFFFFF,0x0), SackP + */ +static struct { + u8* val; + int len; +} prbOpts[] = { + {(u8*) "\x03\x03\x0A\x01\x02\x04\x05\xb4\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20}, + {(u8*) "\x02\x04\x05\x78\x03\x03\x00\x04\x02\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x00", 20}, + {(u8*) "\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x01\x01\x03\x03\x05\x01\x02\x04\x02\x80", 20}, + {(u8*) "\x04\x02\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x03\x03\x0A\x00", 16}, + {(u8*) "\x02\x04\x02\x18\x04\x02\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x03\x03\x0A\x00", 20}, + {(u8*) "\x02\x04\x01\x09\x04\x02\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00", 16}, + {(u8*) "\x03\x03\x0A\x01\x02\x04\x05\xb4\x04\x02\x01\x01", 12}, + {(u8*) "\x03\x03\x0A\x01\x02\x04\x01\x09\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20}, + {(u8*) "\x03\x03\x0A\x01\x02\x04\x01\x09\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20}, + {(u8*) "\x03\x03\x0A\x01\x02\x04\x01\x09\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20}, + {(u8*) "\x03\x03\x0A\x01\x02\x04\x01\x09\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20}, + {(u8*) "\x03\x03\x0A\x01\x02\x04\x01\x09\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20}, + {(u8*) "\x03\x03\x0f\x01\x02\x04\x01\x09\x08\x0A\xff\xff\xff\xff\x00\x00\x00\x00\x04\x02", 20} +}; + +/* TCP Window sizes. Numbering is the same as for prbOpts[] */ +u16 prbWindowSz[] = { 1, 63, 4, 4, 16, 512, 3, 128, 256, 1024, 31337, 32768, 65535 }; + +/* Current time. It is globally accessible so it can save calls to gettimeofday() */ +static struct timeval now; + +/* Global to store performance info */ +static struct scan_performance_vars perf; + + +/****************************************************************************** + * Miscellaneous functions * + ******************************************************************************/ + +/* Return a value based on the IP ID sequence generation + class (one of the IPID_SEQ_* constants). If ipid_seqclass is such that the + test result should be omitted, the function returns NULL. */ +static const char *make_aval_ipid_seq(int ipid_seqclass, u32 ipids[NUM_SEQ_SAMPLES], + HostOsScanStats *hss) { + switch (ipid_seqclass) { + case IPID_SEQ_CONSTANT: + return hss->target->FPR->cp_hex(ipids[0]); + break; + case IPID_SEQ_INCR_BY_2: + case IPID_SEQ_INCR: + return "I"; + break; + case IPID_SEQ_BROKEN_INCR: + return "BI"; + break; + case IPID_SEQ_RPI: + return "RI"; + break; + case IPID_SEQ_RD: + return "RD"; + break; + case IPID_SEQ_ZERO: + return "Z"; + break; + default: + /* Signal to omit test result. */ + return NULL; + break; + } +} + + +/* Returns a guess about the original TTL based on an observed TTL value. + * This function assumes that the target from which we received the packet was + * less than 32 hops away. Also, note that although some systems use an + * initial TTL of 60, this function rounds that to 64, as both values + * cannot be reliably distinguished based on a simple observed hop count. */ +int get_initial_ttl_guess(u8 ttl) { + if (ttl <= 32) + return 32; + else if (ttl <= 64) + return 64; + else if (ttl <= 128) + return 128; + else + return 255; +} + + +/* This function takes an array of "numSamples" IP IDs and analyzes + them to determine their sequence classification. It returns + one of the IPID_SEQ_* classifications defined in nmap.h . If the + function cannot determine the sequence, IPID_SEQ_UNKNOWN is returned. + This islocalhost argument is a boolean specifying whether these + numbers were generated by scanning localhost. */ +int identify_sequence(int numSamples, u32 *ipid_diffs, int islocalhost) { + int i, j, k, l; + + if (islocalhost) { + int allgto = 1; /* ALL diffs greater than one */ + + for (i = 0; i < numSamples - 1; i++) { + if (ipid_diffs[i] < 2) { + allgto = 0; break; + } + } + + if (allgto) { + for (i = 0; i < numSamples - 1; i++) { + if (ipid_diffs[i] % 256 == 0) /* Stupid MS */ + ipid_diffs[i] -= 256; + else + ipid_diffs[i]--; /* Because on localhost the RST sent back use an IPID */ + } + } + } + + /* Constant */ + j = 1; /* j is a flag meaning "all differences seen are zero" */ + for (i = 0; i < numSamples - 1; i++) { + if (ipid_diffs[i] != 0) { + j = 0; + break; + } + } + if (j) { + return IPID_SEQ_CONSTANT; + } + + /* Random Positive Increments */ + for (i = 0; i < numSamples - 1; i++) { + if (ipid_diffs[i] > 1000 && + (ipid_diffs[i] % 256 != 0 || + (ipid_diffs[i] % 256 == 0 && ipid_diffs[i] >= 25600))) { + return IPID_SEQ_RPI; + } + } + + j = 1; /* j is a flag meaning "all differences seen are < 10" */ + k = 1; /* k is a flag meaning "all difference seen are multiples of 256 and + * no greater than 5120" */ + l = 1; /* l is a flag meaning "all differences are multiples of 2" */ + for (i = 0; i < numSamples - 1; i++) { + if (k && (ipid_diffs[i] > 5120 || ipid_diffs[i] % 256 != 0)) { + k = 0; + } + + if (l && ipid_diffs[i] % 2 != 0) { + l = 0; + } + + if (j && ipid_diffs[i] > 9) { + j = 0; + } + } + + /* Broken Increment */ + if (k == 1) { + return IPID_SEQ_BROKEN_INCR; + } + + /* Incrementing by 2 */ + if (l == 1) + return IPID_SEQ_INCR_BY_2; + + /* Incremental by 1 */ + if (j == 1) + return IPID_SEQ_INCR; + + return IPID_SEQ_UNKNOWN; +} + +/* Calculate the distances between the ipids and write them + into the ipid_diffs array. If the sequence class can be determined + immediately, return it; otherwise return -1 */ +int get_diffs(u32 *ipid_diffs, int numSamples, const u32 *ipids, int islocalhost) { + int i; + bool allipideqz = true; + + if (numSamples < 2) + return IPID_SEQ_UNKNOWN; + + for (i = 1; i < numSamples; i++) { + if (ipids[i - 1] != 0 || ipids[i] != 0) + allipideqz = false; /* All IP.ID values do *NOT* equal zero */ + + ipid_diffs[i - 1] = ipids[i] - ipids[i - 1]; + + /* Random */ + if (numSamples > 2 && ipid_diffs[i - 1] > 20000) + return IPID_SEQ_RD; + } + + if (allipideqz) { + return IPID_SEQ_ZERO; + } + else { + return -1; + } + +} + +/* Indentify the ipid sequence for 32-bit IPID values (IPv6) */ +int get_ipid_sequence_32(int numSamples, const u32 *ipids, int islocalhost) { + int ipid_seq = IPID_SEQ_UNKNOWN; + u32 ipid_diffs[32]; + assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); + ipid_seq = get_diffs(ipid_diffs, numSamples, ipids, islocalhost); + if (ipid_seq < 0) { + return identify_sequence(numSamples, ipid_diffs, islocalhost); + } + else { + return ipid_seq; + } +} + +/* Indentify the ipid sequence for 16-bit IPID values (IPv4) */ +int get_ipid_sequence_16(int numSamples, const u32 *ipids, int islocalhost) { + int i; + int ipid_seq = IPID_SEQ_UNKNOWN; + u32 ipid_diffs[32]; + assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); + ipid_seq = get_diffs(ipid_diffs, numSamples, ipids, islocalhost); + /* AND with 0xffff so that in case the 16 bit counter was + * flipped over we still have a continuous sequence */ + for (i = 0; i < numSamples; i++) { + ipid_diffs[i] = ipid_diffs[i] & 0xffff; + } + if (ipid_seq < 0) { + return identify_sequence(numSamples, ipid_diffs, islocalhost); + } + else { + return ipid_seq; + } +} + +/* Convert a TCP sequence prediction difficulty index like 1264386 + into a difficulty string like "Worthy Challenge */ +const char *seqidx2difficultystr(unsigned long idx) { + return (idx < 3) ? "Trivial joke" : (idx < 6) ? "Easy" : (idx < 11) ? "Medium" : (idx < 12) ? "Formidable" : (idx < 16) ? "Worthy challenge" : "Good luck!"; +} + +const char *ipidclass2ascii(int seqclass) { + switch (seqclass) { + case IPID_SEQ_CONSTANT: + return "Duplicated ipid (!)"; + case IPID_SEQ_INCR: + return "Incremental"; + case IPID_SEQ_INCR_BY_2: + return "Incrementing by 2"; + case IPID_SEQ_BROKEN_INCR: + return "Broken little-endian incremental"; + case IPID_SEQ_RD: + return "Randomized"; + case IPID_SEQ_RPI: + return "Random positive increments"; + case IPID_SEQ_ZERO: + return "All zeros"; + case IPID_SEQ_UNKNOWN: + return "Busy server or unknown class"; + default: + return "ERROR, WTF?"; + } +} + +const char *tsseqclass2ascii(int seqclass) { + switch (seqclass) { + case TS_SEQ_ZERO: + return "zero timestamp"; + case TS_SEQ_2HZ: + return "2HZ"; + case TS_SEQ_100HZ: + return "100HZ"; + case TS_SEQ_1000HZ: + return "1000HZ"; + case TS_SEQ_OTHER_NUM: + return "other"; + case TS_SEQ_UNSUPPORTED: + return "none returned (unsupported)"; + case TS_SEQ_UNKNOWN: + return "unknown class"; + default: + return "ERROR, WTF?"; + } +} + + +/** Sets up the pcap descriptor in HOS (obtains a descriptor and sets the + * appropriate BPF filter, based on the supplied list of targets). */ +static void begin_sniffer(HostOsScan *HOS, std::vector<Target *> &Targets) { + char pcap_filter[2048]; + /* 20 IPv6 addresses is max (45 byte addy + 14 (" or src host ")) * 20 == 1180 */ + char dst_hosts[1200]; + int filterlen = 0; + int len; + unsigned int targetno; + bool doIndividual = Targets.size() <= 20; // Don't bother IP limits if scanning huge # of hosts + pcap_filter[0] = '\0'; + + /* If we have 20 or less targets, build a list of addresses so we can set + * an explicit BPF filter */ + if (doIndividual) { + for (targetno = 0; targetno < Targets.size(); targetno++) { + len = Snprintf(dst_hosts + filterlen, + sizeof(dst_hosts) - filterlen, + "%ssrc host %s", (targetno == 0)? "" : " or ", + Targets[targetno]->targetipstr()); + if (len < 0 || len + filterlen >= (int) sizeof(dst_hosts)) + fatal("ran out of space in dst_hosts"); + filterlen += len; + } + len = Snprintf(dst_hosts + filterlen, sizeof(dst_hosts) - filterlen, ")))"); + if (len < 0 || len + filterlen >= (int) sizeof(dst_hosts)) + fatal("ran out of space in dst_hosts"); + } + + /* Open a network interface for packet capture */ + HOS->pd = my_pcap_open_live(Targets[0]->deviceName(), 8192, + o.spoofsource ? 1 : 0, pcap_selectable_fd_valid() ? 200 : 2); + if (HOS->pd == NULL) + fatal("%s", PCAP_OPEN_ERRMSG); + + struct sockaddr_storage ss = Targets[0]->source(); + /* Build the final BPF filter */ + if (ss.ss_family == AF_INET) { + if (doIndividual) + len = Snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s and (icmp or (tcp and (%s", + inet_ntoa(((struct sockaddr_in *)&ss)->sin_addr), dst_hosts); + else + len = Snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s and (icmp or tcp)", + inet_ntoa(((struct sockaddr_in *)&ss)->sin_addr)); + if (len < 0 || len >= (int) sizeof(pcap_filter)) + fatal("ran out of space in pcap filter"); + + /* Compile and apply the filter to the pcap descriptor */ + if (o.debugging) + log_write(LOG_PLAIN, "Packet capture filter (device %s): %s\n", Targets[0]->deviceFullName(), pcap_filter); + set_pcap_filter(Targets[0]->deviceFullName(), HOS->pd, pcap_filter); + } + + return; +} + + +/* Sets everything up so the current round can be performed. This includes + * reinitializing some variables of the supplied objects and deleting + * some old information. */ +static void startRound(OsScanInfo *OSI, HostOsScan *HOS, int roundNum) { + std::list<HostOsScanInfo *>::iterator hostI; + HostOsScanInfo *hsi = NULL; + + /* Reinitial some parameters of the scan system. */ + HOS->reInitScanSystem(); + + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + if (hsi->FPs[roundNum]) { + delete hsi->FPs[roundNum]; + hsi->FPs[roundNum] = NULL; + } + hsi->hss->initScanStats(); + } +} + +/* Run the sequence generation tests (6 TCP probes sent 100ms apart) */ +static void doSeqTests(OsScanInfo *OSI, HostOsScan *HOS) { + std::list<HostOsScanInfo *>::iterator hostI; + HostOsScanInfo *hsi = NULL; + HostOsScanStats *hss = NULL; + unsigned int unableToSend = 0; /* # of times in a row that hosts were unable to send probe */ + unsigned int expectReplies = 0; + long to_usec = 0; + int timeToSleep = 0; + struct ip *ip = NULL; + struct link_header linkhdr; + struct sockaddr_storage ss; + unsigned int bytes = 0; + struct timeval rcvdtime; + struct timeval stime; + struct timeval tmptv; + bool timedout = false; + bool thisHostGood = false; + bool foundgood = false; + bool goodResponse = false; + int numProbesLeft = 0; + + memset(&stime, 0, sizeof(stime)); + memset(&tmptv, 0, sizeof(tmptv)); + + /* For each host, build a list of sequence probes to send */ + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + hss = hsi->hss; + HOS->buildSeqProbeList(hss); + } + + /* Iterate until we have sent all the probes */ + do { + if (timeToSleep > 0) { + if (o.debugging > 1) + log_write(LOG_PLAIN, "Sleep %dus for next sequence probe\n", timeToSleep); + usleep(timeToSleep); + } + + gettimeofday(&now, NULL); + expectReplies = 0; + unableToSend = 0; + + if (o.debugging > 2) { + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + log_write(LOG_PLAIN, "Host %s. ProbesToSend %d: \tProbesActive %d\n", + hss->target->targetipstr(), hss->numProbesToSend(), + hss->numProbesActive()); + } + } + + /* Send a seq probe to each host. */ + while (unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) { + hsi = OSI->nextIncompleteHost(); + hss = hsi->hss; + gettimeofday(&now, NULL); + if (hss->numProbesToSend()>0 && HOS->hostSeqSendOK(hss, NULL)) { + HOS->sendNextProbe(hss); + expectReplies++; + unableToSend = 0; + } else { + unableToSend++; + } + } + + HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent; + + gettimeofday(&now, NULL); + + /* Count the pcap wait time. */ + if (!HOS->stats->sendOK()) { + TIMEVAL_MSEC_ADD(stime, now, 1000); + + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + if (HOS->nextTimeout((*hostI)->hss, &tmptv)) { + if (TIMEVAL_SUBTRACT(tmptv, stime) < 0) + stime = tmptv; + } + } + } else { + foundgood = false; + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + thisHostGood = HOS->hostSeqSendOK((*hostI)->hss, &tmptv); + if (thisHostGood) { + stime = tmptv; + foundgood = true; + break; + } + + if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) { + stime = tmptv; + foundgood = true; + } + } + } + + do { + to_usec = TIMEVAL_SUBTRACT(stime, now); + if (to_usec < 2000) + to_usec = 2000; + + if (o.debugging > 2) + log_write(LOG_PLAIN, "pcap wait time is %ld.\n", to_usec); + + ip = (struct ip*) readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); + + gettimeofday(&now, NULL); + + if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) { + timedout = true; + break; + } else if (!ip) { + continue; + } + + if (TIMEVAL_SUBTRACT(now, stime) > 200000) { + /* While packets are still being received, I'll be generous and give + an extra 1/5 sec. But we have to draw the line somewhere */ + timedout = true; + } + + if (bytes < (4 * ip->ip_hl) + 4U) + continue; + + memset(&ss, 0, sizeof(ss)); + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; + ss.ss_family = AF_INET; + hsi = OSI->findIncompleteHost(&ss); + if (!hsi) + continue; /* Not from one of our targets. */ + setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); + + goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); + + if (goodResponse) + expectReplies--; + + } while (!timedout && expectReplies > 0); + + /* Remove any timeout hosts during the scan. */ + OSI->removeCompletedHosts(); + + numProbesLeft = 0; + for (hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + HOS->updateActiveSeqProbes(hss); + numProbesLeft += hss->numProbesToSend(); + numProbesLeft += hss->numProbesActive(); + } + + gettimeofday(&now, NULL); + + if (expectReplies == 0) { + timeToSleep = TIMEVAL_SUBTRACT(stime, now); + } else { + timeToSleep = 0; + } + + } while (numProbesLeft > 0); + +} + + +/* TCP, UDP, ICMP Tests */ +static void doTUITests(OsScanInfo *OSI, HostOsScan *HOS) { + std::list<HostOsScanInfo *>::iterator hostI; + HostOsScanInfo *hsi = NULL; + HostOsScanStats *hss = NULL; + unsigned int unableToSend; /* # of times in a row that hosts were unable to send probe */ + unsigned int expectReplies; + long to_usec; + int timeToSleep = 0; + + struct ip *ip = NULL; + struct link_header linkhdr; + struct sockaddr_storage ss; + unsigned int bytes; + struct timeval rcvdtime; + + struct timeval stime, tmptv; + + bool timedout = false; + bool thisHostGood; + bool foundgood; + bool goodResponse; + int numProbesLeft = 0; + + memset(&stime, 0, sizeof(stime)); + memset(&tmptv, 0, sizeof(tmptv)); + + for (hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + hss = hsi->hss; + HOS->buildTUIProbeList(hss); + } + + do { + + if (timeToSleep > 0) { + if (o.debugging > 1) { + log_write(LOG_PLAIN, "Time to sleep %d. Sleeping. \n", timeToSleep); + } + + usleep(timeToSleep); + } + + gettimeofday(&now, NULL); + expectReplies = 0; + unableToSend = 0; + + if (o.debugging > 2) { + for (hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + log_write(LOG_PLAIN, "Host %s. ProbesToSend %d: \tProbesActive %d\n", + hss->target->targetipstr(), hss->numProbesToSend(), + hss->numProbesActive()); + } + } + + while (unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) { + hsi = OSI->nextIncompleteHost(); + hss = hsi->hss; + gettimeofday(&now, NULL); + if (hss->numProbesToSend()>0 && HOS->hostSendOK(hss, NULL)) { + HOS->sendNextProbe(hss); + expectReplies++; + unableToSend = 0; + } else { + unableToSend++; + } + } + + HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent; + + gettimeofday(&now, NULL); + + /* Count the pcap wait time. */ + if (!HOS->stats->sendOK()) { + TIMEVAL_MSEC_ADD(stime, now, 1000); + + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); + hostI++) { + if (HOS->nextTimeout((*hostI)->hss, &tmptv)) { + if (TIMEVAL_SUBTRACT(tmptv, stime) < 0) + stime = tmptv; + } + } + } + else { + foundgood = false; + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + thisHostGood = HOS->hostSendOK((*hostI)->hss, &tmptv); + if (thisHostGood) { + stime = tmptv; + foundgood = true; + break; + } + + if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) { + stime = tmptv; + foundgood = true; + } + } + } + + do { + to_usec = TIMEVAL_SUBTRACT(stime, now); + if (to_usec < 2000) to_usec = 2000; + + if (o.debugging > 2) + log_write(LOG_PLAIN, "pcap wait time is %ld.\n", to_usec); + + ip = (struct ip*) readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); + + gettimeofday(&now, NULL); + + if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) { + timedout = true; + break; + } else if (!ip) { + continue; + } + + if (TIMEVAL_SUBTRACT(now, stime) > 200000) { + /* While packets are still being received, I'll be generous and give + an extra 1/5 sec. But we have to draw the line somewhere */ + timedout = true; + } + + if (bytes < (4 * ip->ip_hl) + 4U) + continue; + + memset(&ss, 0, sizeof(ss)); + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; + ss.ss_family = AF_INET; + hsi = OSI->findIncompleteHost(&ss); + if (!hsi) + continue; /* Not from one of our targets. */ + setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); + + goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); + + if (goodResponse) + expectReplies--; + + } while (!timedout && expectReplies > 0); + + /* Remove any timeout hosts during the scan. */ + OSI->removeCompletedHosts(); + + numProbesLeft = 0; + for (hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + HOS->updateActiveTUIProbes(hss); + numProbesLeft += hss->numProbesToSend(); + numProbesLeft += hss->numProbesActive(); + } + + gettimeofday(&now, NULL); + + if (expectReplies == 0) { + timeToSleep = TIMEVAL_SUBTRACT(stime, now); + } else { + timeToSleep = 0; + } + + } while (numProbesLeft > 0); +} + + +static void endRound(OsScanInfo *OSI, HostOsScan *HOS, int roundNum) { + std::list<HostOsScanInfo *>::iterator hostI; + HostOsScanInfo *hsi = NULL; + int distance = -1; + enum dist_calc_method distance_calculation_method = DIST_METHOD_NONE; + + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + distance = -1; + hsi = *hostI; + HOS->makeFP(hsi->hss); + + hsi->FPs[roundNum] = hsi->hss->getFP(); + hsi->FPR->FPs[roundNum] = hsi->FPs[roundNum]; + hsi->FPR->numFPs = roundNum + 1; + double tr = hsi->hss->timingRatio(); + hsi->target->FPR->maxTimingRatio = MAX(hsi->target->FPR->maxTimingRatio, tr); + match_fingerprint(hsi->FPs[roundNum], &hsi->FP_matches[roundNum], + o.reference_FPs, OSSCAN_GUESS_THRESHOLD); + + if (hsi->FP_matches[roundNum].overall_results == OSSCAN_SUCCESS && + hsi->FP_matches[roundNum].num_perfect_matches > 0) { + memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info)); + if (roundNum > 0) { + if (o.verbose) + log_write(LOG_STDOUT, "WARNING: OS didn't match until try #%d\n", roundNum + 1); + } + match_fingerprint(hsi->FPR->FPs[roundNum], hsi->FPR, + o.reference_FPs, OSSCAN_GUESS_THRESHOLD); + hsi->isCompleted = true; + } + + if (islocalhost(hsi->target->TargetSockAddr())) { + /* scanning localhost */ + distance = 0; + distance_calculation_method = DIST_METHOD_LOCALHOST; + } else if (hsi->target->MACAddress()) { + /* on the same network segment */ + distance = 1; + distance_calculation_method = DIST_METHOD_DIRECT; + } else if (hsi->hss->distance!=-1) { + distance = hsi->hss->distance; + distance_calculation_method = DIST_METHOD_ICMP; + } + + hsi->target->distance = hsi->target->FPR->distance = distance; + hsi->target->distance_calculation_method = distance_calculation_method; + hsi->target->FPR->distance_guess = hsi->hss->distance_guess; + + } + OSI->removeCompletedHosts(); +} + + +static void findBestFPs(OsScanInfo *OSI) { + std::list<HostOsScanInfo *>::iterator hostI; + HostOsScanInfo *hsi = NULL; + int i; + + double bestacc; + int bestaccidx; + + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info)); + + /* Now lets find the best match */ + bestacc = 0; + bestaccidx = 0; + for (i = 0; i < hsi->FPR->numFPs; i++) { + if (hsi->FP_matches[i].overall_results == OSSCAN_SUCCESS && + hsi->FP_matches[i].num_matches > 0 && + hsi->FP_matches[i].accuracy[0] > bestacc) { + bestacc = hsi->FP_matches[i].accuracy[0]; + bestaccidx = i; + if (hsi->FP_matches[i].num_perfect_matches) + break; + } + } + + // Now we redo the match, since target->FPR has various data (such as + // target->FPR->numFPs) which is not in FP_matches[bestaccidx]. This is + // kinda ugly. + match_fingerprint(hsi->FPR->FPs[bestaccidx], (FingerPrintResultsIPv4 *) hsi->target->FPR, + o.reference_FPs, OSSCAN_GUESS_THRESHOLD); + } +} + + +static void printFP(OsScanInfo *OSI) { + std::list<HostOsScanInfo *>::const_iterator hostI; + const HostOsScanInfo *hsi = NULL; + const FingerPrintResultsIPv4 *FPR; + + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + FPR = hsi->FPR; + + log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT, + "No OS matches for %s by new os scan system.\n\nTCP/IP fingerprint:\n%s", + hsi->target->targetipstr(), + mergeFPs(FPR->FPs, FPR->numFPs, true, + hsi->target->TargetSockAddr(), hsi->target->distance, + hsi->target->distance_calculation_method, + hsi->target->MACAddress(), + FPR->osscan_opentcpport, FPR->osscan_closedtcpport, + FPR->osscan_closedudpport, false)); + } +} + + +/* Goes through every unmatched host in OSI. If a host has completed + the maximum number of OS detection tries allowed for it without + matching, it is transferred to the passed in unMatchedHosts list. + Returns the number of hosts moved to unMatchedHosts. */ +static int expireUnmatchedHosts(OsScanInfo *OSI, std::list<HostOsScanInfo *> *unMatchedHosts) { + std::list<HostOsScanInfo *>::iterator hostI, nextHost; + int hostsRemoved = 0; + HostOsScanInfo *HOS; + + gettimeofday(&now, NULL); + for (hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI = nextHost) { + HOS = *hostI; + nextHost = hostI; + nextHost++; + + int max_tries = o.maxOSTries(); /* The amt. if print is suitable for submission */ + if (HOS->target->FPR->OmitSubmissionFP()) + max_tries = MIN(max_tries, STANDARD_OS2_TRIES); + + if (HOS->FPR->numFPs >= max_tries) { + /* We've done all the OS2 tries we're going to do ... move this + to unMatchedHosts */ + HOS->target->stopTimeOutClock(&now); + OSI->incompleteHosts.erase(hostI); + /* We need to adjust nextI if necessary */ + OSI->resetHostIterator(); + hostsRemoved++; + unMatchedHosts->push_back(HOS); + } + } + return hostsRemoved; +} + + +/****************************************************************************** + * Implementation of class OFProbe * + ******************************************************************************/ + +OFProbe::OFProbe() { + type = OFP_UNSET; + subid = 0; + tryno = -1; + retransmitted = false; + memset(&sent, 0, sizeof(sent)); + memset(&prevSent, 0, sizeof(prevSent)); +} + + +const char *OFProbe::typestr() const { + switch (type) { + case OFP_UNSET: + return "OFP_UNSET"; + case OFP_TSEQ: + return "OFP_TSEQ"; + case OFP_TOPS: + return "OFP_TOPS"; + case OFP_TECN: + return "OFP_TECN"; + case OFP_T1_7: + return "OFP_T1_7"; + case OFP_TUDP: + return "OFP_TUDP"; + case OFP_TICMP: + return "OFP_TICMP"; + default: + assert(false); + return "ERROR"; + } +} + + +/****************************************************************************** + * Implementation of class HostOsScanStats * + ******************************************************************************/ + +HostOsScanStats::HostOsScanStats(Target * t) { + int i; + + target = t; + FP = NULL; + + memset(&si, 0, sizeof(si)); + memset(&ipid, 0, sizeof(ipid)); + + openTCPPort = -1; + closedTCPPort = -1; + closedUDPPort = -1; + + num_probes_sent = 0; + sendDelayMs = MAX(o.scan_delay, OS_PROBE_DELAY); + lastProbeSent = now; + + /* Timing */ + timing.cwnd = perf.host_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; + gettimeofday(&timing.last_drop, NULL); + + for (i = 0; i < NUM_FPTESTS; i++) + FPtests[i] = NULL; + for (i = 0; i < 6; i++) { + TOps_AVs[i] = NULL; + TWin_AVs[i] = NULL; + } + + icmpEchoReply = NULL; + + distance = -1; + distance_guess = -1; +} + + +HostOsScanStats::~HostOsScanStats() { + int i; + + for (i = 0; i < NUM_FPTESTS; i++) { + if (FPtests[i] != NULL) { + delete FPtests[i]; + FPtests[i] = NULL; + } + } + + while (!probesToSend.empty()) { + delete probesToSend.front(); + probesToSend.pop_front(); + } + + while (!probesActive.empty()) { + delete probesActive.front(); + probesActive.pop_front(); + } + + if (icmpEchoReply) free(icmpEchoReply); +} + + +void HostOsScanStats::initScanStats() { + Port *tport = NULL; + Port port; + int i; + + /* Lets find an open port to use if we don't already have one */ + openTCPPort = -1; + /* target->FPR->osscan_opentcpport = -1; + target->FPR->osscan_closedtcpport = -1; + target->FPR->osscan_closedudpport = -1; */ + + if (target->FPR->osscan_opentcpport > 0) + openTCPPort = target->FPR->osscan_opentcpport; + else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_TCP, PORT_OPEN))) { + openTCPPort = tport->portno; + /* If it is zero, let's try another one if there is one ) */ + if (tport->portno == 0) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_OPEN))) + openTCPPort = tport->portno; + + target->FPR->osscan_opentcpport = openTCPPort; + } + + /* We should look at a different port if we know that this port is tcpwrapped */ + if (o.servicescan && openTCPPort > 0 && target->ports.isTCPwrapped(openTCPPort)) { + if (o.debugging) { + log_write(LOG_STDOUT, "First choice open TCP port %d is tcpwrapped. ", openTCPPort); + } + /* Keep moving to other ports until we find one which is not tcpwrapped, or until we run out of ports */ + while ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_OPEN))) { + openTCPPort = tport->portno; + if (!target->ports.isTCPwrapped(openTCPPort)) { + break; + } + } + + target->FPR->osscan_opentcpport = openTCPPort; + + if (o.debugging) { + if (target->ports.isTCPwrapped(openTCPPort)) { + log_write(LOG_STDOUT, "All open TCP ports are found to be tcpwrapped. Using %d for OS detection, but results might not be accurate.\n", openTCPPort); + } else { + log_write(LOG_STDOUT, "Using non-tcpwrapped port %d for OS detection.\n", openTCPPort); + } + } + } + + /* Now we should find a closed TCP port */ + if (target->FPR->osscan_closedtcpport > 0) + closedTCPPort = target->FPR->osscan_closedtcpport; + else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_TCP, PORT_CLOSED))) { + closedTCPPort = tport->portno; + + /* If it is zero, let's try another one if there is one ) */ + if (tport->portno == 0) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_CLOSED))) + closedTCPPort = tport->portno; + + target->FPR->osscan_closedtcpport = closedTCPPort; + } else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_TCP, PORT_UNFILTERED))) { + /* Well, we will settle for unfiltered */ + closedTCPPort = tport->portno; + /* But again we'd prefer not to have zero */ + if (tport->portno == 0) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_UNFILTERED))) + closedTCPPort = tport->portno; + } else { + /* We'll just have to pick one at random :( */ + closedTCPPort = (get_random_uint() % 14781) + 30000; + } + + /* Now we should find a closed UDP port */ + if (target->FPR->osscan_closedudpport > 0) + closedUDPPort = target->FPR->osscan_closedudpport; + else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_UDP, PORT_CLOSED))) { + closedUDPPort = tport->portno; + /* Not zero, if possible */ + if (tport->portno == 0) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_UDP, PORT_CLOSED))) + closedUDPPort = tport->portno; + target->FPR->osscan_closedudpport = closedUDPPort; + } else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_UDP, PORT_UNFILTERED))) { + /* Well, we will settle for unfiltered */ + closedUDPPort = tport->portno; + /* But not zero, please */ + if (tport->portno == 0) + if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_UDP, PORT_UNFILTERED))) + closedUDPPort = tport->portno; + } else { + /* Pick one at random. Shrug. */ + closedUDPPort = (get_random_uint() % 14781) + 30000; + } + + FP = NULL; + for (i = 0; i < NUM_FPTESTS; i++) { + if (FPtests[i] != NULL) { + delete FPtests[i]; + FPtests[i] = NULL; + } + } + for (i = 0; i < 6; i++) { + TOps_AVs[i] = NULL; + TWin_AVs[i] = NULL; + } + + TOpsReplyNum = 0; + TWinReplyNum = 0; + + lastipid = 0; + memset(&si, 0, sizeof(si)); + + for (i = 0; i < NUM_SEQ_SAMPLES; i++) { + ipid.tcp_ipids[i] = -1; + ipid.tcp_closed_ipids[i] = -1; + ipid.icmp_ipids[i] = -1; + } + + memset(&seq_send_times, 0, sizeof(seq_send_times)); + + if (icmpEchoReply) { + free(icmpEchoReply); + icmpEchoReply = NULL; + } + storedIcmpReply = -1; + + memset(&upi, 0, sizeof(upi)); +} + + +/* Fill in an eth_nfo struct with the appropriate source and destination MAC + addresses and a given Ethernet handle. The return value is suitable to pass + to send_ip_packet: if ethsd is NULL, returns NULL; otherwise returns eth. */ +struct eth_nfo *HostOsScanStats::fill_eth_nfo(struct eth_nfo *eth, eth_t *ethsd) const { + if (ethsd == NULL) + return NULL; + + memcpy(eth->srcmac, target->SrcMACAddress(), sizeof(eth->srcmac)); + memcpy(eth->dstmac, target->NextHopMACAddress(), sizeof(eth->srcmac)); + eth->ethsd = ethsd; + eth->devname[0] = '\0'; + + return eth; +} + + +/* Add a probe to the probe list. */ +void HostOsScanStats::addNewProbe(OFProbeType type, int subid) { + OFProbe *probe = new OFProbe(); + probe->type = type; + probe->subid = subid; + probesToSend.push_back(probe); +} + + +/* Remove a probe from the probesActive. */ +void HostOsScanStats::removeActiveProbe(std::list<OFProbe *>::iterator probeI) { + OFProbe *probe = *probeI; + probesActive.erase(probeI); + delete probe; +} + + +/* Get an active probe from active probe list identified by probe type + and subid. Returns probesActive.end() if there isn't one */ +std::list<OFProbe *>::iterator HostOsScanStats::getActiveProbe(OFProbeType type, int subid) { + std::list<OFProbe *>::iterator probeI; + OFProbe *probe = NULL; + + for (probeI = probesActive.begin(); probeI != probesActive.end(); probeI++) { + probe = *probeI; + if (probe->type == type && probe->subid == subid) + break; + } + + if (probeI == probesActive.end()) { + /* not found!? */ + if (o.debugging > 1) + log_write(LOG_PLAIN, "Probe doesn't exist! Probe type: %d. Probe subid: %d\n", type, subid); + return probesActive.end(); + } + + return probeI; +} + + +/* Move a probe from probesToSend to probesActive. */ +void HostOsScanStats::moveProbeToActiveList(std::list<OFProbe *>::iterator probeI) { + probesActive.push_back(*probeI); + probesToSend.erase(probeI); +} + + +/* Move a probe from probesActive to probesToSend. */ +void HostOsScanStats::moveProbeToUnSendList(std::list<OFProbe *>::iterator probeI) { + probesToSend.push_back(*probeI); + probesActive.erase(probeI); +} + + + /* Compute the ratio of amount of time taken between sending 1st TSEQ + probe and 1st ICMP probe compared to the amount of time it should + have taken. Ratios far from 1 can cause bogus results */ +double HostOsScanStats::timingRatio() const { + if (openTCPPort < 0) + return 0; + int msec_ideal = OS_SEQ_PROBE_DELAY * (NUM_SEQ_SAMPLES - 1); + int msec_taken = TIMEVAL_MSEC_SUBTRACT(seq_send_times[NUM_SEQ_SAMPLES -1 ], seq_send_times[0]); + if (o.debugging) { + log_write(LOG_PLAIN, "OS detection timingRatio() == (%.3f - %.3f) * 1000 / %d == %.3f\n", + seq_send_times[NUM_SEQ_SAMPLES - 1].tv_sec + seq_send_times[NUM_SEQ_SAMPLES - 1].tv_usec / 1000000.0, seq_send_times[0].tv_sec + (float) seq_send_times[0].tv_usec / 1000000.0, msec_ideal, (float) msec_taken / msec_ideal); + } + return (double) msec_taken / msec_ideal; +} + + +/****************************************************************************** + * Implementation of class HostOsScan * + ******************************************************************************/ + +/* 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 HostOsScan::nextTimeout(HostOsScanStats *hss, struct timeval *when) const { + assert(hss); + struct timeval probe_to, earliest_to; + std::list<OFProbe *>::const_iterator probeI; + bool firstgood = true; + + assert(when); + memset(&probe_to, 0, sizeof(probe_to)); + memset(&earliest_to, 0, sizeof(earliest_to)); + + for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) { + TIMEVAL_ADD(probe_to, (*probeI)->sent, timeProbeTimeout(hss)); + if (firstgood || TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) { + earliest_to = probe_to; + firstgood = false; + } + } + + *when = (firstgood)? now : earliest_to; + return !firstgood; +} + + +void HostOsScan::adjust_times(HostOsScanStats *hss, const OFProbe *probe, const struct timeval *rcvdtime) { + assert(hss); + assert(probe); + + /* Adjust timing */ + if (rcvdtime) { + adjust_timeouts2(&(probe->sent), rcvdtime, &(hss->target->to)); + adjust_timeouts2(&(probe->sent), rcvdtime, &(stats->to)); + } + + stats->timing.num_replies_expected++; + stats->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 get no response after a timeout (rcvdtime == NULL). */ + if (probe->tryno > 0 || rcvdtime == NULL) { + if (o.debugging > 1) { + if (probe->tryno > 0) { + log_write(LOG_PLAIN, "OS scan DROPPED probe to %s detected (tryno %d)\n", + hss->target->targetipstr(), probe->tryno); + } else { + log_write(LOG_PLAIN, "OS scan DROPPED probe to %s detected (rcvdtime == NULL)\n", + hss->target->targetipstr()); + } + } + if (TIMEVAL_AFTER(probe->sent, hss->timing.last_drop)) + hss->timing.drop(hss->numProbesActive(), &perf, &now); + if (TIMEVAL_AFTER(probe->sent, stats->timing.last_drop)) + stats->timing.drop_group(stats->num_probes_active, &perf, &now); + } + + /* Increase the window for a positive reply. This can overlap with case (1) + above. */ + if (rcvdtime != NULL) { + stats->timing.ack(&perf); + hss->timing.ack(&perf); + } +} + + +HostOsScan::HostOsScan(Target *t) { + pd = NULL; + rawsd = -1; + ethsd = NULL; + + if ((o.sendpref & PACKET_SEND_ETH) && (t->ifType() == devt_ethernet +#ifdef WIN32 + || (g_has_npcap_loopback && t->ifType() == devt_loopback) +#endif + )) { + if ((ethsd = eth_open_cached(t->deviceName())) == NULL) + fatal("%s: Failed to open ethernet device (%s)", __func__, t->deviceName()); + rawsd = -1; + } else { +#ifdef WIN32 + win32_fatal_raw_sockets(t->deviceName()); +#endif + rawsd = nmap_raw_socket(); + if (rawsd < 0) + pfatal("socket troubles in %s", __func__); + unblock_socket(rawsd); + ethsd = NULL; + } + + tcpPortBase = o.magic_port_set? o.magic_port : o.magic_port + get_random_u8(); + udpPortBase = o.magic_port_set? o.magic_port : o.magic_port + get_random_u8(); + reInitScanSystem(); + + stats = new ScanStats(); +} + + +HostOsScan::~HostOsScan() { + if (rawsd >= 0) { + close(rawsd); + rawsd = -1; + } + if (pd) { + pcap_close(pd); + pd = NULL; + } + /* No need to close ethsd due to caching. */ + delete stats; +} + + +void HostOsScan::reInitScanSystem() { + tcpSeqBase = get_random_u32(); + tcpAck = get_random_u32(); + tcpMss = 265; + icmpEchoId = get_random_u16(); + icmpEchoSeq = 295; + udpttl = (time(NULL) % 14) + 51; +} + + +/* Initiate seq probe list */ +void HostOsScan::buildSeqProbeList(HostOsScanStats *hss) { + assert(hss); + int i; + if (hss->openTCPPort == -1) + return; + if (hss->FP_TSeq) + return; + + for (i = 0; i < NUM_SEQ_SAMPLES; i++) + hss->addNewProbe(OFP_TSEQ, i); +} + + +/* Update the seq probes in the active probe list and remove the ones that have + * timed out. */ +void HostOsScan::updateActiveSeqProbes(HostOsScanStats *hss) { + assert(hss); + std::list<OFProbe *>::iterator probeI, nxt; + OFProbe *probe = NULL; + + for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI = nxt) { + nxt = probeI; + nxt++; + probe = *probeI; + + /* Is the probe timedout? */ + if (TIMEVAL_SUBTRACT(now, probe->sent) > (long) timeProbeTimeout(hss)) { + hss->removeActiveProbe(probeI); + assert(stats->num_probes_active > 0); + stats->num_probes_active--; + } + } +} + + +/* Initialize the normal TCP/UDP/ICMP probe list */ +void HostOsScan::buildTUIProbeList(HostOsScanStats *hss) { + assert(hss); + int i; + + /* The order of these probes are important for ipid generation + * algorithm test and should not be changed. + * + * At doSeqTests we sent 6 TSeq probes to generate 6 tcp replies, + * and here we follow with 3 probes to generate 3 icmp replies. In + * this way we can expect to get "good" IPid sequence. + * + * **** Should be done in a more elegant way. ***** + */ + + /* ticmp */ + if (!hss->FP_TIcmp) { + for (i = 0; i < 2; i++) { + hss->addNewProbe(OFP_TICMP, i); + } + } + + /* tudp */ + if (!hss->FP_TUdp) { + hss->addNewProbe(OFP_TUDP, 0); + } + + if (hss->openTCPPort != -1) { + /* tops/twin probes. We send the probe again if we didn't get a + response by the corresponding seq probe. */ + if (!hss->FP_TOps || !hss->FP_TWin) { + for (i = 0; i < 6; i++) { + if (!hss->TOps_AVs[i] || !hss->TWin_AVs[i]) + hss->addNewProbe(OFP_TOPS, i); + } + } + + /* tecn */ + if (!hss->FP_TEcn) { + hss->addNewProbe(OFP_TECN, 0); + } + + /* t1_7: t1_t4 */ + for (i = 0; i < 4; i++) { + if (!hss->FPtests[FP_T1_7_OFF + i]) { + hss->addNewProbe(OFP_T1_7, i); + } + } + } + + /* t1_7: t5_t7 */ + for (i = 4; i < 7; i++) { + if (!hss->FPtests[FP_T1_7_OFF + i]) { + hss->addNewProbe(OFP_T1_7, i); + } + } +} + + +/* Update the probes in the active probe list: + * 1) Remove the expired probes (timedout and reached the retry limit); + * 2) Move timedout probes to probeNeedToSend; */ +void HostOsScan::updateActiveTUIProbes(HostOsScanStats *hss) { + assert(hss); + std::list<OFProbe *>::iterator probeI, nxt; + OFProbe *probe = NULL; + + for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI = nxt) { + nxt = probeI; + nxt++; + probe = *probeI; + + if (TIMEVAL_SUBTRACT(now, probe->sent) > (long) timeProbeTimeout(hss)) { + if (probe->tryno >= 3) { + /* The probe is expired. */ + hss->removeActiveProbe(probeI); + assert(stats->num_probes_active > 0); + stats->num_probes_active--; + } + else { + /* It is timedout, move it to the sendlist */ + hss->moveProbeToUnSendList(probeI); + assert(stats->num_probes_active > 0); + stats->num_probes_active--; + } + } + } +} + + +/* Check whether the host is sendok. If not, fill _when_ with the time + * when it will be sendOK and return false; else, fill it with now and + * return true. */ +bool HostOsScan::hostSendOK(HostOsScanStats *hss, struct timeval *when) const { + assert(hss); + std::list<OFProbe *>::const_iterator probeI; + int packTime; + struct timeval probe_to, earliest_to, sendTime; + long tdiff; + + if (hss->target->timedOut(&now)) { + if (when) + *when = now; + return false; + } + + if (hss->sendDelayMs > 0) { + packTime = TIMEVAL_MSEC_SUBTRACT(now, hss->lastProbeSent); + if (packTime < (int) hss->sendDelayMs) { + if (when) { + TIMEVAL_MSEC_ADD(*when, hss->lastProbeSent, hss->sendDelayMs); + } + return false; + } + } + + if (hss->timing.cwnd >= hss->numProbesActive() + .5) { + if (when) + *when = now; + return true; + } + + if (!when) + return false; + + TIMEVAL_MSEC_ADD(earliest_to, now, 10000); + + /* Any timeouts coming up? */ + for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) { + TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, timeProbeTimeout(hss) / 1000); + if (TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) { + earliest_to = probe_to; + } + } + + // Will any scan delay affect this? + if (hss->sendDelayMs > 0) { + TIMEVAL_MSEC_ADD(sendTime, hss->lastProbeSent, hss->sendDelayMs); + if (TIMEVAL_MSEC_SUBTRACT(sendTime, now) < 0) + sendTime = 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 { + if (tdiff > 0 && hss->timing.cwnd > hss->numProbesActive() + .5) { + earliest_to = sendTime; + } + } + } + + *when = earliest_to; + return false; +} + + +/* Check whether it is OK to send the next seq probe to the host. If + * not, fill param "when" with the time when it will be sendOK and return + * false; else, fill it with now and return true. */ +bool HostOsScan::hostSeqSendOK(HostOsScanStats *hss, struct timeval *when) const { + assert(hss); + std::list<OFProbe *>::const_iterator probeI; + int packTime = 0, maxWait = 0; + struct timeval probe_to, earliest_to, sendTime; + long tdiff; + + if (hss->target->timedOut(&now)) { + if (when) + *when = now; + return false; + } + + packTime = TIMEVAL_SUBTRACT(now, hss->lastProbeSent); + + /* + * If the user insist a larger sendDelayMs, use it. But + * the seq result may be inaccurate. + */ + maxWait = MAX(OS_SEQ_PROBE_DELAY * 1000, hss->sendDelayMs * 1000); + if (packTime < maxWait) { + if (when) { + TIMEVAL_ADD(*when, hss->lastProbeSent, maxWait); + } + return false; + } + + if (hss->timing.cwnd >= hss->numProbesActive() + .5) { + if (when) + *when = now; + return true; + } + + if (!when) + return false; + + TIMEVAL_MSEC_ADD(earliest_to, now, 10000); + + /* Any timeouts coming up? */ + for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) { + TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, timeProbeTimeout(hss) / 1000); + if (TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) { + earliest_to = probe_to; + } + } + + TIMEVAL_ADD(sendTime, hss->lastProbeSent, maxWait); + if (TIMEVAL_SUBTRACT(sendTime, now) < 0) + sendTime = now; + tdiff = TIMEVAL_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 { + if (tdiff > 0 && hss->timing.cwnd > hss->numProbesActive() + .5) { + earliest_to = sendTime; + } + } + + *when = earliest_to; + return false; +} + + +unsigned long HostOsScan::timeProbeTimeout(HostOsScanStats *hss) const { + assert(hss); + if (hss->target->to.srtt > 0) { + /* We have at least one timing value to use. Good enough, I suppose */ + return hss->target->to.timeout; + } else if (stats->to.srtt > 0) { + /* OK, we'll use this one instead */ + return stats->to.timeout; + } else { + return hss->target->to.timeout; /* It comes with a default */ + } +} + + +void HostOsScan::sendNextProbe(HostOsScanStats *hss) { + assert(hss); + std::list<OFProbe *>::iterator probeI; + OFProbe *probe = NULL; + + if (hss->probesToSend.empty()) + return; + + if (!hss->target->timeOutClockRunning() && !hss->target->timedOut(NULL)) { + hss->target->startTimeOutClock(&now); + } + + probeI = hss->probesToSend.begin(); + probe = *probeI; + + switch (probe->type) { + case OFP_TSEQ: + sendTSeqProbe(hss, probe->subid); + break; + case OFP_TOPS: + sendTOpsProbe(hss, probe->subid); + break; + case OFP_TECN: + sendTEcnProbe(hss); + break; + case OFP_T1_7: + sendT1_7Probe(hss, probe->subid); + break; + case OFP_TICMP: + sendTIcmpProbe(hss, probe->subid); + break; + case OFP_TUDP: + sendTUdpProbe(hss, probe->subid); + break; + default: + assert(false); + } + + probe->tryno++; + if (probe->tryno > 0) { + /* This is a retransmission */ + probe->retransmitted = true; + probe->prevSent = probe->sent; + } + probe->sent = now; + + hss->lastProbeSent = now; + hss->num_probes_sent++; + stats->num_probes_sent++; + + hss->moveProbeToActiveList(probeI); + stats->num_probes_active++; + + if (o.debugging > 1) { + log_write(LOG_PLAIN, "Send probe (type: %s, subid: %d) to %s\n", + probe->typestr(), probe->subid, hss->target->targetipstr()); + } + +} + + +void HostOsScan::sendTSeqProbe(HostOsScanStats *hss, int probeNo) { + assert(hss); + assert(probeNo >= 0 && probeNo < NUM_SEQ_SAMPLES); + + if (hss->openTCPPort == -1) + return; + + send_tcp_probe(hss, o.ttl, false, NULL, 0, + tcpPortBase + probeNo, hss->openTCPPort, + tcpSeqBase + probeNo, tcpAck, + 0, TH_SYN, prbWindowSz[probeNo], 0, + prbOpts[probeNo].val, prbOpts[probeNo].len, NULL, 0); + + hss->seq_send_times[probeNo] = now; +} + + +void HostOsScan::sendTOpsProbe(HostOsScanStats *hss, int probeNo) { + assert(hss); + assert(probeNo >= 0 && probeNo < NUM_SEQ_SAMPLES); + + if (hss->openTCPPort == -1) + return; + + send_tcp_probe(hss, o.ttl, false, NULL, 0, + tcpPortBase + NUM_SEQ_SAMPLES + probeNo, hss->openTCPPort, + tcpSeqBase, tcpAck, + 0, TH_SYN, prbWindowSz[probeNo], 0, + prbOpts[probeNo].val, prbOpts[probeNo].len, NULL, 0); +} + + +void HostOsScan::sendTEcnProbe(HostOsScanStats *hss) { + assert(hss); + + if (hss->openTCPPort == -1) + return; + + send_tcp_probe(hss, o.ttl, false, NULL, 0, + tcpPortBase + NUM_SEQ_SAMPLES + 6, hss->openTCPPort, + tcpSeqBase, 0, + 8, TH_CWR|TH_ECE|TH_SYN, prbWindowSz[6], 63477, + prbOpts[6].val, prbOpts[6].len, NULL, 0); +} + + +void HostOsScan::sendT1_7Probe(HostOsScanStats *hss, int probeNo) { + assert(hss); + assert(probeNo >=0 && probeNo < 7); + + int port_base = tcpPortBase + NUM_SEQ_SAMPLES + 7; + + switch (probeNo) { + case 0: /* T1 */ + /* T1 is normally filled in by sendTSeqProbe so this case doesn't happen. In + case all six Seq probes failed, this one will be re-sent. It is the same + as the first probe sent by sendTSeqProbe. */ + if (hss->openTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, false, NULL, 0, + port_base, hss->openTCPPort, + tcpSeqBase, tcpAck, + 0, TH_SYN, prbWindowSz[0], 0, + prbOpts[0].val, prbOpts[0].len, NULL, 0); + break; + case 1: /* T2 */ + if (hss->openTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, true, NULL, 0, + port_base + 1, hss->openTCPPort, + tcpSeqBase, tcpAck, + 0, 0, prbWindowSz[7], 0, + prbOpts[7].val, prbOpts[7].len, NULL, 0); + break; + case 2: /* T3 */ + if (hss->openTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, false, NULL, 0, + port_base + 2, hss->openTCPPort, + tcpSeqBase, tcpAck, + 0, TH_SYN|TH_FIN|TH_URG|TH_PUSH, prbWindowSz[8], 0, + prbOpts[8].val, prbOpts[8].len, NULL, 0); + break; + case 3: /* T4 */ + if (hss->openTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, true, NULL, 0, + port_base + 3, hss->openTCPPort, + tcpSeqBase, tcpAck, + 0, TH_ACK, prbWindowSz[9], 0, + prbOpts[9].val, prbOpts[9].len, NULL, 0); + break; + case 4: /* T5 */ + if (hss->closedTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, false, NULL, 0, + port_base + 4, hss->closedTCPPort, + tcpSeqBase, tcpAck, + 0, TH_SYN, prbWindowSz[10], 0, + prbOpts[10].val, prbOpts[10].len, NULL, 0); + break; + case 5: /* T6 */ + if (hss->closedTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, true, NULL, 0, + port_base + 5, hss->closedTCPPort, + tcpSeqBase, tcpAck, + 0, TH_ACK, prbWindowSz[11], 0, + prbOpts[11].val, prbOpts[11].len, NULL, 0); + break; + case 6: /* T7 */ + if (hss->closedTCPPort == -1) + return; + send_tcp_probe(hss, o.ttl, false, NULL, 0, + port_base + 6, hss->closedTCPPort, + tcpSeqBase, tcpAck, + 0, TH_FIN|TH_PUSH|TH_URG, prbWindowSz[12], 0, + prbOpts[12].val, prbOpts[12].len, NULL, 0); + } +} + + +void HostOsScan::sendTIcmpProbe(HostOsScanStats *hss, int probeNo) { + assert(hss); + assert(probeNo >= 0 && probeNo < 2); + if (probeNo == 0) { + send_icmp_echo_probe(hss, IP_TOS_DEFAULT, + true, 9, icmpEchoId, icmpEchoSeq, 120); + } + else { + send_icmp_echo_probe(hss, IP_TOS_RELIABILITY, + false, 0, icmpEchoId + 1, icmpEchoSeq + 1, 150); + } +} + + +void HostOsScan::sendTUdpProbe(HostOsScanStats *hss, int probeNo) { + assert(hss); + if (hss->closedUDPPort == -1) + return; + send_closedudp_probe(hss, udpttl, udpPortBase + probeNo, hss->closedUDPPort); +} + + +bool HostOsScan::processResp(HostOsScanStats *hss, const struct ip *ip, unsigned int len, struct timeval *rcvdtime) { + const struct ip *ip2; + const struct tcp_hdr *tcp; + const struct icmp *icmp; + int testno; + bool isPktUseful = false; + std::list<OFProbe *>::iterator probeI; + OFProbe *probe; + + if (len < 20 || len < (4 * ip->ip_hl) + 4U) + return false; + + len -= 4 * ip->ip_hl; + + if (ip->ip_p == IPPROTO_TCP) { + if (len < 20) + return false; + tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + if (len < (unsigned int)(4 * tcp->th_off)) + return false; + testno = ntohs(tcp->th_dport) - tcpPortBase; + + if (testno >= 0 && testno < NUM_SEQ_SAMPLES) { + /* TSeq */ + isPktUseful = processTSeqResp(hss, ip, testno); + + if (isPktUseful) { + hss->ipid.tcp_ipids[testno] = ntohs(ip->ip_id); + probeI = hss->getActiveProbe(OFP_TSEQ, testno); + /* printf("tcp ipid = %d\n", ntohs(ip->ip_id)); */ + } + + /* Use the seq response to do other tests. We don't care if it + * is useful for these tests. + */ + if (testno == 0) { + /* the first reply is used to do T1 */ + processT1_7Resp(hss, ip, 0); + } + /* the 1st NUM_SEQ_SAMPLES replies are used to do TOps and TWin */ + processTOpsResp(hss, tcp, testno); + processTWinResp(hss, tcp, testno); + + } else if (testno >= NUM_SEQ_SAMPLES && testno < NUM_SEQ_SAMPLES + 6) { + + /* TOps/Twin */ + isPktUseful = processTOpsResp(hss, tcp, testno - NUM_SEQ_SAMPLES); + isPktUseful |= processTWinResp(hss, tcp, testno - NUM_SEQ_SAMPLES); + if (isPktUseful) { + probeI = hss->getActiveProbe(OFP_TOPS, testno - NUM_SEQ_SAMPLES); + } + + } else if (testno == NUM_SEQ_SAMPLES + 6) { + + /* TEcn */ + isPktUseful = processTEcnResp(hss, ip); + if (isPktUseful) { + probeI = hss->getActiveProbe(OFP_TECN, 0); + } + + } else if (testno >= NUM_SEQ_SAMPLES + 7 && testno < NUM_SEQ_SAMPLES + 14) { + + isPktUseful = processT1_7Resp(hss, ip, testno - NUM_SEQ_SAMPLES - 7); + + if (isPktUseful) { + probeI = hss->getActiveProbe(OFP_T1_7, testno - NUM_SEQ_SAMPLES - 7); + + /* Closed-port TCP IP ID sequence numbers (SEQ.CI). Uses T5, T6, and T7. + T5 starts at NUM_SEQ_SAMPLES + 11. */ + if (testno >= NUM_SEQ_SAMPLES + 11) + hss->ipid.tcp_closed_ipids[testno - (NUM_SEQ_SAMPLES + 11)] = ntohs(ip->ip_id); + } + } + } + else if (ip->ip_p == IPPROTO_ICMP) { + if (len < 8) + return false; + icmp = ((struct icmp *)(((char *) ip) + 4 * ip->ip_hl)); + + /* Is it an icmp echo reply? */ + if (icmp->icmp_type == ICMP_ECHOREPLY) { + testno = ntohs(icmp->icmp_id) - icmpEchoId; + if (testno == 0 || testno == 1) { + isPktUseful = processTIcmpResp(hss, ip, testno); + if (isPktUseful) { + probeI = hss->getActiveProbe(OFP_TICMP, testno); + } + + if (isPktUseful && probeI != hss->probesActive.end() && !(*probeI)->retransmitted) { /* Retransmitted ipid is useless. */ + hss->ipid.icmp_ipids[testno] = ntohs(ip->ip_id); + /* printf("icmp ipid = %d\n", ntohs(ip->ip_id)); */ + } + } + } + + /* Is it a destination port unreachable? */ + if (icmp->icmp_type == 3 && icmp->icmp_code == 3) { + len -= 8; /* icmp destination unreachable header len. */ + if (len < 28) + return false; /* must larger than an ip and an udp header length */ + ip2 = (struct ip*)((char *)icmp + 8); + len -= 4 * ip2->ip_hl; + if (len < 8) + return false; + + isPktUseful = processTUdpResp(hss, ip); + if (isPktUseful) { + probeI = hss->getActiveProbe(OFP_TUDP, 0); + } + } + + } + + if (isPktUseful && probeI != hss->probesActive.end()) { + probe = *probeI; + + if (rcvdtime) + adjust_times(hss, probe, rcvdtime); + + if (o.debugging > 1) { + log_write(LOG_PLAIN, "Got a valid response for probe (type: %s subid: %d) from %s\n", + probe->typestr(), probe->subid, hss->target->targetipstr()); + } + + /* delete the probe. */ + hss->removeActiveProbe(probeI); + assert(stats->num_probes_active > 0); + stats->num_probes_active--; + + return true; + } + + return false; +} + + +void HostOsScan::makeFP(HostOsScanStats *hss) { + assert(hss); + + int i; + + if (!hss->FP_TSeq) + makeTSeqFP(hss); + + if (!hss->FP_TOps) + makeTOpsFP(hss); + + if (!hss->FP_TWin) + makeTWinFP(hss); + + for (i = 3; i < NUM_FPTESTS; i++) { + if (!hss->FPtests[i] && + ((i >= 3 && i <= 7 && hss->openTCPPort != -1) || + (i >= 8 && i <= 10 && hss->target->FPR->osscan_closedtcpport != -1) || + i >= 11)) { + /* We create a Resp (response) attribute with value of N (no) because + it is important here to note whether responses were or were not + received */ + hss->FPtests[i] = new FingerTest(INT2ID(i), *o.reference_FPs->MatchPoints); + hss->FPtests[i]->setAVal("R", "N"); + } + else if (hss->FPtests[i]) { + FingerTest &test = *hss->FPtests[i]; + /* The value for this attribute is the + * received TTL. We replace it with the initial TTL. */ + const char *recvTTL = test.getAVal("T"); + if (recvTTL) { + int ttl = strtol(recvTTL, NULL, 16); + + if (hss->distance_guess == -1) + hss->distance_guess = get_initial_ttl_guess(ttl) - ttl; + + if (hss->distance != -1) { + /* We've gotten response for the UDP probe and thus have + the "true" hop count. Add the number of hops between + us and the target (hss->distance - 1) to the received + TTL to get the initial TTL. */ + test.setAVal("T", hss->target->FPR->cp_hex(ttl + hss->distance - 1)); + } else { + /* Guess the initial TTL value */ + test.setAVal("TG", hss->target->FPR->cp_hex(get_initial_ttl_guess(ttl))); + /* Delete this unknown-distance-dependent value */ + test.setAVal("T", NULL); + } + } + } + } + + /* Link them up. */ + hss->FP = new FingerPrint; + for (i = 0; i < NUM_FPTESTS; i++) { + if (hss->FPtests[i] == NULL) + continue; + hss->FP->tests[i] = *hss->FPtests[i]; + } +} + + +/* Send a TCP probe. This takes care of decoys and filling in Ethernet + * addresses if necessary. Used for the SEQ, OPS, WIN, ECN, and T1-T7 probes. */ +int HostOsScan::send_tcp_probe(HostOsScanStats *hss, + int ttl, bool df, u8* ipopt, int ipoptlen, + u16 sport, u16 dport, u32 seq, u32 ack, + u8 reserved, u8 flags, u16 window, u16 urp, + u8 *options, int optlen, + char *data, u16 datalen) { + struct eth_nfo eth, *ethptr; + + ethptr = hss->fill_eth_nfo(ð, ethsd); + + return send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), + ttl, df, ipopt, ipoptlen, sport, dport, seq, ack, + reserved, flags, window, urp, + options, optlen, data, datalen); +} + + +/* Send an echo probe. This takes care of decoys and filling in Ethernet + * addresses if necessary. Used for the IE probes. */ +int HostOsScan::send_icmp_echo_probe(HostOsScanStats *hss, + u8 tos, bool df, u8 pcode, + unsigned short id, u16 seq, u16 datalen) { + u8 *packet = NULL; + u32 packetlen = 0; + int decoy; + int res = -1; + struct eth_nfo eth, *ethptr; + + ethptr = hss->fill_eth_nfo(ð, ethsd); + + for (decoy = 0; decoy < o.numdecoys; decoy++) { + packet = build_icmp_raw(&((struct sockaddr_in *)&o.decoys[decoy])->sin_addr, hss->target->v4hostip(), + o.ttl, get_random_u16(), tos, df, NULL, 0, seq, id, + ICMP_ECHO, pcode, NULL, datalen, &packetlen); + if (!packet) + return -1; + res = send_ip_packet(rawsd, ethptr, hss->target->TargetSockAddr(), packet, packetlen); + free(packet); + if (res == -1) + return -1; + } + + return 0; +} + + +/* Send a UDP probe. This takes care of decoys and filling in Ethernet + * addresses if necessary. Used for the U1 probe. */ +int HostOsScan::send_closedudp_probe(HostOsScanStats *hss, + int ttl, u16 sport, u16 dport) { + static int myttl = 0; + static u8 patternbyte = 0x43; /* character 'C' */ + static u16 id = 0x1042; + u8 packet[328]; /* 20 IP hdr + 8 UDP hdr + 300 data */ + struct ip *ip = (struct ip *) packet; + struct udp_hdr *udp = (struct udp_hdr *) (packet + sizeof(struct ip)); + struct in_addr *source; + int datalen = 300; + unsigned char *data = packet + 28; + unsigned short realcheck; /* the REAL checksum */ + int res; + int decoy; + struct eth_nfo eth, *ethptr; + + ethptr = hss->fill_eth_nfo(ð, ethsd); + + /* if (!patternbyte) patternbyte = (get_random_uint() % 60) + 65; */ + memset(data, patternbyte, datalen); + + /* while (!id) id = get_random_uint(); */ + + if (ttl == -1) { + myttl = (time(NULL) % 14) + 51; + } else { + myttl = ttl; + } + + /* check that required fields are there and not too silly */ + if (!sport || !dport) { + error("%s: One or more of your parameters suck!", __func__); + return 1; + } + + for (decoy = 0; decoy < o.numdecoys; decoy++) { + if (o.decoys[decoy].ss_family == AF_INET6) + return 1; + source = &((struct sockaddr_in *)&o.decoys[decoy])->sin_addr; + + memset((char *) packet, 0, sizeof(struct ip) + sizeof(struct udp_hdr)); + + udp->uh_sport = htons(sport); + udp->uh_dport = htons(dport); + udp->uh_ulen = htons(8 + datalen); + + /* OK, now we should be able to compute a valid checksum */ + realcheck = ipv4_pseudoheader_cksum(source, hss->target->v4hostip(), IPPROTO_UDP, + sizeof(struct udp_hdr) + datalen, (char *) udp); +#if STUPID_SOLARIS_CHECKSUM_BUG + udp->uh_sum = sizeof(struct udp_hdr) + datalen; +#else + udp->uh_sum = realcheck; +#endif + + /* Now for the ip header */ + ip->ip_v = 4; + ip->ip_hl = 5; + ip->ip_len = htons(sizeof(struct ip) + sizeof(struct udp_hdr) + datalen); + ip->ip_id = htons(id); + ip->ip_ttl = myttl; + ip->ip_p = IPPROTO_UDP; + ip->ip_src.s_addr = source->s_addr; + ip->ip_dst.s_addr= hss->target->v4hostip()->s_addr; + + hss->upi.ipck = in_cksum((unsigned short *)ip, sizeof(struct ip)); +#if HAVE_IP_IP_SUM + ip->ip_sum = hss->upi.ipck; +#endif + + /* OK, now if this is the real she-bang (ie not a decoy) then + we stick all the inph0 in our upi */ + if (decoy == o.decoyturn) { + hss->upi.iptl = 28 + datalen; + hss->upi.ipid = id; + hss->upi.sport = sport; + hss->upi.dport = dport; + hss->upi.udpck = realcheck; + hss->upi.udplen = 8 + datalen; + hss->upi.patternbyte = patternbyte; + hss->upi.target.s_addr = ip->ip_dst.s_addr; + } + + if ((res = send_ip_packet(rawsd, ethptr, hss->target->TargetSockAddr(), packet, ntohs(ip->ip_len))) == -1) + { + gh_perror("send_ip_packet in %s", __func__); + return 1; + } + } + + return 0; +} + + +/****************************************************************************** + * Implementation of class ScanStats * + ******************************************************************************/ + +ScanStats::ScanStats() { + /* init timing val */ + timing.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; + gettimeofday(&timing.last_drop, NULL); + + initialize_timeout_info(&to); + + num_probes_active = 0; + num_probes_sent = num_probes_sent_at_last_wait = 0; +} + + +/* Returns true if the os scan system says that sending is OK.*/ +bool ScanStats::sendOK() const { + if (num_probes_sent - num_probes_sent_at_last_wait >= 50) + return false; + + if (timing.cwnd < num_probes_active + 0.5) + return false; + + return true; +} + + +/****************************************************************************** + * Implementation of class HostOsScan * + ******************************************************************************/ + +static unsigned int gcd_n_uint(int nvals, unsigned int *val) { + unsigned int a, b, c; + + if (!nvals) + return 1; + a = *val; + for (nvals--; nvals; nvals--) { + b = *++val; + if (a < b) { + c = a; + a = b; + b = c; + } + while (b) { + c = a % b; + a = b; + b = c; + } + } + return a; +} + +void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { + int i, j; + u32 seq_diffs[NUM_SEQ_SAMPLES]; + u32 ts_diffs[NUM_SEQ_SAMPLES]; + float seq_rates[NUM_SEQ_SAMPLES]; + unsigned long time_usec_diffs[NUM_SEQ_SAMPLES]; + double seq_stddev = 0; + double seq_rate = 0; + double seq_avg_rate = 0; + double avg_ts_hz = 0.0; /* Avg. amount that timestamps incr. each second */ + u32 seq_gcd = 1; + int tcp_ipid_seqclass; /* TCP IPID SEQ TYPE defines in nmap.h */ + int tcp_closed_ipid_seqclass; /* TCP IPID SEQ TYPE defines in nmap.h */ + int icmp_ipid_seqclass; /* ICMP IPID SEQ TYPE defines in nmap.h */ + int good_tcp_ipid_num, good_tcp_closed_ipid_num, good_icmp_ipid_num; + int tsnewval = 0; + + hss->FP_TSeq = new FingerTest(FingerPrintDef::SEQ, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TSeq; + + /* Now we make sure there are no gaps in our response array ... */ + for (i = 0, j = 0; i < NUM_SEQ_SAMPLES; i++) { + if (hss->si.seqs[i] != 0) /* We found a good one */ { + if (j < i) { + hss->si.seqs[j] = hss->si.seqs[i]; + hss->si.ipids[j] = hss->si.ipids[i]; + hss->si.timestamps[j] = hss->si.timestamps[i]; + hss->seq_send_times[j] = hss->seq_send_times[i]; + } + if (j > 0) { + seq_diffs[j - 1] = MOD_DIFF(hss->si.seqs[j], hss->si.seqs[j - 1]); + + ts_diffs[j - 1] = MOD_DIFF(hss->si.timestamps[j], hss->si.timestamps[j - 1]); + time_usec_diffs[j - 1] = TIMEVAL_SUBTRACT(hss->seq_send_times[j], hss->seq_send_times[j - 1]); + if (!time_usec_diffs[j - 1]) time_usec_diffs[j - 1]++; /* We divide by this later */ + /* Rate of ISN increase per second */ + seq_rates[j - 1] = seq_diffs[j - 1] * 1000000.0 / time_usec_diffs[j - 1]; + seq_avg_rate += seq_rates[j - 1]; + } + j++; + } /* Otherwise nothing good in this slot to copy */ + } + + hss->si.responses = j; /* Just for assurance */ + + /* Time to look at the TCP ISN predictability */ + if (hss->si.responses >= 4 && o.scan_delay <= 1000) { + seq_avg_rate /= hss->si.responses - 1; + seq_rate = seq_avg_rate; + + /* First calculate the GCD */ + seq_gcd = gcd_n_uint(hss->si.responses -1, seq_diffs); + + if (!seq_gcd) { + /* Constant ISN */ + seq_rate = 0; + seq_stddev = 0; + hss->si.index = 0; + } else { + + /* Finally we take a binary logarithm, multiply by 8, and round + * to get the final result */ + seq_rate = log(seq_rate) / log(2.0); + seq_rate = (unsigned int) (seq_rate * 8 + 0.5); + + /* Normally we don't divide by gcd in computing the rate stddev + * because otherwise we'll get an artificially low value about + * 1/32 of the time if the responses all happen to be even. On + * the other hand, if a system inherently uses a large gcd such + * as 64,000, we want to get rid of it. So as a compromise, we + * divide by the gcd if it is at least 9 */ + int div_gcd = 1; + if (seq_gcd > 9) + div_gcd = seq_gcd; + + for (i = 0; i < hss->si.responses - 1; i++) { + double rtmp = seq_rates[i] / div_gcd - seq_avg_rate / div_gcd; + seq_stddev += rtmp * rtmp; + } + + /* We divide by ((numelements in seq_diffs) - 1), which is + * (si.responses - 2), because that gives a better approx of + * std. dev when you're only looking at a subset of whole + * population. */ + seq_stddev /= hss->si.responses - 2; + + /* Next we need to take the square root of this value */ + seq_stddev = sqrt(seq_stddev); + + /* Finally we take a binary logarithm, multiply by 8, and round + * to get the final result */ + if (seq_stddev <= 1) + hss->si.index = 0; + else { + seq_stddev = log(seq_stddev) / log(2.0); + hss->si.index = (int) (seq_stddev * 8 + 0.5); + } + } + + test.setAVal("SP", hss->target->FPR->cp_hex(hss->si.index)); + test.setAVal("GCD", hss->target->FPR->cp_hex(seq_gcd)); + test.setAVal("ISR", hss->target->FPR->cp_hex((unsigned int) seq_rate)); + } else if (hss->si.responses > 0) { + if (o.debugging) + log_write(LOG_PLAIN, "Insufficient responses from %s for TCP sequencing (%d), OS detection may be less accurate\n", hss->target->targetipstr(), hss->si.responses); + } + + /* Now it is time to deal with IPIDs */ + good_tcp_ipid_num = 0; + good_tcp_closed_ipid_num = 0; + good_icmp_ipid_num = 0; + + for (i = 0; i < NUM_SEQ_SAMPLES; i++) { + if (hss->ipid.tcp_ipids[i] != 0xffffffff) { + if (good_tcp_ipid_num < i) { + hss->ipid.tcp_ipids[good_tcp_ipid_num] = hss->ipid.tcp_ipids[i]; + } + good_tcp_ipid_num++; + } + + if (hss->ipid.tcp_closed_ipids[i] != 0xffffffff) { + if (good_tcp_closed_ipid_num < i) { + hss->ipid.tcp_closed_ipids[good_tcp_closed_ipid_num] = hss->ipid.tcp_closed_ipids[i]; + } + good_tcp_closed_ipid_num++; + } + + if (hss->ipid.icmp_ipids[i] != 0xffffffff) { + if (good_icmp_ipid_num < i) { + hss->ipid.icmp_ipids[good_icmp_ipid_num] = hss->ipid.icmp_ipids[i]; + } + good_icmp_ipid_num++; + } + } + + if (good_tcp_ipid_num >= 3) { + tcp_ipid_seqclass = get_ipid_sequence_16(good_tcp_ipid_num, hss->ipid.tcp_ipids, islocalhost(hss->target->TargetSockAddr())); + } else { + tcp_ipid_seqclass = IPID_SEQ_UNKNOWN; + } + /* Only print open tcp ipid seqclass in the final report. */ + hss->si.ipid_seqclass = tcp_ipid_seqclass; + + if (good_tcp_closed_ipid_num >= 2) { + tcp_closed_ipid_seqclass = get_ipid_sequence_16(good_tcp_closed_ipid_num, hss->ipid.tcp_closed_ipids, islocalhost(hss->target->TargetSockAddr())); + } else { + tcp_closed_ipid_seqclass = IPID_SEQ_UNKNOWN; + } + + if (good_icmp_ipid_num >= 2) { + icmp_ipid_seqclass = get_ipid_sequence_16(good_icmp_ipid_num, hss->ipid.icmp_ipids, islocalhost(hss->target->TargetSockAddr())); + } else { + icmp_ipid_seqclass = IPID_SEQ_UNKNOWN; + } + + /* This fills in TI=Z or something like that. */ + test.setAVal("TI", make_aval_ipid_seq(tcp_ipid_seqclass, hss->ipid.tcp_ipids, hss)); + test.setAVal("CI", make_aval_ipid_seq(tcp_closed_ipid_seqclass, hss->ipid.tcp_closed_ipids, hss)); + test.setAVal("II", make_aval_ipid_seq(icmp_ipid_seqclass, hss->ipid.icmp_ipids, hss)); + + /* SS: Shared IP ID sequence boolean */ + if ((tcp_ipid_seqclass == IPID_SEQ_INCR || + tcp_ipid_seqclass == IPID_SEQ_BROKEN_INCR || + tcp_ipid_seqclass == IPID_SEQ_RPI) && + (icmp_ipid_seqclass == IPID_SEQ_INCR || + icmp_ipid_seqclass == IPID_SEQ_BROKEN_INCR || + icmp_ipid_seqclass == IPID_SEQ_RPI)) { + /* Both are incremental. Thus we have "SS" test. Check if they + are in the same sequence. */ + u32 avg = (hss->ipid.tcp_ipids[good_tcp_ipid_num - 1] - hss->ipid.tcp_ipids[0]) / (good_tcp_ipid_num - 1); + if (hss->ipid.icmp_ipids[0] < hss->ipid.tcp_ipids[good_tcp_ipid_num - 1] + 3 * avg) { + test.setAVal("SS", "S"); + } else { + test.setAVal("SS", "O"); + } + } + + /* Now we look at TCP Timestamp sequence prediction */ + /* Battle plan: + 1) Compute average increments per second, and variance in incr. per second + 2) If any are 0, set to constant + 3) If variance is high, set to random incr. [ skip for now ] + 4) if ~10/second, set to appropriate thing + 5) Same with ~100/sec + */ + if (hss->si.ts_seqclass == TS_SEQ_UNKNOWN && hss->si.responses >= 2) { + time_t uptime = 0; + avg_ts_hz = 0.0; + for (i = 0; i < hss->si.responses - 1; i++) { + double dhz; + + dhz = (double) ts_diffs[i] / (time_usec_diffs[i] / 1000000.0); + /* printf("ts incremented by %d in %li usec -- %fHZ\n", ts_diffs[i], time_usec_diffs[i], dhz); */ + avg_ts_hz += dhz / (hss->si.responses - 1); + } + + if (avg_ts_hz > 0 && avg_ts_hz < 5.66) { /* relatively wide range because sampling time so short and frequency so slow */ + hss->si.ts_seqclass = TS_SEQ_2HZ; + uptime = hss->si.timestamps[0] / 2; + } + else if (avg_ts_hz > 70 && avg_ts_hz < 150) { + hss->si.ts_seqclass = TS_SEQ_100HZ; + uptime = hss->si.timestamps[0] / 100; + } + else if (avg_ts_hz > 724 && avg_ts_hz < 1448) { + hss->si.ts_seqclass = TS_SEQ_1000HZ; + uptime = hss->si.timestamps[0] / 1000; + } + else if (avg_ts_hz > 0) { + hss->si.ts_seqclass = TS_SEQ_OTHER_NUM; + uptime = hss->si.timestamps[0] / (unsigned int)(0.5 + avg_ts_hz); + } + + if (uptime > 63072000) { + /* Up 2 years? Perhaps, but they're probably lying. */ + if (o.debugging) { + /* long long is probably excessive for number of days, but sick of + * truncation warnings and finding the right format string for time_t + */ + log_write(LOG_STDOUT, "Ignoring claimed %s uptime of %lld days\n", + hss->target->targetipstr(), (long long) (uptime / 86400)); + } + uptime = 0; + } + hss->si.lastboot = hss->seq_send_times[0].tv_sec - uptime; + } + + switch (hss->si.ts_seqclass) { + + case TS_SEQ_ZERO: + test.setAVal("TS", "0"); + break; + case TS_SEQ_2HZ: + case TS_SEQ_100HZ: + case TS_SEQ_1000HZ: + case TS_SEQ_OTHER_NUM: + /* Here we "cheat" a little to make the classes correspond more + closely to common real-life frequencies (particularly 100) + which aren't powers of two. */ + if (avg_ts_hz <= 5.66) { + /* 1 would normally range from 1.4 - 2.82, but we expand that + to 0 - 5.66, so we won't ever even get a value of 2. Needs + to be wide because our test is so fast that it is hard to + match slow frequencies exactly. */ + tsnewval = 1; + } else if (avg_ts_hz > 70 && avg_ts_hz <= 150) { + /* mathematically 7 would be 90.51 - 181, but we change to 70-150 to + better align with common freq 100 */ + tsnewval = 7; + } else if (avg_ts_hz > 150 && avg_ts_hz <= 350) { + /* would normally be 181 - 362. Now aligns better with 200 */ + tsnewval = 8; + } else { + /* Do a log base2 rounded to nearest int */ + tsnewval = (unsigned int)(0.5 + log(avg_ts_hz) / log(2.0)); + } + + test.setAVal("TS", hss->target->FPR->cp_hex(tsnewval)); + break; + case TS_SEQ_UNSUPPORTED: + test.setAVal("TS", "U"); + break; + } +} + + +void HostOsScan::makeTOpsFP(HostOsScanStats *hss) { + assert(hss); + int i, n; + + if (hss->TOpsReplyNum != 6) + return; + + for (n = 0; n < 6; n++) { + if (!hss->TOps_AVs[n]) + break; + } + if (n < 6) { + if (o.debugging) + error("We didn't get all the TOps replies from %s", hss->target->targetipstr()); + return; + } + + hss->FP_TOps = new FingerTest(FingerPrintDef::OPS, *o.reference_FPs->MatchPoints); + std::vector<const char *> &results = *hss->FP_TOps->results; + + for (i = 0; i < n; i++) + results[i] = hss->TOps_AVs[i]; +} + + +void HostOsScan::makeTWinFP(HostOsScanStats *hss) { + assert(hss); + int i, n; + + if (hss->TWinReplyNum != 6) + return; + + for (n = 0; n < 6; n++) { + if (!hss->TWin_AVs[n]) + break; + } + if (n < 6) { + if (o.debugging) + error("We didn't get all the TWin replies from %s", hss->target->targetipstr()); + return; + } + + hss->FP_TWin = new FingerTest(FingerPrintDef::WIN, *o.reference_FPs->MatchPoints); + std::vector<const char *> &results = *hss->FP_TWin->results; + + for (i = 0; i < n; i++) + results[i] = hss->TWin_AVs[i]; +} + + +bool HostOsScan::processTSeqResp(HostOsScanStats *hss, const struct ip *ip, int replyNo) { + assert(replyNo >= 0 && replyNo < NUM_SEQ_SAMPLES); + + const struct tcp_hdr *tcp; + int seq_response_num; /* response # for sequencing */ + u32 timestamp = 0; /* TCP timestamp we receive back */ + + if (hss->lastipid != 0 && ip->ip_id == hss->lastipid) { + /* Probably a duplicate -- this happens sometimes when scanning localhost */ + return false; + } + hss->lastipid = ip->ip_id; + + tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + + if ((tcp->th_flags & TH_RST)) { + if (hss->si.responses == 0) { + error("WARNING: RST from %s port %d -- is this port really open?", + hss->target->targetipstr(), hss->openTCPPort); + } + return false; + } + + if ((tcp->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { + /* error("DEBUG: response is SYN|ACK to port %hu\n", ntohs(tcp->th_dport)); */ + /*readtcppacket((char *)ip, ntohs(ip->ip_len));*/ + /* We use the ACK value to match up our sent with rcv'd packets */ + seq_response_num = ntohl(tcp->th_ack) - tcpSeqBase - 1; + /* printf("seq_response_num = %d\treplyNo = %d\n", seq_response_num, replyNo); */ + + if (seq_response_num != replyNo) { + /* BzzT! Value out of range */ + if (o.debugging) { + error("Unable to associate os scan response with sent packet for %s.", + hss->target->targetipstr()); + error("Received ack: %lX; sequence sent: %lX. Packet:", + (unsigned long) ntohl(tcp->th_ack), + (unsigned long) tcpSeqBase); + readtcppacket((unsigned char *)ip, ntohs(ip->ip_len)); + } + seq_response_num = replyNo; + } + + if (hss->si.seqs[seq_response_num] == 0) { + /* New response found! */ + hss->si.responses++; + hss->si.seqs[seq_response_num] = ntohl(tcp->th_seq); /* TCP ISN */ + hss->si.ipids[seq_response_num] = ntohs(ip->ip_id); + + if ((gettcpopt_ts(tcp, ×tamp, NULL) == 0)) + hss->si.ts_seqclass = TS_SEQ_UNSUPPORTED; + else { + if (timestamp == 0) { + hss->si.ts_seqclass = TS_SEQ_ZERO; + } + } + hss->si.timestamps[seq_response_num] = timestamp; + /* printf("Response #%d -- ipid=%hu ts=%i\n", seq_response_num, ntohs(ip->ip_id), timestamp); */ + + return true; + } + } + + return false; +} + + +bool HostOsScan::processTOpsResp(HostOsScanStats *hss, const struct tcp_hdr *tcp, int replyNo) { + assert(replyNo >= 0 && replyNo < 6); + char ops_buf[256]; + + if (hss->FP_TOps || hss->TOps_AVs[replyNo]) + return false; + + int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); + + if (opsParseResult <= 0) { + if (opsParseResult < 0 && o.debugging) + error("Option parse error for TOps response %d from %s.", replyNo, hss->target->targetipstr()); + hss->TOps_AVs[replyNo] = ""; + } + else { + hss->TOps_AVs[replyNo] = hss->target->FPR->cp_dup(ops_buf, opsParseResult); + } + + hss->TOpsReplyNum++; + return true; +} + + +bool HostOsScan::processTWinResp(HostOsScanStats *hss, const struct tcp_hdr *tcp, int replyNo) { + assert(replyNo >= 0 && replyNo < 6); + + if (hss->FP_TWin || hss->TWin_AVs[replyNo]) + return false; + + hss->TWin_AVs[replyNo] = hss->target->FPR->cp_hex(ntohs(tcp->th_win)); + + hss->TWinReplyNum++; + return true; +} + + +bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip) { + char ops_buf[256]; + char quirks_buf[10]; + char *p; + const struct tcp_hdr *tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + + if (hss->FP_TEcn) + return false; + + /* Create the Avals */ + hss->FP_TEcn = new FingerTest(FingerPrintDef::ECN, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TEcn; + + test.setAVal("R", "Y"); + + /* don't frag flag */ + if (ntohs(ip->ip_off) & IP_DF) + test.setAVal("DF", "Y"); + else + test.setAVal("DF", "N"); + + /* TTL */ + test.setAVal("T", hss->target->FPR->cp_hex(ip->ip_ttl)); + + /* TCP Window size */ + test.setAVal("W", hss->target->FPR->cp_hex(ntohs(tcp->th_win))); + + /* Now for the TCP options ... */ + int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); + + if (opsParseResult <= 0) { + if (opsParseResult < 0 && o.debugging) + error("Option parse error for ECN response from %s.", hss->target->targetipstr()); + test.setAVal("O", ""); + } + else { + test.setAVal("O", hss->target->FPR->cp_dup(ops_buf, opsParseResult)); + } + + /* Explicit Congestion Notification support test */ + if ((tcp->th_flags & TH_ECE) && (tcp->th_flags & TH_CWR)) + /* echo back */ + test.setAVal("CC", "S"); + else if (tcp->th_flags & TH_ECE) + /* support */ + test.setAVal("CC", "Y"); + else if (!(tcp->th_flags & TH_CWR)) + /* not support */ + test.setAVal("CC", "N"); + else + test.setAVal("CC", "O"); + + /* TCP miscellaneous quirks test */ + p = quirks_buf; + if (tcp->th_x2) { + /* Reserved field of TCP is not zero */ + assert(p + 1 < quirks_buf + sizeof(quirks_buf)); + *p++ = 'R'; + } + if (!(tcp->th_flags & TH_URG) && tcp->th_urp) { + /* URG pointer value when urg flag not set */ + assert(p + 1 < quirks_buf + sizeof(quirks_buf)); + *p++ = 'U'; + } + *p = '\0'; + test.setAVal("Q", hss->target->FPR->cp_dup(quirks_buf, p - quirks_buf)); + + return true; +} + + +bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int replyNo) { + assert(replyNo >= 0 && replyNo < 7); + + const struct tcp_hdr *tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + + int i; + int length; + char flags_buf[10]; + char quirks_buf[10]; + char *p; + FingerPrintDef::TestID testid = INT2ID(ID2INT(FingerPrintDef::T1) + replyNo); + + if (hss->FPtests[FP_T1_7_OFF + replyNo]) + return false; + + hss->FPtests[FP_T1_7_OFF + replyNo] = new FingerTest(testid, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FPtests[FP_T1_7_OFF + replyNo]; + + /* First we give the "response" flag to say we did actually receive + a packet -- this way we won't match a template with R=N */ + test.setAVal("R", "Y"); + + /* Next we check whether the Don't Fragment bit is set */ + if (ntohs(ip->ip_off) & IP_DF) + test.setAVal("DF", "Y"); + else + test.setAVal("DF", "N"); + + /* TTL */ + test.setAVal("T", hss->target->FPR->cp_hex(ip->ip_ttl)); + + if (replyNo != 0) { + /* Now we do the TCP Window size */ + test.setAVal("W", hss->target->FPR->cp_hex(ntohs(tcp->th_win))); + } + + /* Seq test values: + Z = zero + A = same as ack + A+ = ack + 1 + O = other + */ + if (ntohl(tcp->th_seq) == 0) + test.setAVal("S", "Z"); + else if (ntohl(tcp->th_seq) == tcpAck) + test.setAVal("S", "A"); + else if (ntohl(tcp->th_seq) == tcpAck + 1) + test.setAVal("S", "A+"); + else + test.setAVal("S", "O"); + + /* ACK test values: + Z = zero + S = same as syn + S+ = syn + 1 + O = other + */ + if (ntohl(tcp->th_ack) == 0) + test.setAVal("A", "Z"); + else if (ntohl(tcp->th_ack) == tcpSeqBase) + test.setAVal("A", "S"); + else if (ntohl(tcp->th_ack) == tcpSeqBase + 1) + test.setAVal("A", "S+"); + else + test.setAVal("A", "O"); + + /* Flags. They must be in this order: + E = ECN Echo + U = Urgent + A = Acknowledgement + P = Push + R = Reset + S = Synchronize + F = Final + */ + struct { + u8 flag; + char c; + } flag_defs[] = { + { TH_ECE, 'E' }, + { TH_URG, 'U' }, + { TH_ACK, 'A' }, + { TH_PUSH, 'P' }, + { TH_RST, 'R' }, + { TH_SYN, 'S' }, + { TH_FIN, 'F' }, + }; + assert(sizeof(flag_defs) / sizeof(flag_defs[0]) < sizeof(flags_buf)); + p = flags_buf; + for (i = 0; i < (int) (sizeof(flag_defs) / sizeof(flag_defs[0])); i++) { + if (tcp->th_flags & flag_defs[i].flag) + *p++ = flag_defs[i].c; + } + *p = '\0'; + test.setAVal("F", hss->target->FPR->cp_dup(flags_buf, p - flags_buf)); + + if (replyNo != 0) { + char ops_buf[256]; + + /* Now for the TCP options ... */ + int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); + if (opsParseResult <= 0) { + if (opsParseResult < 0 && o.debugging) + error("Option parse error for T%d response from %s.", replyNo, hss->target->targetipstr()); + test.setAVal("O", ""); + } + else { + test.setAVal("O", hss->target->FPR->cp_dup(ops_buf, opsParseResult)); + } + } + + /* Rst Data CRC32 */ + length = (int) ntohs(ip->ip_len) - 4 * ip->ip_hl -4 * tcp->th_off; + if ((tcp->th_flags & TH_RST) && length>0) { + test.setAVal("RD", hss->target->FPR->cp_hex(nbase_crc32(((u8 *)tcp) + 4 * tcp->th_off, length))); + } else { + test.setAVal("RD", "0"); + } + + /* TCP miscellaneous quirks test */ + p = quirks_buf; + if (tcp->th_x2) { + /* Reserved field of TCP is not zero */ + assert(p + 1 < quirks_buf + sizeof(quirks_buf)); + *p++ = 'R'; + } + if (!(tcp->th_flags & TH_URG) && tcp->th_urp) { + /* URG pointer value when urg flag not set */ + assert(p + 1 < quirks_buf + sizeof(quirks_buf)); + *p++ = 'U'; + } + *p = '\0'; + test.setAVal("Q", hss->target->FPR->cp_dup(quirks_buf, p - quirks_buf)); + + return true; +} + + +bool HostOsScan::processTUdpResp(HostOsScanStats *hss, const struct ip *ip) { + + assert(hss); + assert(ip); + + const struct icmp *icmp; + const struct ip *ip2; + unsigned short checksum; + unsigned short *checksumptr; + const struct udp_hdr *udp; + const unsigned char *datastart, *dataend; + + if (hss->FP_TUdp) + return false; + + icmp = ((struct icmp *)(((char *) ip) + 4 * ip->ip_hl)); + + /* Make sure this is icmp port unreachable. */ + assert(icmp->icmp_type == 3 && icmp->icmp_code == 3); + + ip2 = (struct ip*)((char *)icmp + 8); + udp = (struct udp_hdr *)((char *)ip2 + 4 * ip2->ip_hl); + + /* The ports should match. */ + if (ntohs(udp->uh_sport) != hss->upi.sport || ntohs(udp->uh_dport) != hss->upi.dport) { + return false; + } + + hss->FP_TUdp = new FingerTest(FingerPrintDef::U1, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TUdp; + + /* First of all, if we got this far the response was yes */ + test.setAVal("R", "Y"); + + /* Also, we now know that the port we reached was closed */ + if (hss->target->FPR->osscan_closedudpport == -1) + hss->target->FPR->osscan_closedudpport = hss->upi.dport; + + /* Now let us do an easy one, Don't fragment */ + if (ntohs(ip->ip_off) & IP_DF) + test.setAVal("DF", "Y"); + else + test.setAVal("DF", "N"); + + /* TTL */ + test.setAVal("T", hss->target->FPR->cp_hex(ip->ip_ttl)); + + /* Now we look at the IP datagram length that was returned, some + machines send more of the original packet back than others */ + test.setAVal("IPL", hss->target->FPR->cp_hex(ntohs(ip->ip_len))); + + /* unused filed not zero in Destination Unreachable Message */ + test.setAVal("UN", hss->target->FPR->cp_hex(ntohl(icmp->icmp_void))); + + /* OK, lets check the returned IP length, some systems @$@ this + up */ + if (ntohs(ip2->ip_len) == 328) + test.setAVal("RIPL", "G"); + else + test.setAVal("RIPL", hss->target->FPR->cp_hex(ntohs(ip2->ip_len))); + + /* This next test doesn't work on Solaris because the lamers + overwrite our ip_id */ +#if !defined(SOLARIS) && !defined(SUNOS) && !defined(IRIX) && !defined(HPUX) + + /* Now lets see how they treated the ID we sent ... */ + if (ntohs(ip2->ip_id) == hss->upi.ipid) + test.setAVal("RID", "G"); /* The good "expected" value */ + else + test.setAVal("RID", hss->target->FPR->cp_hex(ntohs(ip2->ip_id))); + +#endif + + /* Let us see if the IP checksum we got back computes */ + + /* Thanks to some machines not having struct ip member ip_sum we + have to go with this BS */ + checksumptr = (unsigned short *) ((char *) ip2 + 10); + checksum = *checksumptr; + + if (checksum == 0) { + test.setAVal("RIPCK", "Z"); + } else { + *checksumptr = 0; + if (in_cksum((unsigned short *)ip2, 20) == checksum) { + test.setAVal("RIPCK", "G"); /* The "expected" good value */ + } else { + test.setAVal("RIPCK", "I"); /* They modified it */ + } + *checksumptr = checksum; + } + + /* UDP checksum */ + if (udp->uh_sum == hss->upi.udpck) + test.setAVal("RUCK", "G"); /* The "expected" good value */ + else + test.setAVal("RUCK", hss->target->FPR->cp_hex(ntohs(udp->uh_sum))); + + /* Finally we ensure the data is OK */ + datastart = ((unsigned char *)udp) + 8; + dataend = (unsigned char *) ip + ntohs(ip->ip_len); + + while (datastart < dataend) { + if (*datastart != hss->upi.patternbyte) + break; + datastart++; + } + if (datastart < dataend) + test.setAVal("RUD", "I"); /* They modified it */ + else + test.setAVal("RUD", "G"); + + /* Count hop count */ + if (hss->distance == -1) { + hss->distance = this->udpttl - ip2->ip_ttl + 1; + } + + return true; +} + + +bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, int replyNo) { + assert(replyNo == 0 || replyNo == 1); + + const struct ip *ip1, *ip2; + const struct icmp *icmp1, *icmp2; + unsigned short value1, value2; + + if (hss->FP_TIcmp) + return false; + + if (hss->icmpEchoReply == NULL) { + /* This is the first icmp reply we get, store it and return. */ + hss->icmpEchoReply = (struct ip *) safe_malloc(ntohs(ip->ip_len)); + memcpy(hss->icmpEchoReply, ip, ntohs(ip->ip_len)); + hss->storedIcmpReply = replyNo; + return true; + } else if (hss->storedIcmpReply == replyNo) { + /* This is a duplicated icmp reply. */ + return false; + } + + /* Ok, now we get another reply. */ + if (hss->storedIcmpReply == 0) { + ip1 = hss->icmpEchoReply; + ip2 = ip; + } else { + ip1 = ip; + ip2 = hss->icmpEchoReply; + } + + icmp1 = ((struct icmp *)(((char *) ip1) + 4 * ip1->ip_hl)); + icmp2 = ((struct icmp *)(((char *) ip2) + 4 * ip2->ip_hl)); + + assert(icmp1->icmp_type == 0 && icmp2->icmp_type == 0); + + hss->FP_TIcmp= new FingerTest(FingerPrintDef::IE, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TIcmp; + + test.setAVal("R", "Y"); + + /* DFI test values: + * Y. Both set DF; + * S. Both use the DF that the sender uses; + * N. Both not set; + * O. Other(both different with the sender, -_-b). + */ + value1 = (ntohs(ip1->ip_off) & IP_DF); + value2 = (ntohs(ip2->ip_off) & IP_DF); + if (value1 && value2) + /* both set */ + test.setAVal("DFI", "Y"); + else if (value1 && !value2) + /* echo back */ + test.setAVal("DFI", "S"); + else if (!value1 && !value2) + /* neither set */ + test.setAVal("DFI", "N"); + else + test.setAVal("DFI", "O"); + + /* TTL */ + + test.setAVal("T", hss->target->FPR->cp_hex(ip1->ip_ttl)); + + /* ICMP Code value. Test values: + * [Value]. Both set Code to the same value [Value]; + * S. Both use the Code that the sender uses; + * O. Other. + */ + value1 = icmp1->icmp_code; + value2 = icmp2->icmp_code; + if (value1 == value2) { + if (value1 == 0) + test.setAVal("CD", "Z"); + else + test.setAVal("CD", hss->target->FPR->cp_hex(value1)); + } + else if (value1 == 9 && value2 == 0) + /* both the same as in the corresponding probe */ + test.setAVal("CD", "S"); + else + test.setAVal("CD", "O"); + + return true; +} + + +int HostOsScan::get_tcpopt_string(const struct tcp_hdr *tcp, int mss, char *result, int maxlen) const { + char *p; + const char *q; + u16 tmpshort; + u32 tmpword; + int length; + int opcode; + + p = result; + length = (tcp->th_off * 4) - sizeof(struct tcp_hdr); + q = ((char *)tcp) + sizeof(struct tcp_hdr); + + /* + * Example parsed result: M5B4ST11NW2 + * MSS, Sack Permitted, Timestamp with both value not zero, Nop, WScale with value 2 + */ + + /* Be aware of the max increment value for p in parsing, + * now is 5 = strlen("Mxxxx") <-> MSS Option + */ + while (length > 0 && (p - result) < (maxlen - 5)) { + opcode = *q++; + if (!opcode) { /* End of List */ + *p++ = 'L'; + length--; + } else if (opcode == 1) { /* No Op */ + *p++ = 'N'; + length--; + } else if (opcode == 2) { /* MSS */ + if (length < 4) + break; /* MSS has 4 bytes */ + *p++ = 'M'; + q++; + memcpy(&tmpshort, q, 2); + /* if (ntohs(tmpshort) == mss) */ + /* *p++ = 'E'; */ + sprintf(p, "%hX", ntohs(tmpshort)); + p += strlen(p); /* max movement of p is 4 (0xFFFF) */ + q += 2; + length -= 4; + } else if (opcode == 3) { /* Window Scale */ + if (length < 3) + break; /* Window Scale option has 3 bytes */ + *p++ = 'W'; + q++; + snprintf(p, length, "%hhX", *((u8*)q)); + p += strlen(p); /* max movement of p is 2 (max WScale value is 0xFF) */ + q++; + length -= 3; + } else if (opcode == 4) { /* SACK permitted */ + if (length < 2) + break; /* SACK permitted option has 2 bytes */ + *p++ = 'S'; + q++; + length -= 2; + } else if (opcode == 8) { /* Timestamp */ + if (length < 10) + break; /* Timestamp option has 10 bytes */ + *p++ = 'T'; + q++; + memcpy(&tmpword, q, 4); + if (tmpword) + *p++ = '1'; + else + *p++ = '0'; + q += 4; + memcpy(&tmpword, q, 4); + if (tmpword) + *p++ = '1'; + else + *p++ = '0'; + q += 4; + length -= 10; + } + } + + if (length > 0) { + /* We could reach here for one of the two reasons: + * 1. At least one option is not correct. (Eg. Should have 4 bytes but only has 3 bytes left). + * 2. The option string is too long. + */ + *result = '\0'; + return -1; + } + + *p = '\0'; + return p - result; +} + + +/****************************************************************************** + * Implementation of class HostOsScanInfo * + ******************************************************************************/ + +HostOsScanInfo::HostOsScanInfo(Target *t, OsScanInfo *OsSI) { + target = t; + OSI = OsSI; + + FPs = (FingerPrint **) safe_zalloc(o.maxOSTries() * sizeof(FingerPrint *)); + FP_matches = new FingerPrintResultsIPv4[o.maxOSTries()]; + timedOut = false; + isCompleted = false; + + if (target->FPR == NULL) { + this->FPR = new FingerPrintResultsIPv4; + target->FPR = this->FPR; + } + target->osscanSetFlag(OS_PERF); + + hss = new HostOsScanStats(t); +} + + +HostOsScanInfo::~HostOsScanInfo() { + delete hss; + free(FPs); + delete[] FP_matches; +} + + +/****************************************************************************** + * Implementation of class OsScanInfo * + ******************************************************************************/ + +OsScanInfo::OsScanInfo(std::vector<Target *> &Targets) { + unsigned int targetno; + HostOsScanInfo *hsi; + int num_timedout = 0; + + gettimeofday(&now, NULL); + + numInitialTargets = 0; + + /* build up incompleteHosts list */ + for (targetno = 0; targetno < Targets.size(); targetno++) { + /* check if Targets[targetno] is good to be scanned + * if yes, append it to the list + */ + if (Targets[targetno]->timedOut(&now)) { + num_timedout++; + continue; + } + +#ifdef WIN32 + if (g_has_npcap_loopback == 0 && Targets[targetno]->ifType() == devt_loopback) { + log_write(LOG_STDOUT, "Skipping OS Scan against %s because it doesn't work against your own machine (localhost)\n", Targets[targetno]->NameIP()); + continue; + } +#endif + + if (Targets[targetno]->ports.getStateCounts(IPPROTO_TCP, PORT_OPEN) == 0 || + (Targets[targetno]->ports.getStateCounts(IPPROTO_TCP, PORT_CLOSED) == 0 && + Targets[targetno]->ports.getStateCounts(IPPROTO_TCP, PORT_UNFILTERED) == 0)) { + if (o.osscan_limit) { + if (o.verbose) + log_write(LOG_PLAIN, "Skipping OS Scan against %s due to absence of open (or perhaps closed) ports\n", Targets[targetno]->NameIP()); + continue; + } else { + Targets[targetno]->osscanSetFlag(OS_PERF_UNREL); + } + } + + hsi = new HostOsScanInfo(Targets[targetno], this); + incompleteHosts.push_back(hsi); + numInitialTargets++; + } + + nextI = incompleteHosts.begin(); +} + + +OsScanInfo::~OsScanInfo() +{ + while (!incompleteHosts.empty()) { + delete incompleteHosts.front(); + incompleteHosts.pop_front(); + } +} + + +/* Find a HostScanStats by IP its address in the incomplete list. Returns NULL if + none are found. */ +HostOsScanInfo *OsScanInfo::findIncompleteHost(const struct sockaddr_storage *ss) { + std::list<HostOsScanInfo *>::iterator hostI; + const struct sockaddr_in *sin = (struct sockaddr_in *) ss; + + if (sin->sin_family != AF_INET) + fatal("%s passed a non IPv4 address", __func__); + + for (hostI = incompleteHosts.begin(); hostI != incompleteHosts.end(); hostI++) { + if ((*hostI)->target->v4hostip()->s_addr == sin->sin_addr.s_addr) + return *hostI; + } + return NULL; +} + + +/* 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. */ +HostOsScanInfo *OsScanInfo::nextIncompleteHost() { + HostOsScanInfo *nxt; + + if (incompleteHosts.empty()) + return NULL; + + nxt = *nextI; + nextI++; + if (nextI == incompleteHosts.end()) + nextI = incompleteHosts.begin(); + + return nxt; +} + + +/* Removes any hosts that have completed their scans from the incompleteHosts + list. Returns the number of hosts removed. */ +int OsScanInfo::removeCompletedHosts() { + std::list<HostOsScanInfo *>::iterator hostI, nxt; + HostOsScanInfo *hsi = NULL; + int hostsRemoved = 0; + bool timedout = false; + + for (hostI = incompleteHosts.begin(); hostI != incompleteHosts.end(); + hostI = nxt) { + nxt = hostI; + nxt++; + hsi = *hostI; + timedout = hsi->target->timedOut(&now); + if (hsi->isCompleted || timedout) { + /* 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 && numInitialTargets > 50) { + int remain = incompleteHosts.size() - 1; + if (remain && !timedout) + log_write(LOG_STDOUT, "Completed os scan against %s in %.3fs (%d %s)\n", + hsi->target->targetipstr(), + o.TimeSinceStart() - this->starttime, remain, + (remain == 1)? "host left" : "hosts left"); + else if (timedout) + log_write(LOG_STDOUT, "%s timed out during os scan (%d %s)\n", + hsi->target->targetipstr(), remain, + (remain == 1)? "host left" : "hosts left"); + } + incompleteHosts.erase(hostI); + hostsRemoved++; + hsi->target->stopTimeOutClock(&now); + delete hsi; + } + } + return hostsRemoved; +} + +/****************************************************************************** + * Implementation of class OSScan() * + ******************************************************************************/ + +/* Constructor */ +OSScan::OSScan() { + this->reset(); + return; +} + +/* Destructor */ +OSScan::~OSScan() { + return; +} + +/* Function that initializes internal variables */ +void OSScan::reset() { + +} + + +/* This function takes a group of targets and divides it in chunks if there are + * too many to be processed at the same time. The threshold is based on Nmap's + * timing level (when timing level is above 4, no chunking is performed). + * The reason targets are processed in smaller groups is to improve accuracy. */ +int OSScan::chunk_and_do_scan(std::vector<Target *> &Targets, int family) { + unsigned int max_os_group_sz = 20; + double fudgeratio = 1.2; /* Allow a slightly larger final group rather than finish with a tiny one */ + std::vector<Target *> tmpTargets; + unsigned int startidx = 0; + + if (o.timing_level == 4) + max_os_group_sz = (unsigned int) (max_os_group_sz * 1.5); + + if (o.timing_level > 4 || Targets.size() <= max_os_group_sz * fudgeratio) { + if (family == AF_INET6) + os_scan_ipv6(Targets); + else + os_scan_ipv4(Targets); + return OP_SUCCESS; + } + + /* We need to split it up */ + while (startidx < Targets.size()) { + int diff = Targets.size() - startidx; + if (diff > max_os_group_sz * fudgeratio) { + diff = max_os_group_sz; + } + tmpTargets.assign(Targets.begin() + startidx, Targets.begin() + startidx + diff); + if (family == AF_INET6) + os_scan_ipv6(Targets); + else + os_scan_ipv4(Targets); + startidx += diff; + } + return OP_SUCCESS; +} + + +/* Performs the OS detection for IPv4 hosts. This method should not be called + * directly. os_scan() should be used instead, as it handles chunking so + * you don't do too many targets in parallel */ +int OSScan::os_scan_ipv4(std::vector<Target *> &Targets) { + int itry = 0; + /* Hosts which haven't matched and have been removed from incompleteHosts because + * they have exceeded the number of retransmissions the host is allowed. */ + std::list<HostOsScanInfo *> unMatchedHosts; + + /* Check we have at least one target*/ + if (Targets.size() == 0) { + return OP_FAILURE; + } + + perf.init(); + + OsScanInfo OSI(Targets); + if (OSI.numIncompleteHosts() == 0) { + /* no one will be scanned */ + return OP_FAILURE; + } + OSI.starttime = o.TimeSinceStart(); + + HostOsScan HOS(Targets[0]); + + /* Initialize the pcap session handler in HOS */ + begin_sniffer(&HOS, Targets); + while (OSI.numIncompleteHosts() != 0) { +#ifdef WIN32 + // Reset system idle timer to avoid going to sleep + SetThreadExecutionState(ES_SYSTEM_REQUIRED); +#endif + if (itry > 0) + sleep(1); + if (itry == 3) + usleep(1500000); /* Try waiting a little longer just in case it matters */ + if (o.verbose) { + char targetstr[128]; + bool plural = (OSI.numIncompleteHosts() != 1); + if (!plural) { + (*(OSI.incompleteHosts.begin()))->target->NameIP(targetstr, sizeof(targetstr)); + } else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) OSI.numIncompleteHosts()); + log_write(LOG_STDOUT, "%s OS detection (try #%d) against %s\n", (itry == 0)? "Initiating" : "Retrying", itry + 1, targetstr); + log_flush_all(); + } + startRound(&OSI, &HOS, itry); + doSeqTests(&OSI, &HOS); + doTUITests(&OSI, &HOS); + endRound(&OSI, &HOS, itry); + expireUnmatchedHosts(&OSI, &unMatchedHosts); + itry++; + } + + /* Now move the unMatchedHosts array back to IncompleteHosts */ + if (!unMatchedHosts.empty()) + OSI.incompleteHosts.splice(OSI.incompleteHosts.begin(), unMatchedHosts); + + if (OSI.numIncompleteHosts()) { + /* For hosts that don't have a perfect match, find the closest fingerprint + * in the DB and, if we are in debugging mode, print them. */ + findBestFPs(&OSI); + if (o.debugging > 1) + printFP(&OSI); + } + + return OP_SUCCESS; +} + + +/* Performs the OS detection for IPv6 hosts. This method should not be called + * directly. os_scan() should be used instead, as it handles chunking so + * you don't do too many targets in parallel */ +int OSScan::os_scan_ipv6(std::vector<Target *> &Targets) { + + /* Object instantiation */ + FPEngine6 fp6; + + /* Safe checks. */ + if (Targets.size() == 0) { + return OP_FAILURE; + } + + return fp6.os_scan(Targets); +} + + +/* This function performs the OS detection. It processes the supplied list of + * targets and classifies it into two groups: IPv4 and IPv6 targets. Then, + * OS detection is carried out for those two separate groups. It returns + * OP_SUCCESS on success or OP_FAILURE in case of error. */ +int OSScan::os_scan(std::vector<Target *> &Targets) { + std::vector<Target *> ip4_targets; + std::vector<Target *> ip6_targets; + int res4 = OP_SUCCESS, res6 = OP_SUCCESS; + + /* Make sure we have at least one target */ + if (Targets.size() <= 0) + return OP_FAILURE; + + /* Classify targets into two groups: IPv4 and IPv6 */ + for (size_t i = 0; i < Targets.size(); i++) { + if (Targets[i]->af() == AF_INET6) + ip6_targets.push_back(Targets[i]); + else + ip4_targets.push_back(Targets[i]); + } + + /* Do IPv4 OS Detection */ + if (ip4_targets.size() > 0) + res4 = this->os_scan_ipv4(ip4_targets); + + /* Do IPv6 OS Detection */ + if (ip6_targets.size() > 0) + res6 = this->os_scan_ipv6(ip6_targets); + + /* If both scans were successful, return OK */ + if (res4 == OP_SUCCESS && res6 == OP_SUCCESS) + return OP_SUCCESS; + else + return OP_FAILURE; +} |