
/***************************************************************************
 * 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-2026 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;

/* 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;
  }

}

/* Identify 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;
  }
}

/* Identify 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 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_BEFORE(tmptv, stime))
            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_BEFORE(tmptv, stime)) {
          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);

      const u8 *ip = readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true);

      gettimeofday(&now, NULL);

      if (!ip && TIMEVAL_BEFORE(stime, now)) {
        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;
      }

      struct ip iphdr;
      if (bytes < sizeof(iphdr))
        continue;
      memcpy(&iphdr, ip, sizeof(iphdr));
      if (iphdr.ip_hl < 5 || bytes < (4 * iphdr.ip_hl) + 4U)
        continue;

      memset(&ss, 0, sizeof(ss));
      ((struct sockaddr_in *) &ss)->sin_addr.s_addr = iphdr.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 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_BEFORE(tmptv, stime))
            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_BEFORE(tmptv, stime)) {
          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);

      const u8 *ip = readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true);

      gettimeofday(&now, NULL);

      if (!ip && TIMEVAL_BEFORE(stime, now)) {
        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;
      }

      struct ip iphdr;
      if (bytes < sizeof(iphdr))
        continue;
      memcpy(&iphdr, ip, sizeof(iphdr));
      if (iphdr.ip_hl < 5 || bytes < (4 * iphdr.ip_hl) + 4U)
        continue;

      memset(&ss, 0, sizeof(ss));
      ((struct sockaddr_in *) &ss)->sin_addr.s_addr = iphdr.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;
    /* Have to calculate timingRatio before calling makeFP, since that can muck
     * with the seq_send_times array. */
    double tr = hsi->hss->timingRatio();
    HOS->makeFP(hsi->hss);

    hsi->FPs[roundNum] = hsi->hss->getFP();
    hsi->FPR->FPs[roundNum] = hsi->FPs[roundNum];
    hsi->FPR->numFPs = roundNum + 1;
    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;
  }

  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();
  }
}


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));

  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, netutil_eth_t *ethsd) const {
  if (ethsd == NULL)
    return NULL;

  eth = target->FillEthNfo(eth, ethsd);
  if (eth) {
    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));

  unsigned long usec_to = timeProbeTimeout(hss);
  for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) {
    TIMEVAL_ADD(probe_to, (*probeI)->sent, usec_to);
    if (firstgood || TIMEVAL_BEFORE(probe_to, earliest_to)) {
      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;
  int sendpref = o.sendpref;

  if (!raw_socket_or_eth(sendpref, t->deviceName(), t->ifType(), &rawsd, &ethsd, AF_INET)) {
    fatal("%s: Failed to open raw socket or ethernet device", __func__);
  }
  if (rawsd >= 0)
    unblock_socket(rawsd);

  if (o.magic_port_set) {
    tcpPortBase = o.magic_port;
    udpPortBase = o.magic_port;
  }
  else {
    /* It would be best if we could get the next base port from UltraScan to
     * avoid colliding with port scan states, but realistically there is enough
     * time between the scan phases and a random 15-bit number is sufficiently
     * unlikely to overlap. */
    // See HostOsScan::reInitScanSystem() for explanation:
#define PRIME_32K 32261
    tcpPortBase = 33000 + get_random_uint() % PRIME_32K;
    udpPortBase = 33000 + get_random_uint() % PRIME_32K;
  }

  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;
  if (!o.magic_port_set) {
    /* Base port must be incremented between rounds because the target port is
     * in SYN-RECEIVED state, so it will continue to ACK the original sequence
     * number and ignore the new one based on tcpSeqBase. Many supported
     * systems send a RST upon receiving the unexpected SYN/ACK, which avoids
     * this problem, but some (including Windows) drop those instead, so we
     * have to change the source port to get a new TCP state. */
    // See UltraScanInfo::increment_base_port() in scan_engine.h for explanation:
    tcpPortBase = 33000 + (tcpPortBase - 33000 + 256) % PRIME_32K;
    udpPortBase = 33000 + (udpPortBase - 33000 + 256) % PRIME_32K;
  }

}


/* 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;
  long usec_to = timeProbeTimeout(hss);

  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) > usec_to) {
      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;
  long usec_to = timeProbeTimeout(hss);

  for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI = nxt) {
    nxt = probeI;
    nxt++;
    probe = *probeI;

    if (TIMEVAL_SUBTRACT(now, probe->sent) > usec_to) {
      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? */
  unsigned long msec_to = timeProbeTimeout(hss) / 1000;
  for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) {
    TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, msec_to);
    if (TIMEVAL_BEFORE(probe_to, earliest_to)) {
      earliest_to = probe_to;
    }
  }

  // Will any scan delay affect this?
  if (hss->sendDelayMs > 0) {
    TIMEVAL_MSEC_ADD(sendTime, hss->lastProbeSent, hss->sendDelayMs);
    if (TIMEVAL_BEFORE(sendTime, now))
      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? */
  unsigned long msec_to = timeProbeTimeout(hss) / 1000;
  for (probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) {
    TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, msec_to);
    if (TIMEVAL_BEFORE(probe_to, earliest_to)) {
      earliest_to = probe_to;
    }
  }

  TIMEVAL_ADD(sendTime, hss->lastProbeSent, maxWait);
  if (TIMEVAL_BEFORE(sendTime, now))
    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 u8 *pkt, unsigned int len, struct timeval *rcvdtime) {
  int testno;
  bool isPktUseful = false;
  std::list<OFProbe *>::iterator probeI;
  OFProbe *probe;
  const u8 *l4pkt = NULL;

  struct ip ip;
  if (len < sizeof(ip))
    return false;
  memcpy(&ip, pkt, sizeof(ip));
  const unsigned int iphlen = 4 * ip.ip_hl;
  if (iphlen < sizeof(ip) || len < iphlen + 4U)
    return false;

  len -= iphlen;
  l4pkt = pkt + iphlen;

  if (ip.ip_p == IPPROTO_TCP) {
    struct tcp_hdr tcp;
    if (len < sizeof(tcp))
      return false;
    memcpy(&tcp, l4pkt, sizeof(tcp));
    const unsigned int tcphlen = 4 * tcp.th_off;
    if (tcphlen < sizeof(tcp) || len < tcphlen)
      return false;
    testno = ntohs(tcp.th_dport) - tcpPortBase;

    if (testno >= 0 && testno < NUM_SEQ_SAMPLES) {
      /* TSeq */
      isPktUseful = processTSeqResp(hss, &ip, pkt, &tcp, l4pkt, len, 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, &tcp, l4pkt, len, 0);
      }
      /* the 1st NUM_SEQ_SAMPLES replies are used to do TOps and TWin */
      processTOpsResp(hss, l4pkt, len, testno);
      processTWinResp(hss, &tcp, testno);

    } else if (testno >= NUM_SEQ_SAMPLES && testno < NUM_SEQ_SAMPLES + 6) {

      /* TOps/Twin */
      isPktUseful = processTOpsResp(hss, l4pkt, len, 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, &tcp, l4pkt, len);
      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, &tcp, l4pkt, len, 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 < ICMP_LEN_MIN)
      return false;
    struct icmp icmp;
    memcpy(&icmp, l4pkt, MIN(len, sizeof(icmp)));

    /* 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, &icmp, 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? */
    else if (icmp.icmp_type == 3 && icmp.icmp_code == 3) {
      isPktUseful = processTUdpResp(hss, &ip, &icmp, l4pkt, len);
      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(&eth, 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(&eth, 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(&eth, 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, const u8 *pkt, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen, int replyNo) {
  assert(replyNo >= 0 && replyNo < NUM_SEQ_SAMPLES);

  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;

  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, 0);*/
    /* 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(pkt, 0);
      }
      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(tcppkt, tcplen, &timestamp, 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 u8 *tcp, int tcplen, 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, tcplen, 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, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen) {
  char ops_buf[256];
  char quirks_buf[10];
  char *p;

  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(tcppkt, tcplen, 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, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen, int replyNo) {
  assert(replyNo >= 0 && replyNo < 7);

  int i;
  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(tcppkt, tcplen, 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 */
  int length = tcplen - 4 * tcp->th_off;
  if ((tcp->th_flags & TH_RST) && length>0) {
    test.setAVal("RD", hss->target->FPR->cp_hex(nbase_crc32(tcppkt + 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, const struct icmp *icmp, const u8 *icmppkt, unsigned int icmplen) {

  assert(hss);
  assert(icmppkt);

  if (hss->FP_TUdp)
    return false;

  /* Make sure this is icmp port unreachable. */
  assert(icmp->icmp_type == 3 && icmp->icmp_code == 3);

  struct ip ip2;
  struct udp_hdr udp;
  if (icmplen < 8 + sizeof(ip2) + sizeof(udp))
    return false;

  const u8 *ip2pkt = icmppkt + 8;
  memcpy(&ip2, ip2pkt, sizeof(ip2));
  unsigned int ip2hlen = 4 * ip2.ip_hl;
  if (ip2hlen < sizeof(ip2) || icmplen < 8 + ip2hlen + sizeof(udp))
    return false;
  const u8 *udppkt = ip2pkt + ip2hlen;
  memcpy(&udp, udppkt, sizeof(udp));

  /* 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 */
  u16 *checksumptr = (u16 *) (ip2pkt + 10);
  u16 checksum = *checksumptr;

  if (checksum == 0) {
    test.setAVal("RIPCK", "Z");
  } else {
    *checksumptr = 0;
    if (in_cksum((const u16 *)ip2pkt, 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 */
  const u8 *datastart = udppkt + 8;
  const u8 *dataend = icmppkt + icmplen;

  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, const struct icmp *icmp, int replyNo) {
  assert(replyNo == 0 || replyNo == 1);

  if (hss->FP_TIcmp)
    return false;

  if (hss->icmpEchoReply[replyNo].received) {
    /* This is a duplicated icmp reply. */
    return false;
  }
  else {
    hss->icmpEchoReply[replyNo].received = true;
    hss->icmpEchoReply[replyNo].ip_off = ip->ip_off;
    hss->icmpEchoReply[replyNo].icmp_code = icmp->icmp_code;
  }
  HostOsScanStats::icmpEchoReply_t &reply1 = hss->icmpEchoReply[0];
  HostOsScanStats::icmpEchoReply_t &reply2 = hss->icmpEchoReply[1];

  if (!reply1.received || !reply2.received) {
    return true;
  }

  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).
   */
  u16 value1 = (ntohs(reply1.ip_off) & IP_DF);
  u16 value2 = (ntohs(reply2.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(ip->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 = reply1.icmp_code;
  value2 = reply2.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;
}

struct tcpopt_string_ctx {
  char *p;
  char *end;
  bool valid;
  tcpopt_string_ctx() : p(NULL), end(NULL), valid(true) {}
  bool check_length(int len) const {
    return (end - p) >= len;
  }
  void put(char c) {
    assert(end > p);
    *p++ = c;
  }
  void put_hex(unsigned int u) {
    int w = sprintf(p, "%X", u);
    p += w;
  }
};

static bool tcpopt_tostring(u8 op, u8 oplen, const u8 *data, void *ctx)
{
  tcpopt_string_ctx *args = static_cast<tcpopt_string_ctx *>(ctx);

  if (!args->check_length(1))
    return false;

  const u8 *q = data + 2;

  switch (op) {
    case 0: /* End of List */
      args->put('L');
      break;
    case 1: /* No Op */
      args->put('N');
      break;
    case 2: /* MSS */
      if (oplen < 4) {
        args->valid = false;
        break; /* MSS has 4 bytes */
      }
      args->put('M');
      if (!args->check_length(4))
        return false;
      args->put_hex((q[0] << 8) + q[1]);
      break;
    case 3:/* Window Scale */
      if (oplen < 3) {
        args->valid = false;
        break; /* Window Scale option has 3 bytes */
      }
      args->put('W');
      if (!args->check_length(2))
        return false;
      args->put_hex(q[0]);
      break;
    case 4:/* SACK permitted */
      if (oplen < 2) {
        args->valid = false;
        break; /* SACK permitted option has 2 bytes */
      }
      args->put('S');
      break;
    case 8: /* Timestamp */
      if (oplen < 10) {
        args->valid = false;
        break; /* Timestamp option has 10 bytes */
      }
      args->put('T');
      if (!args->check_length(2))
        return false;
      args->put((q[0] || q[1] || q[2] || q[3]) ? '1' : '0');
      args->put((q[4] || q[5] || q[6] || q[7]) ? '1' : '0');
      break;
    default:
      break;
  }
  return args->valid;
}

int HostOsScan::get_tcpopt_string(const u8 *tcp, int tcplen, char *result, int maxlen) const {
  assert(tcp);
  assert(result);
  assert(maxlen > 0);
  memset(result, 0, maxlen);

  /*
   * Example parsed result: M5B4ST11NW2
   *   MSS, Sack Permitted, Timestamp with both value not zero, Nop, WScale with value 2
   */

  TCPOptions opts;
  if (!opts.fromTCPPacket(tcp, tcplen))
    return -1;
  tcpopt_string_ctx ctx;
  ctx.p = result;
  ctx.end = result + maxlen - 1;

  if (!opts.foreachOpt(tcpopt_tostring, &ctx) || !ctx.valid) {
    /* 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;
  }
  return ctx.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;
    }

    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;
}
