diff --git a/CHANGELOG b/CHANGELOG
index 1a41474ed..fd138e07b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,7 @@
# Nmap Changelog ($Id$); -*-text-*-
+Nmap 4.20ALPHA3
+
+o Added back Win32 support thanks to a patch by kx (kxmail(a)gmail.com)
Nmap 4.20ALPHA2
diff --git a/Makefile.in b/Makefile.in
index 643439d8a..585b4be43 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-export NMAP_VERSION = 4.20ALPHA2
+export NMAP_VERSION = 4.20ALPHA3
NMAP_NAME= Nmap
NMAP_URL= http://www.insecure.org/nmap/
NMAP_PLATFORM=@host@
diff --git a/mswin32/nmap.vcproj b/mswin32/nmap.vcproj
index d148e149c..41663d750 100644
--- a/mswin32/nmap.vcproj
+++ b/mswin32/nmap.vcproj
@@ -263,6 +263,10 @@
RelativePath="..\osscan.cc"
>
+
+
@@ -404,6 +408,10 @@
RelativePath="..\osscan.h"
>
+
+
@@ -590,7 +598,7 @@
@@ -600,7 +608,31 @@
+
+
+
+
+
+
+
+
@@ -614,7 +646,7 @@
@@ -624,7 +656,7 @@
@@ -638,7 +670,7 @@
@@ -648,7 +680,7 @@
@@ -662,7 +694,7 @@
@@ -672,7 +704,7 @@
@@ -686,7 +718,7 @@
@@ -696,7 +728,7 @@
@@ -710,7 +742,7 @@
@@ -720,7 +752,7 @@
@@ -733,9 +765,9 @@
>
diff --git a/nmap_winconfig.h b/nmap_winconfig.h
index 7e800be44..51e4403f1 100644
--- a/nmap_winconfig.h
+++ b/nmap_winconfig.h
@@ -106,7 +106,7 @@
/* Without this, Windows will give us all sorts of crap about using functions
like strcpy() even if they are done safely */
#define _CRT_SECURE_NO_DEPRECATE 1
-#define NMAP_VERSION "4.20ALPHA2"
+#define NMAP_VERSION "4.20ALPHA3"
#define NMAP_NAME "Nmap"
#define NMAP_URL "http://www.insecure.org/nmap"
#define NMAP_PLATFORM "i686-pc-windows-windows"
diff --git a/osscan2.cc b/osscan2.cc
new file mode 100644
index 000000000..57a16b486
--- /dev/null
+++ b/osscan2.cc
@@ -0,0 +1,3500 @@
+
+#include "osscan.h"
+#include "osscan2.h"
+#include "timing.h"
+#include "NmapOps.h"
+#include
+#include
+
+#define NUM_FPTESTS 13
+#define MAX_SCAN_ROUND 3
+
+using namespace std;
+extern NmapOps o;
+
+/* 7 options:
+ * 0~5: six options for TSeq/TOps/TWin/T1 probes.
+ * 6: T2~T7 probes.
+ *
+ * option 0: WScale (10), Nop, MSS (1460), Timestamp, Nop, Nop, SackP, Nop, Nop
+ * option 1: MSS (1400), WScale (0), Nop, SackP, Nop, Nop, T, Nop, Nop
+ * option 2: T, Nop, Nop, SackP, Nop, Nop, WScale (5), Nop, MSS (640)
+ * option 3: SackP, Nop, Nop, T, Nop, Nop, WScale (10), Nop
+ * option 4: MSS (536), SackP, Nop, Nop, T, Nop, Nop, WScale (10), Nop
+ * option 5: MSS (265), T, Nop, Nop, SackP, Nop, End
+ * option 6: WScale (10), Nop, MSS (265), T, SackP
+ */
+static struct {
+ u8* val;
+ int len;
+} prbOpts[NUM_SEQ_SAMPLES + 1] = {
+ {(u8*) "\003\003\012\001\002\004\005\264\010\012\377\377\377\377\000\000\000\000\001\001\004\002\001\001", 24},
+ {(u8*) "\002\004\005\170\003\003\000\001\004\002\001\001\010\012\377\377\377\377\000\000\000\000\001\001", 24},
+ {(u8*) "\010\012\377\377\377\377\000\000\000\000\001\001\004\002\001\001\003\003\005\001\002\004\002\200", 24},
+ {(u8*) "\004\002\001\001\010\012\377\377\377\377\000\000\000\000\001\001\003\003\012\001", 20},
+ {(u8*) "\002\004\002\030\004\002\001\001\010\012\377\377\377\377\000\000\000\000\001\001\003\003\012\001", 24},
+ {(u8*) "\002\004\001\011\010\012\377\377\377\377\000\000\000\000\001\001\004\002\001\000", 20},
+ {(u8*) "\003\003\012\001\002\004\001\011\010\012\377\377\377\377\000\000\000\000\004\002", 20}
+};
+
+/* A global now. Updated after potentially meaningful delays. This can
+ * be used to save a call to gettimeofday()
+ */
+static struct timeval now;
+
+class OFProbe;
+class HostOsScanStats;
+class HostOsScan;
+class HostOsScanInfo;
+class OsScanInfo;
+
+/* Performance tuning variable. */
+struct os_scan_performance_vars {
+ int low_cwnd; /* The lowest cwnd (congestion window) allowed */
+ int host_initial_cwnd; /* Initial congestion window for ind. hosts */
+ int group_initial_cwnd; /* Initial congestion window for all hosts as a group */
+ int max_cwnd; /* I should never have more than this many probes
+ outstanding */
+ int quick_incr; /* How many probes are incremented for each response
+ in quick start mode */
+ int cc_incr; /* How many probes are incremented per (roughly) rtt in
+ congestion control mode */
+ int initial_ccthresh;
+ /* When a successful ping response comes back, it counts as this many
+ "normal" responses, because the fact that pings are neccessary means
+ we aren't getting much input. */
+ int ping_magnifier;
+ /* Try to send a scanping if no response has been received from a target host
+ in this many usecs */
+ int pingtime;
+ double group_drop_cwnd_divisor; /* all-host group cwnd divided by this
+ value if any packet drop occurs */
+ double group_drop_ccthresh_divisor; /* used to drop the group ccthresh when
+ any drop occurs */
+ double host_drop_ccthresh_divisor; /* used to drop the host ccthresh when
+ any drop occurs */
+ int tryno_cap; /* The maximum trynumber (starts at zero) allowed */
+} perf;
+
+/* Some of the algorithms used here are TCP congestion control
+ techniques from RFC2581. */
+struct osscan_timing_vals {
+ double cwnd; /* Congestion window - in probes */
+
+ /* The threshold after which mode is changed from QUICK_START to
+ CONGESTION_CONTROL */
+ int ccthresh;
+
+ /* Number of updates to this utv (generally packet receipts ) */
+ int num_updates;
+
+ /* Last time values were adjusted for a drop (you usually only want
+ to adjust again based on probes sent after that adjustment so a
+ sudden batch of drops doesn't destroy timing. Init to now */
+ struct timeval last_drop;
+};
+
+typedef enum OFProbeType {
+ OFP_UNSET,
+ OFP_TSEQ,
+ OFP_TOPS,
+ OFP_TECN,
+ OFP_T1_7,
+ OFP_TICMP,
+ OFP_TUDP
+} OFProbeType;
+
+class OFProbe
+{
+public:
+ OFProbe();
+
+ /* The literal string for the current probe type. */
+ const char *typestr();
+
+ /* Type of the probe: for what os fingerprinting test? */
+ OFProbeType type;
+
+ /* Subid of this probe to separate different tcp/udp/icmp. */
+ int subid;
+
+ int tryno; /* Try (retransmission) number of this probe */
+
+ /* A packet may be timedout for a while before being retransmitted
+ due to packet sending rate limitations */
+ bool retransmitted;
+
+ struct timeval sent;
+
+ /* Time the previous probe was sent, if this is a retransmit (tryno > 0) */
+ struct timeval prevSent;
+};
+
+/*
+ * HostOsScanStats stores the status for a host being scanned
+ * in a scan round.
+ */
+class HostOsScanStats
+{
+ friend class HostOsScan;
+public:
+ HostOsScanStats(Target *t);
+ ~HostOsScanStats();
+ void initScanStats();
+
+ void addNewProbe(OFProbeType type, int subid);
+ void removeActiveProbe(list::iterator probeI);
+
+/* Get an active probe from active probe list identified by probe type
+ and subid. returns probesActive.end() if there isn't one. */
+ list::iterator getActiveProbe(OFProbeType type, int subid);
+ void moveProbeToActiveList(list::iterator probeI);
+ void moveProbeToUnSendList(list::iterator probeI);
+ unsigned int numProbesToSend() {return probesToSend.size();}
+ unsigned int numProbesActive() {return probesActive.size();}
+
+ FingerPrint *getFP() {fpPassed = true; return FP;}
+
+ Target *target; /* the Target */
+ struct seq_info si;
+ struct ipid_info ipid;
+ int distance; /* hop count between us and the target */
+
+private:
+ /* Ports of the targets used in os fingerprinting. */
+ unsigned long openTCPPort, closedTCPPort, closedUDPPort;
+
+ /* Probe list used in tests. At first, probes are linked in
+ * probesToSend; when a probe is sent, it will be removed from
+ * probesToSend and appended to probesActive. If any probes in
+ * probesActive are timedout, they will be moved to probesToSend and
+ * sent again till expired.
+ */
+ list probesToSend;
+ list probesActive;
+
+ /* A record of total number of probes that have been sent to this
+ * host, including restranmited ones. */
+ unsigned int num_probes_sent;
+ /* Delay between two probes. */
+ unsigned int sendDelayMs;
+ /* When the last probe is sent. */
+ struct timeval lastProbeSent;
+
+ struct osscan_timing_vals timing;
+
+ /*
+ * Fingerprint of this target. When a scan is completed, it'll
+ * finally be passed to hs->target->FPR->FPs[x].
+ */
+ FingerPrint *FP;
+ FingerPrint *FPtests[NUM_FPTESTS];
+#define FP_TSeq FPtests[0]
+#define FP_TOps FPtests[1]
+#define FP_TWin FPtests[2]
+#define FP_TEcn FPtests[3]
+#define FP_T1_7_OFF 4
+#define FP_T1 FPtests[4]
+#define FP_T2 FPtests[5]
+#define FP_T3 FPtests[6]
+#define FP_T4 FPtests[7]
+#define FP_T5 FPtests[8]
+#define FP_T6 FPtests[9]
+#define FP_T7 FPtests[10]
+#define FP_TUdp FPtests[11]
+#define FP_TIcmp FPtests[12]
+ struct AVal *TOps_AVs[6]; /* 6 AVs of TOps */
+ struct AVal *TWin_AVs[6]; /* 6 AVs of TWin */
+
+ /* Whether the above FPs is passed. If not and the hss stats is to be
+ deleted, delete the FPs. This happens when the host is timedout
+ during the scan. */
+ bool fpPassed;
+
+ /* The following are variables to store temporary results
+ * during the os fingerprinting process of this host.
+ */
+ u16 lastipid;
+ struct timeval seq_send_times[NUM_SEQ_SAMPLES];
+
+ int TWinReplyNum; /* how many TWin replies are received. */
+ int TOpsReplyNum; /* how many TOps replies are received. Actually it is the same with TOpsReplyNum. */
+
+ struct ip *icmpEchoReply; /* To store one of the two icmp replies */
+ int storedIcmpReply; /* Which one of the two icmp replies is stored? */
+
+ struct udpprobeinfo upi; /* info of the udp probe we sent */
+};
+
+/* These are statistics for the whole group of Targets */
+class ScanStats {
+public:
+ ScanStats();
+
+ /* Returns true if the system says that sending is OK. */
+ bool sendOK();
+
+ struct osscan_timing_vals timing;
+ struct timeout_info to; /* rtt/timeout info */
+
+ /* Total number of active probes */
+ int num_probes_active;
+ /* Number of probes sent in total. */
+ int num_probes_sent;
+ int num_probes_sent_at_last_wait;
+};
+
+/*
+ * HostOsScan does the scan job, setting and using the status of a host in
+ * the host's HostOsScanStats.
+ */
+class HostOsScan
+{
+public:
+ HostOsScan(Target *t); /* OsScan need a target to set eth stuffs */
+ ~HostOsScan();
+
+ pcap_t *pd;
+ ScanStats *stats;
+
+ /* (Re)Initial the parameters that will be used during the scan.*/
+ void reInitScanSystem();
+
+ void buildSeqProbeList(HostOsScanStats *hss);
+ void updateActiveSeqProbes(HostOsScanStats *hss);
+
+ void buildTUIProbeList(HostOsScanStats *hss);
+ void updateActiveTUIProbes(HostOsScanStats *hss);
+
+ /* send the next probe in the probe list of the hss */
+ void sendNextProbe(HostOsScanStats *hss);
+
+ /* Process one response.
+ * If the response is useful, return true. */
+ bool processResp(HostOsScanStats *hss, struct ip *ip, unsigned int len, struct timeval *rcvdtime);
+
+ /* Make up the fingerprint. */
+ void makeFP(HostOsScanStats *hss);
+
+ /* 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 hostSendOK(HostOsScanStats *hss, struct timeval *when);
+
+ /* Check whether it is ok to send the next seq probe to the host. If
+ * not, fill _when_ with the time when it will be sendOK and return
+ * false; else, fill it with now and return true.
+ */
+ bool hostSeqSendOK(HostOsScanStats *hss, struct timeval *when);
+
+
+ /* How long I am currently willing to wait for a probe response
+ before considering it timed out. Uses the host values from
+ target if they are available, otherwise from gstats. Results
+ returned in MICROseconds. */
+ unsigned long timeProbeTimeout(HostOsScanStats *hss);
+
+ /* 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 nextTimeout(HostOsScanStats *hss, struct timeval *when);
+
+ /* Adjust various timing variables based on pcket receipt. */
+ void adjust_times(HostOsScanStats *hss, OFProbe *probe, struct timeval *rcvdtime);
+
+private:
+ /* Probe send functions. */
+ void sendTSeqProbe(HostOsScanStats *hss, int probeNo);
+ void sendTOpsProbe(HostOsScanStats *hss, int probeNo);
+ void sendTEcnProbe(HostOsScanStats *hss);
+ void sendT1_7Probe(HostOsScanStats *hss, int probeNo);
+ void sendTUdpProbe(HostOsScanStats *hss, int probeNo);
+ void sendTIcmpProbe(HostOsScanStats *hss, int probeNo);
+ /* Response process functions. */
+ bool processTSeqResp(HostOsScanStats *hss, struct ip *ip, int replyNo);
+ bool processTOpsResp(HostOsScanStats *hss, struct tcphdr *tcp, int replyNo);
+ bool processTWinResp(HostOsScanStats *hss, struct tcphdr *tcp, int replyNo);
+ bool processTEcnResp(HostOsScanStats *hss, struct ip *ip);
+ bool processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyNo);
+ bool processTUdpResp(HostOsScanStats *hss, struct ip *ip);
+ bool processTIcmpResp(HostOsScanStats *hss, struct ip *ip, int replyNo);
+
+ void makeTSeqFP(HostOsScanStats *hss);
+ void makeTOpsFP(HostOsScanStats *hss);
+ void makeTWinFP(HostOsScanStats *hss);
+
+ bool get_tcpopt_string(struct tcphdr *tcp, int mss, char *result, int maxlen);
+
+ int rawsd; /* raw socket descriptor */
+ struct eth_nfo eth;
+ struct eth_nfo *ethptr; /* for passing to send_ functions */
+
+ unsigned int tcpSeqBase, tcpAck; /* Seq&Ack value used in TCP probes */
+ int tcpMss; /* tcp Mss value used in TCP probes */
+ int udpttl; /* ttl value used in udp probe. */
+ unsigned short icmpEchoId, icmpEchoSeq; /* Icmp Echo Id&Seq value used in ICMP probes*/
+
+ /* Source port number in TCP probes. Different probe will use
+ * arbitrary offset value of it. */
+ int tcpPortBase;
+ int udpPortBase;
+};
+
+/*
+ * The overall os scan information of a host:
+ * - Fingerprints gotten from every scan round;
+ * - Maching results of these fingerprints.
+ * - Is it timeout/completed?
+ * - ...
+ */
+class HostOsScanInfo
+{
+public:
+ HostOsScanInfo(Target *t, OsScanInfo *OSI);
+ ~HostOsScanInfo();
+
+ Target *target; /* the Target */
+ OsScanInfo *OSI; /* The OSI which contains this HostOsScanInfo */
+ FingerPrint *FPs[MAX_SCAN_ROUND]; /* Fingerprints of the host */
+ FingerPrintResults FP_matches[MAX_SCAN_ROUND]; /* Fingerprint-matching results */
+ struct seq_info si[MAX_SCAN_ROUND];
+ bool timedOut;
+ bool isCompleted;
+ HostOsScanStats *hss; /* Scan status of the host in one scan round */
+};
+
+/*
+ * Maintain a link of incomplete HostOsScanInfo.
+ */
+class OsScanInfo
+{
+public:
+ OsScanInfo(vector &Targets);
+ ~OsScanInfo();
+
+ list incompleteHosts;
+ unsigned int starttimems;
+
+ unsigned int numIncompleteHosts() {return incompleteHosts.size();}
+ HostOsScanInfo *findIncompleteHost(struct sockaddr_storage *ss);
+ /* 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 *nextIncompleteHost();
+ int removeCompletedHosts();
+private:
+ unsigned int numInitialTargets;
+ list::iterator nextI;
+};
+
+
+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() {
+ 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";
+ }
+}
+
+HostOsScanStats::HostOsScanStats(Target * t) {
+ int i;
+
+ target = t;
+ FP = NULL;
+
+ openTCPPort = (unsigned int)-1;
+ closedTCPPort = (unsigned int)-1;
+ closedUDPPort = (unsigned int)-1;
+
+ num_probes_sent = 0;
+ sendDelayMs = o.scan_delay;
+ lastProbeSent = now;
+
+ /* timing */
+ timing.cwnd = perf.host_initial_cwnd;
+ timing.ccthresh = perf.initial_ccthresh; /* Will be reduced if any packets are dropped anyway */
+ timing.num_updates = 0;
+ gettimeofday(&timing.last_drop, NULL);
+
+ for (i=0; iresults) {
+ free(FPtests[i]->results);
+ }
+ free(FPtests[i]);
+ }
+ }
+ for(i=0; i<6; i++) {
+ if(TOps_AVs[i]) free(TOps_AVs[i]);
+ if(TWin_AVs[i]) free(TWin_AVs[i]);
+ }
+ }
+
+ while(!probesToSend.empty()) {
+ delete probesToSend.front();
+ probesToSend.pop_front();
+ }
+
+ while(!probesActive.empty()) {
+ delete probesActive.front();
+ probesActive.pop_front();
+ }
+
+ if (icmpEchoReply) free(icmpEchoReply);
+}
+
+void HostOsScanStats::initScanStats() {
+ Port *tport = NULL;
+ int i;
+
+ /* Lets find an open port to use */
+ openTCPPort = (unsigned long) -1;
+ target->FPR->osscan_opentcpport = -1;
+ target->FPR->osscan_closedtcpport = -1;
+ target->FPR->osscan_closedudpport = -1;
+
+ if ((tport = target->ports.nextPort(NULL, IPPROTO_TCP, PORT_OPEN))) {
+ openTCPPort = tport->portno;
+ target->FPR->osscan_opentcpport = tport->portno;
+ }
+
+ /* Now we should find a closed port */
+ if ((tport = target->ports.nextPort(NULL, IPPROTO_TCP, PORT_CLOSED))) {
+ closedTCPPort = tport->portno;
+ target->FPR->osscan_closedtcpport = tport->portno;
+ } else if ((tport = target->ports.nextPort(NULL, IPPROTO_TCP, PORT_UNFILTERED))) {
+ /* Well, we will settle for unfiltered */
+ closedTCPPort = tport->portno;
+ } else {
+ closedTCPPort = (get_random_uint() % 14781) + 30000;
+ }
+
+ /* Now we should find a closed udp port */
+ if ((tport = target->ports.nextPort(NULL, IPPROTO_UDP, PORT_CLOSED))) {
+ closedUDPPort = tport->portno;
+ target->FPR->osscan_closedudpport = tport->portno;
+ } else if ((tport = target->ports.nextPort(NULL, IPPROTO_UDP, PORT_UNFILTERED))) {
+ /* Well, we will settle for unfiltered */
+ closedUDPPort = tport->portno;
+ } else {
+ closedUDPPort = (get_random_uint() % 14781) + 30000;
+ }
+
+ if (o.verbose && openTCPPort != (unsigned long) -1)
+ log_write(LOG_STDOUT, "OSScan against host %s: assuming TCP port %lu is open, %lu is closed, UDP port %lu is closed and none is firewalled\n",
+ target->targetipstr(), openTCPPort, closedTCPPort, closedUDPPort);
+
+ FP = NULL;
+ for (i=0; itype = type;
+ probe->subid = subid;
+ probesToSend.push_back(probe);
+}
+
+/* Remove a probe from the probesActive. */
+void HostOsScanStats::removeActiveProbe(list::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 */
+list::iterator HostOsScanStats::getActiveProbe(OFProbeType type, int subid) {
+ list::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)
+ printf("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(list::iterator probeI) {
+ probesActive.push_back(*probeI);
+ probesToSend.erase(probeI);
+}
+
+/* Move a probe from probesActive to probesToSend. */
+void HostOsScanStats::moveProbeToUnSendList(list::iterator probeI) {
+ probesToSend.push_back(*probeI);
+ probesActive.erase(probeI);
+}
+
+/* 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) {
+ assert(hss);
+ struct timeval probe_to, earliest_to;
+ list::iterator probeI;
+ bool firstgood = true;
+
+ assert(when);
+ memset(&probe_to, 0, sizeof(probe_to));
+ memset(&earliest_to, 0, sizeof(earliest_to));
+
+ for(probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) {
+ TIMEVAL_ADD(probe_to, (*probeI)->sent, timeProbeTimeout(hss));
+ if (firstgood || TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) {
+ earliest_to = probe_to;
+ firstgood = false;
+ }
+ }
+
+ *when = (firstgood)? now : earliest_to;
+ return (firstgood)? false : true;
+}
+
+void HostOsScan::adjust_times(HostOsScanStats *hss, OFProbe *probe, 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));
+ }
+
+ hss->timing.num_updates++;
+ stats->timing.num_updates++;
+
+ /* Adjust window */
+ if (probe->tryno > 0 || !rcvdtime) {
+ if (TIMEVAL_SUBTRACT(probe->sent, hss->timing.last_drop) > 0) {
+ hss->timing.cwnd = perf.low_cwnd;
+ hss->timing.ccthresh = (int) MAX(hss->numProbesActive() / perf.host_drop_ccthresh_divisor, 2);
+ hss->timing.last_drop = now;
+ }
+ if (TIMEVAL_SUBTRACT(probe->sent, stats->timing.last_drop) > 0) {
+ stats->timing.cwnd = MAX(perf.low_cwnd, stats->timing.cwnd / perf.group_drop_cwnd_divisor);
+ stats->timing.ccthresh = (int) MAX(stats->num_probes_active / perf.group_drop_ccthresh_divisor, 2);
+ stats->timing.last_drop = now;
+ }
+ } else {
+ /* Good news -- got a response to first try. Increase window as
+ appropriate. */
+ if (hss->timing.cwnd <= hss->timing.ccthresh) {
+ /* In quick start mode */
+ hss->timing.cwnd += perf.quick_incr;
+ } else {
+ /* Congestion control mode */
+ hss->timing.cwnd += perf.cc_incr / hss->timing.cwnd;
+ }
+ if (hss->timing.cwnd > perf.max_cwnd)
+ hss->timing.cwnd = perf.max_cwnd;
+
+ if (stats->timing.cwnd <= stats->timing.ccthresh) {
+ /* In quick start mode */
+ stats->timing.cwnd += perf.quick_incr;
+ } else {
+ /* Congestion control mode */
+ stats->timing.cwnd += perf.cc_incr / stats->timing.cwnd;
+ }
+ if (stats->timing.cwnd > perf.max_cwnd)
+ stats->timing.cwnd = perf.max_cwnd;
+ }
+}
+
+ScanStats::ScanStats() {
+ /* init timing val */
+ timing.cwnd = perf.group_initial_cwnd;
+ timing.ccthresh = perf.initial_ccthresh; /* Will be reduced if any packets are dropped anyway */
+ 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() {
+ 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;
+}
+
+HostOsScan::HostOsScan(Target *t) {
+ pd = NULL;
+ rawsd = -1;
+
+ if ((o.sendpref & PACKET_SEND_ETH) && t->ifType() == devt_ethernet) {
+ memcpy(eth.srcmac, t->SrcMACAddress(), 6);
+ memcpy(eth.dstmac, t->NextHopMACAddress(), 6);
+ if ((eth.ethsd = eth_open_cached(t->deviceName())) == NULL)
+ fatal("%s: Failed to open ethernet device (%s)", __FUNCTION__, t->deviceName());
+ rawsd = -1;
+ ethptr = ð
+ } else {
+ /* Init our raw socket */
+ if ((rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 )
+ pfatal("socket trobles in get_fingerprint");
+ unblock_socket(rawsd);
+ broadcast_socket(rawsd);
+#ifndef WIN32
+ sethdrinclude(rawsd);
+#endif
+ ethptr = NULL;
+ eth.ethsd = NULL;
+ }
+
+ tcpPortBase = o.magic_port_set? o.magic_port : o.magic_port + get_random_u8();
+ udpPortBase = o.magic_port_set? o.magic_port : o.magic_port + get_random_u8();
+ reInitScanSystem();
+
+ stats = new ScanStats();
+}
+
+HostOsScan::~HostOsScan() {
+ if (rawsd >= 0) { close(rawsd); rawsd = -1; }
+ if (pd) { pcap_close(pd); pd = NULL; }
+ /*
+ * No need to close ethptr->ethsd due to caching
+ * if (eth.ethsd) { eth_close(eth.ethsd); eth.ethsd = NULL; }
+ */
+ delete stats;
+}
+
+void HostOsScan::reInitScanSystem() {
+ tcpSeqBase = get_random_u32();
+ tcpAck = get_random_u32();
+ tcpMss = 265;
+ icmpEchoId = get_random_u16();
+ icmpEchoSeq = 295;
+ udpttl = (time(NULL) % 14) + 51;
+}
+
+/* Initiate seq probe list */
+void HostOsScan::buildSeqProbeList(HostOsScanStats *hss) {
+ assert(hss);
+ int i;
+ if(hss->openTCPPort == (unsigned long)-1) return;
+ if(hss->FP_TSeq) return;
+
+ for(i=0; iaddNewProbe(OFP_TSEQ, i);
+}
+
+/* Update the seq probes in the active probe list:
+ * o Remove the timedout seq probes.
+ */
+void HostOsScan::updateActiveSeqProbes(HostOsScanStats *hss) {
+ assert(hss);
+ list::iterator probeI, nxt;
+ OFProbe *probe = NULL;
+
+ for(probeI = hss->probesActive.begin(); probeI != hss->probesActive.end();
+ probeI = nxt) {
+ nxt = probeI;
+ nxt++;
+ probe = *probeI;
+
+ /* Is the probe timedout? */
+ if (TIMEVAL_SUBTRACT(now, probe->sent) > (long) timeProbeTimeout(hss)) {
+ hss->removeActiveProbe(probeI);
+ stats->num_probes_active--;
+ }
+ }
+}
+
+/* initiate 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 != (unsigned long)-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);
+ list::iterator probeI, nxt;
+ OFProbe *probe = NULL;
+
+ for(probeI = hss->probesActive.begin(); probeI != hss->probesActive.end();
+ probeI = nxt) {
+ nxt = probeI;
+ nxt++;
+ probe = *probeI;
+
+ if(TIMEVAL_SUBTRACT(now, probe->sent) > (long) timeProbeTimeout(hss)) {
+ if(probe->tryno >= 3) {
+ /* The probe is expired. */
+ hss->removeActiveProbe(probeI);
+ stats->num_probes_active--;
+ }
+ else {
+ /* It is timedout, move it to the sendlist */
+ hss->moveProbeToUnSendList(probeI);
+ 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) {
+ assert(hss);
+ list::iterator probeI;
+ int packTime;
+ struct timeval probe_to, earliest_to, sendTime;
+ long tdiff;
+
+ if (hss->target->timedOut(&now)) {
+ if (when) *when = now;
+ return false;
+ }
+
+ if (hss->sendDelayMs > 0) {
+ packTime = TIMEVAL_MSEC_SUBTRACT(now, hss->lastProbeSent);
+ if (packTime < (int) hss->sendDelayMs) {
+ if (when) { TIMEVAL_MSEC_ADD(*when, hss->lastProbeSent, hss->sendDelayMs); }
+ return false;
+ }
+ }
+
+ if (hss->timing.cwnd >= hss->numProbesActive() + .5) {
+ if (when) *when = now;
+ return true;
+ }
+
+ if (!when)
+ return false;
+
+ TIMEVAL_MSEC_ADD(earliest_to, now, 10000);
+
+ /* Any timeouts coming up? */
+ for(probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) {
+ TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, timeProbeTimeout(hss) / 1000);
+ if (TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) {
+ earliest_to = probe_to;
+ }
+ }
+
+ // Will any scan delay affect this?
+ if (hss->sendDelayMs > 0) {
+ TIMEVAL_MSEC_ADD(sendTime, hss->lastProbeSent, hss->sendDelayMs);
+ if (TIMEVAL_MSEC_SUBTRACT(sendTime, now) < 0)
+ sendTime = now;
+ tdiff = TIMEVAL_MSEC_SUBTRACT(earliest_to, sendTime);
+
+ /* Timeouts previous to the sendTime requirement are pointless,
+ and those later than sendTime are not needed if we can send a
+ new packet at sendTime */
+ if (tdiff < 0) {
+ earliest_to = sendTime;
+ } else {
+ if (tdiff > 0 && hss->timing.cwnd > hss->numProbesActive() + .5) {
+ earliest_to = sendTime;
+ }
+ }
+ }
+
+ *when = earliest_to;
+ return false;
+}
+
+/* Check whether it is ok to send the next seq probe to the host. If
+ * not, fill _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) {
+ assert(hss);
+ list::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);
+
+ /* The meaning of 110000: Need to spend at least .5 seconds in
+ * sending all packets to reliably detect 2HZ timestamp sequencing.
+ *
+ * If the user insist a sendDelayMs larger than 110ms, use it. But
+ * the seq result may be inaccurate.
+ */
+ maxWait = MAX(110000, hss->sendDelayMs * 1000);
+ if (packTime < maxWait) {
+ if (when) { TIMEVAL_ADD(*when, hss->lastProbeSent, maxWait); }
+ return false;
+ }
+
+ if (hss->timing.cwnd >= hss->numProbesActive() + .5) {
+ if (when) *when = now;
+ return true;
+ }
+
+ if (!when)
+ return false;
+
+ TIMEVAL_MSEC_ADD(earliest_to, now, 10000);
+
+ /* Any timeouts coming up? */
+ for(probeI = hss->probesActive.begin(); probeI != hss->probesActive.end(); probeI++) {
+ TIMEVAL_MSEC_ADD(probe_to, (*probeI)->sent, timeProbeTimeout(hss) / 1000);
+ if (TIMEVAL_SUBTRACT(probe_to, earliest_to) < 0) {
+ earliest_to = probe_to;
+ }
+ }
+
+ TIMEVAL_ADD(sendTime, hss->lastProbeSent, maxWait);
+ if (TIMEVAL_SUBTRACT(sendTime, now) < 0)
+ sendTime = now;
+ tdiff = TIMEVAL_SUBTRACT(earliest_to, sendTime);
+
+ /* Timeouts previous to the sendTime requirement are pointless,
+ and those later than sendTime are not needed if we can send a
+ new packet at sendTime */
+ if (tdiff < 0) {
+ earliest_to = sendTime;
+ } else {
+ if (tdiff > 0 && hss->timing.cwnd > hss->numProbesActive() + .5) {
+ earliest_to = sendTime;
+ }
+ }
+
+ *when = earliest_to;
+ return false;
+}
+
+unsigned long HostOsScan::timeProbeTimeout(HostOsScanStats *hss) {
+ 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);
+ list::iterator probeI;
+ OFProbe *probe = NULL;
+
+ if(hss->probesToSend.empty())
+ return;
+
+ 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 retransmiting */
+ probe->retransmitted = true;
+ probe->prevSent = probe->sent;
+ }
+ probe->sent = now;
+
+ hss->lastProbeSent = now;
+ hss->num_probes_sent++;
+ stats->num_probes_sent++;
+
+ hss->moveProbeToActiveList(probeI);
+
+ if (o.debugging > 1) {
+ printf("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 == (unsigned long)-1) return;
+
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ tcpPortBase + probeNo, hss->openTCPPort,
+ tcpSeqBase + probeNo, tcpAck, 0,
+ TH_SYN, 0, 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<6);
+
+ if(hss->openTCPPort == (unsigned long)-1) return;
+
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ tcpPortBase + NUM_SEQ_SAMPLES + probeNo, hss->openTCPPort, tcpSeqBase,
+ tcpAck, 0, TH_SYN, 0, 0, prbOpts[probeNo].val, prbOpts[probeNo].len, NULL, 0);
+}
+
+void HostOsScan::sendTEcnProbe(HostOsScanStats *hss) {
+ assert(hss);
+
+ if(hss->openTCPPort == (unsigned long)-1) return;
+
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ tcpPortBase + NUM_SEQ_SAMPLES + 6, hss->openTCPPort, tcpSeqBase, 0, 8,
+ TH_CWR|TH_ECE|TH_SYN, 0, 63479, NULL , 0, 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 */
+ if(hss->openTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ port_base, hss->openTCPPort, tcpSeqBase, tcpAck, 0,
+ TH_SYN, 0, 0, prbOpts[0].val, prbOpts[0].len, NULL, 0);
+ break;
+ case 1: /* T2 */
+ if(hss->openTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, true,
+ port_base + 1, hss->openTCPPort, tcpSeqBase, tcpAck, 0,
+ 0, 0, 0, prbOpts[6].val, prbOpts[6].len, NULL, 0);
+ break;
+ case 2: /* T3 */
+ if(hss->openTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ port_base + 2, hss->openTCPPort, tcpSeqBase, tcpAck, 0,
+ TH_SYN|TH_FIN|TH_URG|TH_PUSH, 0, 0, prbOpts[6].val, prbOpts[6].len, NULL, 0);
+ break;
+ case 3: /* T4 */
+ if(hss->openTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, true,
+ port_base + 3, hss->openTCPPort, tcpSeqBase, tcpAck, 0,
+ TH_ACK, 0, 0, prbOpts[6].val, prbOpts[6].len, NULL, 0);
+ break;
+ case 4: /* T5 */
+ if(hss->closedTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ port_base + 4, hss->closedTCPPort, tcpSeqBase, tcpAck, 0,
+ TH_SYN, 0, 0, prbOpts[6].val, prbOpts[6].len, NULL, 0);
+ break;
+ case 5: /* T6 */
+ if(hss->closedTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, true,
+ port_base + 5, hss->closedTCPPort, tcpSeqBase, tcpAck, 0,
+ TH_ACK, 0, 0, prbOpts[6].val, prbOpts[6].len, NULL, 0);
+ break;
+ case 6: /* T7 */
+ if(hss->closedTCPPort == (unsigned long)-1) return;
+ send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false,
+ port_base + 6, hss->closedTCPPort, tcpSeqBase, tcpAck, 0,
+ TH_FIN|TH_PUSH|TH_URG, 0, 0, prbOpts[6].val, prbOpts[6].len, NULL, 0);
+ break;
+ }
+}
+
+void HostOsScan::sendTIcmpProbe(HostOsScanStats *hss, int probeNo) {
+ assert(hss);
+ assert(probeNo>=0&&probeNo<2);
+ if(probeNo==0) {
+ send_icmp_echo_probe(rawsd, ethptr, hss->target->v4hostip(), IP_TOS_DEFAULT,
+ true, 9, icmpEchoId, icmpEchoSeq, 120);
+ }
+ else {
+ send_icmp_echo_probe(rawsd, ethptr, hss->target->v4hostip(), IP_TOS_RELIABILITY,
+ false, 0, icmpEchoId+1, icmpEchoSeq+1, 150);
+ }
+}
+
+void HostOsScan::sendTUdpProbe(HostOsScanStats *hss, int probeNo) {
+ assert(hss);
+
+ if(hss->closedUDPPort == (unsigned long)-1) return;
+ send_closedudp_probe_2(hss->upi, rawsd, ethptr, hss->target->v4hostip(),
+ this->udpttl, udpPortBase + probeNo, hss->closedUDPPort);
+}
+
+bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int len, struct timeval *rcvdtime) {
+ struct ip *ip2;
+ struct tcphdr *tcp;
+ struct icmp *icmp;
+ int testno;
+ bool isPktUseful = false;
+ list::iterator probeI;
+ OFProbe *probe;
+ int icmp_ipid_no = 0;
+
+ if (len < 20 || len < (4 * ip->ip_hl) + 4U)
+ return false;
+
+ len -= 4 * ip->ip_hl;
+
+ if (ip->ip_p == IPPROTO_TCP) {
+ if(len < 20) return false;
+ tcp = ((struct tcphdr *) (((char *) ip) + 4 * ip->ip_hl));
+ if(len < (unsigned int)(4 * tcp->th_off)) return false;
+ testno = ntohs(tcp->th_dport) - tcpPortBase;
+
+ if (testno >= 0 && testno < NUM_SEQ_SAMPLES) {
+ /* TSeq */
+ isPktUseful = processTSeqResp(hss, ip, testno);
+
+ if(isPktUseful) {
+ hss->ipid.tcp_ipids[testno] = ntohs(ip->ip_id);
+ probeI = hss->getActiveProbe(OFP_TSEQ, testno);
+ /* printf("tcp ipid = %d\n", ntohs(ip->ip_id)); */
+ }
+
+ /* Use the seq response to do other tests. We don't care if it
+ * is useful for these tests.
+ */
+ if (testno == 0) {
+ /* the first reply is used to do T1 */
+ processT1_7Resp(hss, ip, 0);
+ }
+ if (testno<6) {
+ /* the 1th~6th replies are used to do TOps and TWin */
+ processTOpsResp(hss, tcp, testno);
+ processTWinResp(hss, tcp, testno);
+ }
+
+ } else if (testno>=NUM_SEQ_SAMPLES && testnogetActiveProbe(OFP_TOPS, testno - NUM_SEQ_SAMPLES);
+ }
+
+ } else if (testno==NUM_SEQ_SAMPLES+6) {
+
+ /* TEcn */
+ isPktUseful = processTEcnResp(hss, ip);
+ if(isPktUseful) {
+ probeI = hss->getActiveProbe(OFP_TECN, 0);
+ }
+
+ } else if (testno >= NUM_SEQ_SAMPLES+7 && testnogetActiveProbe(OFP_T1_7, testno-NUM_SEQ_SAMPLES-7);
+ }
+ }
+ }
+ else if (ip->ip_p == IPPROTO_ICMP) {
+ if(len < 8) return false;
+ icmp = ((struct icmp *)(((char *) ip) + 4 * ip->ip_hl));
+
+ /* Is it an icmp echo reply? */
+ if (icmp->icmp_type == ICMP_ECHOREPLY) {
+ testno = icmp->icmp_id - icmpEchoId;
+ if (testno==0 || testno==1) {
+ icmp_ipid_no = testno;
+ isPktUseful = processTIcmpResp(hss, ip, testno);
+ if(isPktUseful) {
+ probeI = hss->getActiveProbe(OFP_TICMP, testno);
+ }
+ }
+ }
+
+ /* Is it a destination port unreachable? */
+ if (icmp->icmp_type == 3 && icmp->icmp_code == 3) {
+ len -= 8; /* icmp destination unreachable header len. */
+ if(len < 28) return false; /* must larger than an ip and an udp header length */
+ ip2 = (struct ip*)((char *)icmp + 8);
+ len -= 4 * ip2->ip_hl;
+ if(len < 8) return false;
+
+ isPktUseful = processTUdpResp(hss, ip);
+ if(isPktUseful) {
+ probeI = hss->getActiveProbe(OFP_TUDP, 0);
+ }
+ icmp_ipid_no = 3;
+ }
+
+ if(isPktUseful && probeI != hss->probesActive.end() && !(*probeI)->retransmitted) { /* Retransmitted ipid is useless. */
+ hss->ipid.icmp_ipids[icmp_ipid_no] = ntohs(ip->ip_id);
+ /* printf("icmp ipid = %d\n", ntohs(ip->ip_id)); */
+ }
+ }
+
+ if (isPktUseful && probeI != hss->probesActive.end()) {
+ probe = *probeI;
+
+ if(rcvdtime)
+ adjust_times(hss, probe, rcvdtime);
+
+ if(o.debugging > 1)
+ printf("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);
+ this->stats->num_probes_active--;
+
+ return true;
+ }
+
+ return false;
+}
+
+void HostOsScan::makeFP(HostOsScanStats *hss) {
+ assert(hss);
+
+ int i;
+ int last;
+ FingerPrint *FP;
+ struct AVal *pAV;
+
+ 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] && ((hss->openTCPPort != (unsigned long) -1) || i > 7)) {
+ /* 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] = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ pAV = (struct AVal *) safe_zalloc(sizeof(struct AVal));
+ pAV->attribute = "R";
+ strcpy(pAV->value, "N");
+ pAV->next = NULL;
+ hss->FPtests[i]->results = pAV;
+ hss->FPtests[i]->name = (i == 3)? "ECN" : (i == 4)? "T1" : (i == 5)? "T2" : (i == 6)? "T3" : (i == 7)? "T4" : (i == 8)? "T5" : (i == 9)? "T6" : (i == 10)? "T7" : (i == 11)? "U1" : "IE";
+ }
+ else if(hss->FPtests[i]) {
+ /* Replace TTL with initial TTL. */
+ for(pAV = hss->FPtests[i]->results; pAV; pAV = pAV->next) {
+ if(pAV->attribute == "T") {
+ /* found TTL item */
+ if(hss->distance != -1) {
+ /* We've gotten response for the UDP probe and thus have the "true" hop count. */
+ sprintf(pAV->value, "%hX", atoi(pAV->value) + hss->distance);
+ } else {
+ /* Guess the initial TTL value */
+ pAV->attribute = "TG";
+ sprintf(pAV->value, "%hX", get_initial_ttl_guess(atoi(pAV->value)));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /* Link them up. */
+ last = -1;
+ FP = NULL;
+ for(i=0; i < NUM_FPTESTS ; i++) {
+ if (!hss->FPtests[i]) continue;
+ if (!FP) FP = hss->FPtests[i];
+ if (last > -1) {
+ hss->FPtests[last]->next = hss->FPtests[i];
+ }
+ last = i;
+ }
+ if (last) hss->FPtests[last]->next = NULL;
+
+ /* printf("%s", fp2ascii(FP)); */
+
+ hss->FP = FP;
+}
+
+void HostOsScan::makeTSeqFP(HostOsScanStats *hss) {
+ if (!hss->si.responses) return;
+
+ int i,j;
+ u32 seq_diffs[NUM_SEQ_SAMPLES];
+ u32 ts_diffs[NUM_SEQ_SAMPLES];
+ unsigned long time_usec_diffs[NUM_SEQ_SAMPLES];
+ int avnum;
+ double seq_inc_sum = 0;
+ unsigned int seq_avg_inc = 0;
+ double avg_ts_hz = 0.0; /* Avg. amount that timestamps incr. each second */
+ u32 seq_gcd = 1;
+
+ struct AVal *seq_AVs;
+
+ /* 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 */
+ }
+ j++;
+ } /* Otherwise nothing good in this slot to copy */
+ }
+
+ hss->si.responses = j; /* Just an ensurance */
+
+ hss->si.ipid_seqclass = get_ipid_sequence(&hss->ipid, islocalhost(hss->target->v4hostip()));
+
+ /* 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) {
+ 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 (o.debugging)
+ printf("The avg TCP TS HZ of %s is: %f\n", hss->target->targetipstr(), avg_ts_hz);
+
+ if (avg_ts_hz > 0 && avg_ts_hz < 3.9) { /* relatively wide range because sampling time so short and frequency so slow */
+ hss->si.ts_seqclass = TS_SEQ_2HZ;
+ hss->si.lastboot = hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 2);
+ }
+ else if (avg_ts_hz > 85 && avg_ts_hz < 115) {
+ hss->si.ts_seqclass = TS_SEQ_100HZ;
+ hss->si.lastboot = hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 100);
+ }
+ else if (avg_ts_hz > 900 && avg_ts_hz < 1100) {
+ hss->si.ts_seqclass = TS_SEQ_1000HZ;
+ hss->si.lastboot = hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 1000);
+ }
+ else if (avg_ts_hz > 0) {
+ hss->si.ts_seqclass = TS_SEQ_OTHER_NUM;
+ hss->si.lastboot = hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / (unsigned int)(0.5 + avg_ts_hz));
+ }
+
+ if (hss->si.lastboot && (hss->seq_send_times[0].tv_sec - hss->si.lastboot > 63072000)) {
+ /* Up 2 years? Perhaps, but they're probably lying. */
+ if (o.debugging) {
+ error("Ignoring claimed uptime of %lu days",
+ (hss->seq_send_times[0].tv_sec - hss->si.lastboot) / 86400);
+ }
+ hss->si.lastboot = 0;
+ }
+ }
+
+ /* Time to look at the TCP ISN predictability */
+ if (hss->si.responses >= 4 && o.scan_delay <= 1000) {
+ seq_gcd = gcd_n_uint(hss->si.responses -1, seq_diffs);
+ /* printf("The GCD is %u\n", seq_gcd);*/
+ if (seq_gcd) {
+ for(i=0; i < hss->si.responses - 1; i++)
+ seq_diffs[i] /= seq_gcd;
+ for(i=0; i < hss->si.responses - 1; i++) {
+ if (MOD_DIFF(hss->si.seqs[i+1],hss->si.seqs[i]) > 50000000) {
+ hss->si.seqclass = SEQ_TR;
+ hss->si.index = 0xFF;
+ /* printf("Target is a TR box\n");*/
+ break;
+ }
+ seq_avg_inc += seq_diffs[i];
+ }
+ }
+ if (seq_gcd == 0) {
+ hss->si.seqclass = SEQ_CONSTANT;
+ hss->si.index = 0;
+ } else if (seq_gcd % 64000 == 0) {
+ hss->si.seqclass = SEQ_64K;
+ /* printf("Target is a 64K box\n");*/
+ hss->si.index = 1;
+ } else if (seq_gcd % 800 == 0) {
+ hss->si.seqclass = SEQ_i800;
+ /* printf("Target is a i800 box\n");*/
+ hss->si.index = 3;
+ } else if (hss->si.seqclass == SEQ_UNKNOWN) {
+ seq_avg_inc = (unsigned int) ((0.5) + seq_avg_inc / (hss->si.responses - 1));
+ /* printf("seq_avg_inc=%u\n", seq_avg_inc);*/
+ for(i=0; i < hss->si.responses -1; i++) {
+ /* printf("The difference is %u\n", seq_diffs[i]);
+ printf("Adding %u^2=%e", MOD_DIFF(seq_diffs[i], seq_avg_inc), pow(MOD_DIFF(seq_diffs[i], seq_avg_inc), 2));*/
+ /* pow() seems F#@!#$!ed up on some Linux systems so I will
+ not use it for now
+ seq_inc_sum += pow(MOD_DIFF(seq_diffs[i], seq_avg_inc), 2);
+ */
+
+ seq_inc_sum += ((double)(MOD_DIFF(seq_diffs[i], seq_avg_inc)) * ((double)MOD_DIFF(seq_diffs[i], seq_avg_inc)));
+ /* seq_inc_sum += pow(MOD_DIFF(seq_diffs[i], seq_avg_inc), 2);*/
+
+ }
+ /* printf("The sequence sum is %e\n", seq_inc_sum);*/
+ seq_inc_sum /= (hss->si.responses - 1);
+
+ /* Some versions of Linux libc seem to have broken pow ... so we
+ avoid it */
+#ifdef LINUX
+ hss->si.index = (unsigned int) (0.5 + sqrt(seq_inc_sum));
+#else
+ hss->si.index = (unsigned int) (0.5 + pow(seq_inc_sum, 0.5));
+#endif
+
+ hss->si.index = (unsigned int) (0.5 + log((float)hss->si.index)/log(2.0));
+
+ /* printf("The sequence index is %d\n", hss->si.index);*/
+ if (hss->si.index < 6) {
+ hss->si.seqclass = SEQ_TD;
+ /* printf("Target is a Micro$oft style time dependant box\n");*/
+ }
+ else {
+ hss->si.seqclass = SEQ_RI;
+ /* printf("Target is a random incremental box\n");*/
+ }
+ }
+
+ hss->FP_TSeq = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FP_TSeq->name = "SEQ";
+ seq_AVs = (struct AVal *) safe_zalloc(sizeof(struct AVal) * 5);
+ hss->FP_TSeq->results = seq_AVs;
+ avnum = 0;
+ seq_AVs[avnum].attribute = "CL";
+ switch(hss->si.seqclass) {
+ case SEQ_CONSTANT:
+ strcpy(seq_AVs[avnum].value, "C");
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute= "Val";
+ sprintf(seq_AVs[avnum].value, "%X", hss->si.seqs[0]);
+ break;
+ case SEQ_64K:
+ strcpy(seq_AVs[avnum].value, "64K");
+ break;
+ case SEQ_i800:
+ strcpy(seq_AVs[avnum].value, "i800");
+ break;
+ case SEQ_TD:
+ strcpy(seq_AVs[avnum].value, "TD");
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute="SP";
+ sprintf(seq_AVs[avnum].value, "%X", hss->si.index);
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute= "GCD";
+ sprintf(seq_AVs[avnum].value, "%X", seq_gcd);
+ break;
+ case SEQ_RI:
+ strcpy(seq_AVs[avnum].value, "RI");
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute="SP";
+ sprintf(seq_AVs[avnum].value, "%X", hss->si.index);
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute= "GCD";
+ sprintf(seq_AVs[avnum].value, "%X", seq_gcd);
+ break;
+ case SEQ_TR:
+ strcpy(seq_AVs[avnum].value, "TR");
+ break;
+ }
+
+ /* IP ID Class */
+ switch(hss->si.ipid_seqclass) {
+ case IPID_SEQ_CONSTANT:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "C");
+ break;
+ case IPID_SEQ_INCR:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "I");
+ break;
+ case IPID_SEQ_BROKEN_INCR:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "BI");
+ break;
+ case IPID_SEQ_RPI:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "RPI");
+ break;
+ case IPID_SEQ_RD:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "RD");
+ break;
+ case IPID_SEQ_ZERO:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "Z");
+ break;
+ case IPID_SEQ_LINUX:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "L");
+ break;
+ case IPID_SEQ_VBP:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "IPID";
+ strcpy(seq_AVs[avnum].value, "VBP");
+ break;
+ }
+
+ /* TCP Timestamp option sequencing */
+ switch(hss->si.ts_seqclass) {
+ case TS_SEQ_ZERO:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "TS";
+ strcpy(seq_AVs[avnum].value, "0");
+ break;
+ case TS_SEQ_2HZ:
+ case TS_SEQ_100HZ:
+ case TS_SEQ_1000HZ:
+ case TS_SEQ_OTHER_NUM:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "TS";
+ sprintf(seq_AVs[avnum].value, "%X", (unsigned int)(0.5 + log(avg_ts_hz)/log(2.0)));
+ break;
+ case TS_SEQ_UNSUPPORTED:
+ seq_AVs[avnum].next = &seq_AVs[avnum+1]; avnum++;
+ seq_AVs[avnum].attribute = "TS";
+ strcpy(seq_AVs[avnum].value, "U");
+ break;
+ }
+ }
+ else {
+ log_write(LOG_STDOUT|LOG_NORMAL|LOG_SKID,
+ "Insufficient responses for TCP sequencing (%d), OS detection may be less accurate\n", hss->si.responses);
+ }
+}
+
+void HostOsScan::makeTOpsFP(HostOsScanStats *hss) {
+ assert(hss);
+ int i;
+
+ if (hss->TOpsReplyNum!=6) return;
+
+ for (i=0; i<6; i++) {
+ if (!hss->TOps_AVs[i]) break;
+ if (i<5)
+ hss->TOps_AVs[i]->next = hss->TOps_AVs[i+1];
+ }
+
+ if (i<6) {
+ if (o.debugging)
+ error("We didn't get all the TOps replies from %s", hss->target->targetipstr());
+ return;
+ }
+
+ hss->TOps_AVs[5]->next = NULL;
+
+ hss->FP_TOps= (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FP_TOps->results = hss->TOps_AVs[0];
+ hss->FP_TOps->name = "OPS";
+}
+
+void HostOsScan::makeTWinFP(HostOsScanStats *hss) {
+ assert(hss);
+ int i;
+
+ if (hss->TWinReplyNum!=6) return;
+
+ for (i=0; i<6; i++) {
+ if (!hss->TWin_AVs[i]) break;
+ if (i<5)
+ hss->TWin_AVs[i]->next = hss->TWin_AVs[i+1];
+ }
+
+ if (i<6) {
+ if (o.debugging)
+ error("We didn't get all the TOps replies from %s", hss->target->targetipstr());
+ return;
+ }
+
+ hss->TWin_AVs[5]->next = NULL;
+
+ hss->FP_TWin = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FP_TWin->results = hss->TWin_AVs[0];
+ hss->FP_TWin->name = "WIN";
+}
+
+bool HostOsScan::processTSeqResp(HostOsScanStats *hss, struct ip *ip, int replyNo) {
+ assert(replyNo>=0 && replyNolastipid != 0 && ip->ip_id == hss->lastipid) {
+ /* Probably a duplicate -- this happens sometimes when scanning localhost */
+ return false;
+ }
+ hss->lastipid = ip->ip_id;
+
+ tcp = ((struct tcphdr *) (((char *) ip) + 4 * ip->ip_hl));
+
+ if ((tcp->th_flags & TH_RST)) {
+ if (hss->si.responses == 0) {
+ fprintf(stderr, "WARNING: RST from %s port %lu -- is this port really open?\n",
+ hss->target->targetipstr(), hss->openTCPPort);
+ }
+ return false;
+ }
+
+ if ((tcp->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ /* error("DEBUG: response is SYN|ACK to port %hu\n", ntohs(tcp->th_dport)); */
+ /*readtcppacket((char *)ip, ntohs(ip->ip_len));*/
+ /* We use the ACK value to match up our sent with rcv'd packets */
+ seq_response_num = ntohl(tcp->th_ack) - tcpSeqBase - 1;
+ /* printf("seq_response_num = %d\treplyNo = %d\n", seq_response_num, replyNo); */
+
+ if (seq_response_num != replyNo) {
+ /* BzzT! Value out of range */
+ if (o.debugging) {
+ error("Unable to associate os scan response with sent packet for %s.\nReceived ack: %lX; sequence sent: %lX. Packet:",
+ hss->target->targetipstr(),
+ (unsigned long) ntohl(tcp->th_ack),
+ (unsigned long) tcpSeqBase);
+ readtcppacket((unsigned char *)ip,ntohs(ip->ip_len));
+ }
+ seq_response_num = replyNo;
+ }
+
+ if (hss->si.seqs[seq_response_num] == 0) {
+ /* New response found! */
+ hss->si.responses++;
+ hss->si.seqs[seq_response_num] = ntohl(tcp->th_seq); /* TCP ISN */
+ hss->si.ipids[seq_response_num] = ntohs(ip->ip_id);
+
+ if ((gettcpopt_ts(tcp, ×tamp, NULL) == 0))
+ hss->si.ts_seqclass = TS_SEQ_UNSUPPORTED;
+ else {
+ if (timestamp == 0) {
+ hss->si.ts_seqclass = TS_SEQ_ZERO;
+ }
+ }
+ hss->si.timestamps[seq_response_num] = timestamp;
+ /* printf("Response #%d -- ipid=%hu ts=%i\n", seq_response_num, ntohs(ip->ip_id), timestamp); */
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool HostOsScan::processTOpsResp(HostOsScanStats *hss, struct tcphdr *tcp, int replyNo) {
+ assert(replyNo>=0 || replyNo<6);
+ bool opsParseResult;
+
+ if (hss->FP_TOps || hss->TOps_AVs[replyNo]) return false;
+
+ hss->TOps_AVs[replyNo] = (struct AVal *) safe_zalloc(sizeof(struct AVal));
+ opsParseResult = get_tcpopt_string(tcp, this->tcpMss,
+ hss->TOps_AVs[replyNo]->value,
+ sizeof(hss->TOps_AVs[replyNo]->value));
+
+ if (!opsParseResult) {
+ if (o.debugging)
+ error("Option parse error for TOps response %d from %s.", replyNo, hss->target->targetipstr());
+ hss->TOps_AVs[replyNo]->value[0] = '\0';
+ }
+
+ switch(replyNo) {
+ case 0:
+ hss->TOps_AVs[replyNo]->attribute = "O1";
+ break;
+ case 1:
+ hss->TOps_AVs[replyNo]->attribute = "O2";
+ break;
+ case 2:
+ hss->TOps_AVs[replyNo]->attribute = "O3";
+ break;
+ case 3:
+ hss->TOps_AVs[replyNo]->attribute = "O4";
+ break;
+ case 4:
+ hss->TOps_AVs[replyNo]->attribute = "O5";
+ break;
+ case 5:
+ hss->TOps_AVs[replyNo]->attribute = "O6";
+ break;
+ }
+
+ hss->TOpsReplyNum++;
+ return true;
+}
+
+bool HostOsScan::processTWinResp(HostOsScanStats *hss, struct tcphdr *tcp, int replyNo) {
+ assert(replyNo>=0 || replyNo<6);
+
+ if (hss->FP_TWin || hss->TWin_AVs[replyNo]) return false;
+
+ hss->TWin_AVs[replyNo] = (struct AVal *) safe_zalloc(sizeof(struct AVal));
+ sprintf(hss->TWin_AVs[replyNo]->value, "%hX", ntohs(tcp->th_win));
+
+ switch(replyNo) {
+ case 0:
+ hss->TWin_AVs[replyNo]->attribute = "W1";
+ break;
+ case 1:
+ hss->TWin_AVs[replyNo]->attribute = "W2";
+ break;
+ case 2:
+ hss->TWin_AVs[replyNo]->attribute = "W3";
+ break;
+ case 3:
+ hss->TWin_AVs[replyNo]->attribute = "W4";
+ break;
+ case 4:
+ hss->TWin_AVs[replyNo]->attribute = "W5";
+ break;
+ case 5:
+ hss->TWin_AVs[replyNo]->attribute = "W6";
+ break;
+ }
+
+ hss->TWinReplyNum++;
+ return true;
+}
+
+bool HostOsScan::processTEcnResp(HostOsScanStats *hss, struct ip *ip) {
+ struct AVal *AVs;
+ int i;
+ char *p;
+ int numtests = 5;
+ int current_testno = 0;
+ struct tcphdr *tcp = ((struct tcphdr *) (((char *) ip) + 4 * ip->ip_hl));
+
+ if (hss->FP_TEcn) return false;
+
+ /* Create the Avals */
+ AVs = (struct AVal *) safe_zalloc(numtests * sizeof(struct AVal));
+
+ /* Link them together */
+ for(i=0; i < numtests - 1; i++)
+ AVs[i].next = &AVs[i+1];
+ AVs[numtests-1].next = NULL;
+
+ AVs[current_testno].attribute = "R";
+ strcpy(AVs[current_testno].value, "Y");
+
+ current_testno++;
+
+ /* don't frag flag */
+ AVs[current_testno].attribute = "DF";
+ if(ntohs(ip->ip_off) & IP_DF) {
+ strcpy(AVs[current_testno].value,"Y");
+ } else strcpy(AVs[current_testno].value, "N");
+
+ current_testno++;
+
+ /* TTL */
+ AVs[current_testno].attribute = "T";
+ sprintf(AVs[current_testno].value, "%d", ip->ip_ttl);
+
+ current_testno++;
+
+ /* Explicit Congestion Notification support test */
+ AVs[current_testno].attribute = "CC";
+ if ((tcp->th_flags & TH_ECE) && (tcp->th_flags & TH_CWR))
+ /* echo back */
+ strcpy(AVs[current_testno].value,"S");
+ else if (tcp->th_flags & TH_ECE)
+ /* support */
+ strcpy(AVs[current_testno].value,"Y");
+ else if (!(tcp->th_flags & TH_CWR))
+ /* not support */
+ strcpy(AVs[current_testno].value,"N");
+ else
+ strcpy(AVs[current_testno].value,"O");
+
+ current_testno++;
+
+ /* TCP miscellaneous quirks test */
+ AVs[current_testno].attribute = "Q";
+ p = AVs[current_testno].value;
+ if (tcp->th_x2) {
+ /* Reserved field of TCP is not zero */
+ *p++ = 'R';
+ }
+ if (!(tcp->th_flags & TH_URG) && tcp->th_urp) {
+ /* URG pointer value when urg flag not set */
+ *p++ = 'U';
+ }
+ *p++ = '\0';
+
+ hss->FP_TEcn = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FP_TEcn->name = "ECN";
+ hss->FP_TEcn->results = AVs;
+
+ return true;
+}
+
+bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyNo) {
+
+ assert(replyNo>=0 && replyNo<7);
+
+ int numtests;
+ struct tcphdr *tcp = ((struct tcphdr *) (((char *) ip) + 4 * ip->ip_hl));
+ struct AVal *AVs;
+ int current_testno = 0;
+
+ int i;
+ bool opsParseResult;
+ int length;
+ char *p;
+
+ if (hss->FPtests[FP_T1_7_OFF+replyNo]) return false;
+
+ if(replyNo == 0) numtests = 8; /* T1 doesn't has 'Win','Ops' tests. */
+ else numtests = 10;
+
+ /* Create the Avals */
+ AVs = (struct AVal *) safe_zalloc(numtests * sizeof(struct AVal));
+
+ /* Link them together */
+ for(i=0; i < numtests - 1; i++)
+ AVs[i].next = &AVs[i+1];
+ AVs[numtests-1].next = NULL;
+
+ /* 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 */
+ AVs[current_testno].attribute = "R";
+ strcpy(AVs[current_testno].value, "Y");
+
+ current_testno++;
+
+ /* Next we check whether the Don't Fragment bit is set */
+ AVs[current_testno].attribute = "DF";
+ if(ntohs(ip->ip_off) & 0x4000) {
+ strcpy(AVs[current_testno].value,"Y");
+ } else strcpy(AVs[current_testno].value, "N");
+
+ current_testno++;
+
+ /* TTL */
+ AVs[current_testno].attribute = "T";
+ sprintf(AVs[current_testno].value, "%d", ip->ip_ttl);
+
+ current_testno++;
+
+ if(replyNo!=0) {
+ /* Now we do the TCP Window size */
+ AVs[current_testno].attribute = "W";
+ sprintf(AVs[current_testno].value, "%hX", ntohs(tcp->th_win));
+
+ current_testno++;
+ }
+
+ /* Seq test values:
+ Z = zero
+ A = same as ack
+ A+ = ack + 1
+ O = other
+ */
+ AVs[current_testno].attribute = "S";
+ if (ntohl(tcp->th_seq) == 0)
+ strcpy(AVs[current_testno].value, "Z");
+ else if (ntohl(tcp->th_seq) == tcpAck)
+ strcpy(AVs[current_testno].value, "A");
+ else if (ntohl(tcp->th_seq) == tcpAck + 1)
+ strcpy(AVs[current_testno].value, "A+");
+ else
+ strcpy(AVs[current_testno].value, "O");
+
+ current_testno++;
+
+ /* ACK test values:
+ Z = zero
+ S = same as syn
+ S+ = syn + 1
+ O = other
+ */
+ AVs[current_testno].attribute = "A";
+ if (ntohl(tcp->th_ack) == 0)
+ strcpy(AVs[current_testno].value, "Z");
+ else if (ntohl(tcp->th_ack) == tcpSeqBase)
+ strcpy(AVs[current_testno].value, "S");
+ else if (ntohl(tcp->th_ack) == tcpSeqBase + 1)
+ strcpy(AVs[current_testno].value, "S+");
+ else
+ strcpy(AVs[current_testno].value, "O");
+
+ current_testno++;
+
+ /* Flags. They must be in this order:
+ E = ECN Echo
+ U = Urgent
+ A = Acknowledgement
+ P = Push
+ R = Reset
+ S = Synchronize
+ F = Final
+ */
+ AVs[current_testno].attribute = "F";
+ p = AVs[current_testno].value;
+ if (tcp->th_flags & TH_ECE) *p++ = 'E';
+ if (tcp->th_flags & TH_URG) *p++ = 'U';
+ if (tcp->th_flags & TH_ACK) *p++ = 'A';
+ if (tcp->th_flags & TH_PUSH) *p++ = 'P';
+ if (tcp->th_flags & TH_RST) *p++ = 'R';
+ if (tcp->th_flags & TH_SYN) *p++ = 'S';
+ if (tcp->th_flags & TH_FIN) *p++ = 'F';
+ *p++ = '\0';
+
+ current_testno++;
+
+ if(replyNo!=0) {
+ /* Now for the TCP options ... */
+ AVs[current_testno].attribute = "O";
+ opsParseResult = get_tcpopt_string(tcp, this->tcpMss,
+ AVs[current_testno].value,
+ sizeof(AVs[current_testno].value));
+ if (!opsParseResult) {
+ if (o.debugging)
+ error("Option parse error for T%d response from %s.", replyNo, hss->target->targetipstr());
+ AVs[current_testno].value[0] = '\0';
+ }
+
+ current_testno++;
+ }
+
+ /* Rst Data CRC16 */
+ AVs[current_testno].attribute = "RD";
+ length = (int) ntohs(ip->ip_len) - 4 * ip->ip_hl -4 * tcp->th_off;
+ if ((tcp->th_flags & TH_RST) && length>0) {
+ sprintf(AVs[current_testno].value, "%08lX", crc16(((u8 *)tcp) + 4 * tcp->th_off, length));
+ }
+ else {
+ strcpy(AVs[current_testno].value, "0");
+ }
+
+ current_testno++;
+
+ /* TCP miscellaneous quirks test */
+ AVs[current_testno].attribute = "Q";
+ p = AVs[current_testno].value;
+ if (tcp->th_x2) {
+ /* Reserved field of TCP is not zero */
+ *p++ = 'R';
+ }
+ if (!(tcp->th_flags & TH_URG) && tcp->th_urp) {
+ /* URG pointer value when urg flag not set */
+ *p++ = 'U';
+ }
+ *p = '\0';
+
+ hss->FPtests[FP_T1_7_OFF+replyNo] = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FPtests[FP_T1_7_OFF+replyNo]->results = AVs;
+ hss->FPtests[FP_T1_7_OFF+replyNo]->name = (replyNo == 0)? "T1" : (replyNo == 1)? "T2" : (replyNo == 2)? "T3" : (replyNo == 3)? "T4" : (replyNo == 4)? "T5" : (replyNo == 5)? "T6" : "T7";
+
+ return true;
+}
+
+bool HostOsScan::processTUdpResp(HostOsScanStats *hss, struct ip *ip) {
+ assert(hss);
+ assert(ip);
+
+ struct icmp *icmp;
+ struct ip *ip2;
+ int numtests = 12;
+ unsigned short checksum;
+ unsigned short *checksumptr;
+ udphdr_bsd *udp;
+ struct AVal *AVs;
+ int i;
+ int current_testno = 0;
+ unsigned char *datastart, *dataend;
+
+ if (hss->FP_TUdp) return false;
+
+ icmp = ((struct icmp *)(((char *) ip) + 4 * ip->ip_hl));
+
+ /* Make sure this is icmp port unreachable. */
+ assert(icmp->icmp_type == 3 && icmp->icmp_code == 3);
+
+ ip2 = (struct ip*)((char *)icmp + 8);
+ udp = (udphdr_bsd *)((char *)ip2 + 4 * ip->ip_hl);
+
+ /* The ports should match. */
+ if (ntohs(udp->uh_sport) != hss->upi.sport || ntohs(udp->uh_dport) != hss->upi.dport) {
+ return false;
+ }
+
+ /* Create the Avals */
+ AVs = (struct AVal *) safe_zalloc(numtests * sizeof(struct AVal));
+
+ /* Link them together */
+ for(i=0; i < numtests - 1; i++)
+ AVs[i].next = &AVs[i+1];
+ AVs[numtests-1].next = NULL;
+
+ /* First of all, if we got this far the response was yes */
+ AVs[current_testno].attribute = "R";
+ strcpy(AVs[current_testno].value, "Y");
+
+ current_testno++;
+
+ /* Now let us do an easy one, Don't fragment */
+ AVs[current_testno].attribute = "DF";
+ if(ntohs(ip->ip_off) & 0x4000) {
+ strcpy(AVs[current_testno].value,"Y");
+ } else strcpy(AVs[current_testno].value, "N");
+
+ current_testno++;
+
+ /* TTL */
+ AVs[current_testno].attribute = "T";
+ sprintf(AVs[current_testno].value, "%d", ip->ip_ttl);
+
+ current_testno++;
+
+ /* TOS of the response */
+ AVs[current_testno].attribute = "TOS";
+ sprintf(AVs[current_testno].value, "%hX", ip->ip_tos);
+
+ current_testno++;
+
+ /* Now we look at the IP datagram length that was returned, some
+ machines send more of the original packet back than others */
+ AVs[current_testno].attribute = "IPL";
+ sprintf(AVs[current_testno].value, "%hX", ntohs(ip->ip_len));
+
+ current_testno++;
+
+ /* unused filed not zero in Destination Unreachable Message */
+ AVs[current_testno].attribute = "UN";
+ sprintf(AVs[current_testno].value, "%hX", ntohl(icmp->icmp_hun.ih_void));
+
+ current_testno++;
+
+ /* OK, lets check the returned IP length, some systems @$@ this
+ up */
+ AVs[current_testno].attribute = "RIPL";
+ if(ntohs(ip2->ip_len) == 328)
+ strcpy(AVs[current_testno].value, "G");
+ else
+ sprintf(AVs[current_testno].value, "%hX", ntohs(ip2->ip_len));
+
+ current_testno++;
+
+ /* 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 ... */
+ AVs[current_testno].attribute = "RID";
+ if (ip2->ip_id == hss->upi.ipid)
+ strcpy(AVs[current_testno].value, "G"); /* The good "expected" value */
+ else
+ sprintf(AVs[current_testno].value, "%hX", ntohs(ip2->ip_id));
+
+ current_testno++;
+
+#endif
+
+ /* Let us see if the IP checksum we got back computes */
+
+ AVs[current_testno].attribute = "RIPCK";
+ /* Thanks to some machines not having struct ip member ip_sum we
+ have to go with this BS */
+ checksumptr = (unsigned short *) ((char *) ip2 + 10);
+ checksum = *checksumptr;
+
+ if (checksum == 0)
+ strcpy(AVs[current_testno].value, "0");
+ else {
+ *checksumptr = 0;
+ if (in_cksum((unsigned short *)ip2, 20) == checksum) {
+ strcpy(AVs[current_testno].value, "G"); /* The "expected" good value */
+ } else {
+ strcpy(AVs[current_testno].value, "I"); /* They fucked it up */
+ }
+ *checksumptr = checksum;
+ }
+
+ current_testno++;
+
+ /* UDP checksum */
+ AVs[current_testno].attribute = "RUCK";
+ if (udp->uh_sum == hss->upi.udpck)
+ strcpy(AVs[current_testno].value, "G"); /* The "expected" good value */
+ else
+ sprintf(AVs[current_testno].value, "%hX", ntohs(udp->uh_sum));
+
+ current_testno++;
+
+ /* UDP length ... */
+ AVs[current_testno].attribute = "RUL";
+ if(ntohs(udp->uh_ulen) == 308)
+ strcpy(AVs[current_testno].value, "G"); /* The "expected" good value */
+ else
+ sprintf(AVs[current_testno].value, "%hX", ntohs(udp->uh_ulen));
+
+ current_testno++;
+
+ /* Finally we ensure the data is OK */
+ datastart = ((unsigned char *)udp) + 8;
+ dataend = (unsigned char *) ip + ntohs(ip->ip_len);
+
+ while(datastart < dataend) {
+ if (*datastart != hss->upi.patternbyte) break;
+ datastart++;
+ }
+ AVs[current_testno].attribute = "RUD";
+ if (datastart < dataend)
+ strcpy(AVs[current_testno].value, "I"); /* They fucked it up */
+ else
+ strcpy(AVs[current_testno].value, "G");
+
+ hss->FP_TUdp = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FP_TUdp->name = "U1";
+ hss->FP_TUdp->results = AVs;
+
+ /* Count hop count */
+ if (hss->distance == -1) {
+ hss->distance = this->udpttl - ip2->ip_ttl;
+ }
+
+ return true;
+}
+
+bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, struct ip *ip, int replyNo) {
+ assert(replyNo==0 || replyNo==1);
+
+ int numtests = 7;
+ struct AVal *AVs;
+ struct ip *ip1, *ip2;
+ struct icmp *icmp1, *icmp2;
+ unsigned short value1, value2;
+ int i;
+ int current_testno = 0;
+
+ if (hss->FP_TIcmp) return false;
+
+ if (hss->icmpEchoReply == NULL) {
+ /* This is the first icmp reply we get, store it and return. */
+ hss->icmpEchoReply = (struct ip *) safe_malloc(ntohs(ip->ip_len));
+ memcpy(hss->icmpEchoReply, ip, ntohs(ip->ip_len));
+ hss->storedIcmpReply = replyNo;
+ return true;
+ }
+ else if (hss->storedIcmpReply == replyNo) {
+ /* This is a dunplicated icmp reply. */
+ return false;
+ }
+
+ /* Ok, now we get another reply. */
+ if (hss->storedIcmpReply == 0) {
+ ip1 = hss->icmpEchoReply;
+ ip2 = ip;
+ } else {
+ ip1 = ip;
+ ip2 = hss->icmpEchoReply;
+ }
+
+ icmp1 = ((struct icmp *)(((char *) ip1) + 4 * ip1->ip_hl));
+ icmp2 = ((struct icmp *)(((char *) ip2) + 4 * ip2->ip_hl));
+
+ assert(icmp1->icmp_type == 0 && icmp2->icmp_type == 0);
+
+ /* Create the Avals */
+ AVs = (struct AVal *) safe_zalloc(numtests * sizeof(struct AVal));
+
+ /* Link them together */
+ for(i=0; i < numtests - 1; i++)
+ AVs[i].next = &AVs[i+1];
+ AVs[numtests-1].next = NULL;
+
+ AVs[current_testno].attribute = "R";
+ strcpy(AVs[current_testno].value, "Y");
+
+ current_testno++;
+
+ /* 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).
+ */
+ AVs[current_testno].attribute = "DFI";
+ value1 = (ntohs(ip1->ip_off) & IP_DF);
+ value2 = (ntohs(ip2->ip_off) & IP_DF);
+ if (value1 && value2)
+ /* both set */
+ strcpy(AVs[current_testno].value, "Y");
+ else if (value1 && !value2)
+ /* echo back */
+ strcpy(AVs[current_testno].value, "S");
+ else if (!value1 && !value2)
+ /* neither set */
+ strcpy(AVs[current_testno].value, "N");
+ else
+ strcpy(AVs[current_testno].value, "O");
+
+ current_testno++;
+
+ /* TTL */
+
+ AVs[current_testno].attribute = "T";
+ sprintf(AVs[current_testno].value, "%d", ip1->ip_ttl);
+
+ current_testno++;
+
+ /* Type of service. Test values:
+ * Z. Both are zero;
+ * NN. Both use the same non-zero number;
+ * S. Both use the TOS that the sender uses;
+ * O. Other.
+ */
+ AVs[current_testno].attribute = "TOSI";
+ value1 = ip1->ip_tos;
+ value2 = ip2->ip_tos;
+ if (value1 == value2){
+ if (value1 == 0)
+ strcpy(AVs[current_testno].value, "Z");
+ else
+ sprintf(AVs[current_testno].value, "%hX", value1);
+ }
+ else if (value1 == IP_TOS_DEFAULT && value2 == IP_TOS_RELIABILITY)
+ /* the same with sender */
+ strcpy(AVs[current_testno].value, "S");
+ else
+ strcpy(AVs[current_testno].value, "O");
+
+ current_testno++;
+
+ /* 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.
+ */
+ AVs[current_testno].attribute = "CD";
+ value1 = icmp1->icmp_code;
+ value2 = icmp2->icmp_code;
+ if (value1 == value2){
+ if (value1 == 0)
+ strcpy(AVs[current_testno].value, "Z");
+ else
+ sprintf(AVs[current_testno].value, "%hX", value1);
+ }
+ else if (value1 == 9 && value2 == 0)
+ /* both the same as in the corresponding probe */
+ strcpy(AVs[current_testno].value, "S");
+ else
+ strcpy(AVs[current_testno].value, "O");
+
+ current_testno++;
+
+ /* Sequence Number value in Icmp echo reply. SI test values:
+ * Z. Both are set to zero;
+ * [value]. Both set Seq to the same value [Value];
+ * S. Both use the Seq value that the sender uses;
+ * O. Other.
+ */
+ AVs[5].attribute = "SI";
+ value1 = icmp1->icmp_seq;
+ value2 = icmp2->icmp_seq;
+ if (value1 == value2) {
+ if (value1 == 0)
+ strcpy(AVs[current_testno].value, "Z");
+ else
+ sprintf(AVs[current_testno].value, "%hX", value1);
+ }
+ else if (value1 == this->icmpEchoSeq && value2 == this->icmpEchoSeq + 1)
+ /* Both echo the ones from the probes. */
+ strcpy(AVs[current_testno].value, "S");
+ else {
+ /*
+ if (o.debugging)
+ printf("Seq value in icmp replies from %s aren't the same with the sender. Seq1 = %d\tSeq2 = %d\n",
+ hss->target->targetipstr(), value1, value2);
+ */
+ strcpy(AVs[current_testno].value, "O");
+ }
+
+ current_testno++;
+
+ /* ICMP data length. Pattens:
+ * [Value]. Both truncted to a specific value;
+ * S. Both the same with the sender;
+ * O. Other.
+ */
+ AVs[current_testno].attribute = "DLI";
+ value1 = ntohs(ip1->ip_len) - 4 * ip1->ip_hl - 8;
+ value2 = ntohs(ip2->ip_len) - 4 * ip2->ip_hl - 8;
+ if (value1 == value2){
+ if (value1 == 0)
+ strcpy(AVs[current_testno].value, "Z");
+ else
+ sprintf(AVs[current_testno].value, "%hX", value1);
+ }
+ else if (value1 == 120 && value2 == 150)
+ /* the same as in the corresponding probe */
+ strcpy(AVs[current_testno].value, "S");
+ else
+ /* */
+ strcpy(AVs[current_testno].value, "O");
+
+ hss->FP_TIcmp= (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
+ hss->FP_TIcmp->name = "IE";
+ hss->FP_TIcmp->results = AVs;
+
+ return true;
+}
+
+bool HostOsScan::get_tcpopt_string(struct tcphdr *tcp, int mss, char *result, int maxlen) {
+ char *p,*q;
+ u16 tmpshort;
+ u32 tmpword;
+ int length;
+ int opcode;
+
+ p = result;
+ length = (tcp->th_off * 4) - sizeof(struct tcphdr);
+ q = ((char *)tcp) + sizeof(struct tcphdr);
+
+ /*
+ * Example parsed result: M5B4ST11NW2
+ * MSS, Sack Permitted, Timestamp with both value not zero, Nop, WScale with value 2
+ */
+
+ /* Be aware of the max increament value for p in parsing,
+ * now is 5 = strlen("Mxxxx") <-> MSS Option
+ */
+ while(length > 0 && (p - result) < (maxlen - 5)) {
+ opcode=*q++;
+ if (!opcode) { /* End of List */
+ *p++ = 'L';
+ length--;
+ } else if (opcode == 1) { /* No Op */
+ *p++ = 'N';
+ length--;
+ } else if (opcode == 2) { /* MSS */
+ if(length<4)
+ break; /* MSS has 4 bytes */
+ *p++ = 'M';
+ q++;
+ memcpy(&tmpshort, q, 2);
+ /* if(ntohs(tmpshort) == mss) */
+ /* *p++ = 'E'; */
+ sprintf(p, "%hX", ntohs(tmpshort));
+ p += strlen(p); /* max movement of p is 4 (0xFFFF) */
+ q += 2;
+ length -= 4;
+ } else if (opcode == 3) { /* Window Scale */
+ if(length<3)
+ break; /* Window Scale option has 3 bytes */
+ *p++ = 'W';
+ q++;
+ sprintf(p, "%hX", *((u8*)q));
+ p += strlen(p); /* max movement of p is 2 (max WScale value is 0xFF) */
+ q++;
+ length -= 3;
+ } else if (opcode == 4) { /* SACK permitted */
+ if(length<2)
+ break; /* SACK permitted option has 2 bytes */
+ *p++ = 'S';
+ q++;
+ length -= 2;
+ } else if (opcode == 8) { /* Timestamp */
+ if(length<10)
+ break; /* Timestamp option has 10 bytes */
+ *p++ = 'T';
+ q++;
+ memcpy(&tmpword, q, 4);
+ if(tmpword)
+ *p++ = '1';
+ else
+ *p++ = '0';
+ q += 4;
+ memcpy(&tmpword, q, 4);
+ if(tmpword)
+ *p++ = '1';
+ else
+ *p++ = '0';
+ q += 4;
+ length -= 10;
+ }
+ }
+
+ if(length>0) {
+ /* We could reach here for one of the two reasons:
+ * 1. At least one option is not correct. (Eg. Should have 4 bytes but only has 3 bytes left).
+ * 2. The option string is too long.
+ */
+ *result = '\0';
+ return false;
+ }
+
+ *p = '\0';
+ return true;
+}
+
+HostOsScanInfo::HostOsScanInfo(Target *t, OsScanInfo *OsSI) {
+ int i;
+
+ target = t;
+ OSI = OsSI;
+
+ for (i=0; iFPR == NULL)
+ target->FPR = new FingerPrintResults;
+ target->osscan_performed = 1;
+
+ hss = new HostOsScanStats(t);
+}
+
+HostOsScanInfo::~HostOsScanInfo() {
+ delete hss;
+}
+
+OsScanInfo::OsScanInfo(vector &Targets) {
+ unsigned int targetno;
+ HostOsScanInfo *hsi;
+ int num_timedout = 0;
+
+ gettimeofday(&now, NULL);
+
+ /* build up incompleteHosts list */
+ for(targetno = 0; targetno < Targets.size(); targetno++) {
+ /* check if Targets[targetno] is good to be scanned
+ * if yes, append it to the list
+ */
+ if (Targets[targetno]->timedOut(&now)) {
+ num_timedout++;
+ continue;
+ }
+
+#ifdef WIN32
+ if (Targets[targetno]->ifType() == devt_loopback) {
+ log_write(LOG_STDOUT, "Skipping OS Scan against %s because it doesn't work against your own machine (localhsot)\n", Targets[targetno]->NameIP());
+ continue;
+ }
+#endif
+
+ if (Targets[targetno]->ports.getStateCounts(IPPROTO_TCP, PORT_OPEN) == 0 ||
+ (Targets[targetno]->ports.getStateCounts(IPPROTO_TCP, PORT_CLOSED) == 0 &&
+ Targets[targetno]->ports.getStateCounts(IPPROTO_TCP, PORT_UNFILTERED) == 0)) {
+ if (o.osscan_limit) {
+ if (o.verbose)
+ log_write(LOG_STDOUT|LOG_NORMAL|LOG_SKID, "Skipping OS Scan against %s due to absence of open (or perhaps closed) ports\n", Targets[targetno]->NameIP());
+ continue;
+ } else {
+ log_write(LOG_STDOUT|LOG_NORMAL|LOG_SKID,"Warning: OS detection for %s will be MUCH less reliable because we did not find at least 1 open and 1 closed TCP port\n", Targets[targetno]->targetipstr());
+ }
+ }
+
+ 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(struct sockaddr_storage *ss) {
+ list::iterator hostI;
+ struct sockaddr_in *sin = (struct sockaddr_in *) ss;
+
+ if (sin->sin_family != AF_INET)
+ fatal("UltraScanInfo::findIncompleteHost passed a non IPv4 address");
+
+ 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() {
+ list::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.TimeSinceStartMS() - this->starttimems) / 1000.0, 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;
+}
+
+int send_icmp_echo_probe(int sd, struct eth_nfo *eth, const struct in_addr *victim,
+ u8 tos, bool df, u8 pcode, unsigned short id, u16 seq, u16 datalen) {
+ u8 *packet = NULL;
+ u32 packetlen = 0;
+ int decoy;
+ int res = -1;
+
+ for(decoy = 0; decoy < o.numdecoys; decoy++) {
+ packet = build_icmp_raw(&o.decoys[decoy], victim, o.ttl,
+ get_random_u16(), tos, df, seq, id, ICMP_ECHO, pcode, NULL,
+ datalen, &packetlen);
+ if(!packet) return -1;
+ res = send_ip_packet(sd, eth, packet, packetlen);
+ free(packet);
+ if(res==-1) return -1;
+ }
+
+ return 0;
+}
+
+int send_closedudp_probe_2(struct udpprobeinfo &upi, int sd,
+ struct eth_nfo *eth, const struct in_addr *victim,
+ int ttl, u16 sport, u16 dport) {
+ static int myttl = 0;
+ static u8 patternbyte = 0x41; /* character 'A' */
+ static u16 id = 0x1042;
+ u8 packet[328]; /* 20 IP hdr + 8 UDP hdr + 300 data */
+ struct ip *ip = (struct ip *) packet;
+ udphdr_bsd *udp = (udphdr_bsd *) (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 pseudo_udp_hdr {
+ struct in_addr source;
+ struct in_addr dest;
+ u8 zero;
+ u8 proto;
+ u16 length;
+ } *pseudo = (struct pseudo_udp_hdr *) ((char *)udp - 12) ;
+
+ /* 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 ( !victim || !sport || !dport || (!eth && sd < 0)) {
+ fprintf(stderr, "send_closedudp_probe_2: One or more of your parameters suck!\n");
+ return 1;
+ }
+
+ for(decoy=0; decoy < o.numdecoys; decoy++) {
+ source = &o.decoys[decoy];
+
+ memset((char *) packet, 0, sizeof(struct ip) + sizeof(udphdr_bsd));
+
+ udp->uh_sport = htons(sport);
+ udp->uh_dport = htons(dport);
+ udp->uh_ulen = htons(8 + datalen);
+
+ /* Now the psuedo header for checksuming */
+ pseudo->source.s_addr = source->s_addr;
+ pseudo->dest.s_addr = victim->s_addr;
+ pseudo->proto = IPPROTO_UDP;
+ pseudo->length = htons(sizeof(udphdr_bsd) + datalen);
+
+ /* OK, now we should be able to compute a valid checksum */
+ realcheck = in_cksum((unsigned short *)pseudo, 20 /* pseudo + UDP headers */ +
+ datalen);
+#if STUPID_SOLARIS_CHECKSUM_BUG
+ udp->uh_sum = sizeof(udphdr_bsd) + datalen;
+#else
+ udp->uh_sum = realcheck;
+#endif
+
+ /* Goodbye, pseudo header! */
+ memset(pseudo, 0, sizeof(*pseudo));
+
+ /* Now for the ip header */
+ ip->ip_v = 4;
+ ip->ip_hl = 5;
+ ip->ip_len = htons(sizeof(struct ip) + sizeof(udphdr_bsd) + datalen);
+ ip->ip_id = id;
+ ip->ip_ttl = myttl;
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_src.s_addr = source->s_addr;
+ ip->ip_dst.s_addr= victim->s_addr;
+
+ upi.ipck = in_cksum((unsigned short *)ip, sizeof(struct ip));
+#if HAVE_IP_IP_SUM
+ ip->ip_sum = 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) {
+ upi.iptl = 28 + datalen;
+ upi.ipid = id;
+ upi.sport = sport;
+ upi.dport = dport;
+ upi.udpck = realcheck;
+ upi.udplen = 8 + datalen;
+ upi.patternbyte = patternbyte;
+ upi.target.s_addr = ip->ip_dst.s_addr;
+ }
+ if (TCPIP_DEBUGGING > 1) {
+ log_write(LOG_STDOUT, "Raw UDP packet creation completed! Here it is:\n");
+ readudppacket(packet,1);
+ }
+
+ if ((res = send_ip_packet(sd, eth, packet, ntohs(ip->ip_len))) == -1)
+ {
+ perror("send_ip_packet in send_closedupd_probe_2");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+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;
+}
+
+/* New ipid incremental type judging function.
+ * Judge by tcp ipid, icmp echo reply ipid, icmp destination unreachable ipid.
+ */
+int get_ipid_sequence(struct ipid_info *ipid, int islocalhost) {
+ int i;
+ u16 ipid_tcp[NUM_SEQ_SAMPLES]; /* tcp */
+ u16 ipid_icmp[NUM_SEQ_SAMPLES]; /* icmp */
+ int num_tcp = 0;
+ int num_icmp = 0;
+ int class_tcp = IPID_SEQ_UNKNOWN;
+ int class_icmp = IPID_SEQ_UNKNOWN;
+ int diff = 0; /* gap between icmp ipid and tcp ipid. */
+
+ int result = IPID_SEQ_UNKNOWN;
+
+ for(i = 0; i < NUM_SEQ_SAMPLES; i++) {
+ /* tcp ipids. */
+ if(ipid->tcp_ipids[i] != -1) {
+ ipid_tcp[num_tcp++] = u16(ipid->tcp_ipids[i]);
+ }
+
+ /* icmp ipids. */
+ if(ipid->icmp_ipids[i] != -1) {
+ ipid_icmp[num_icmp++] = u16(ipid->icmp_ipids[i]);
+ }
+
+ }
+
+ if(num_tcp >= 2) class_tcp = ipid_sequence(num_tcp, ipid_tcp, islocalhost);
+ if(num_icmp >= 2) class_icmp = ipid_sequence(num_icmp, ipid_icmp, islocalhost);
+
+ /* printf("class_tcp = %d, class_icmp = %d.\n", class_tcp, class_icmp); */
+
+ if(num_tcp>0 && num_icmp>0) diff = ipid_icmp[0] - ipid_tcp[num_tcp-1];
+
+ switch (class_tcp) {
+
+ case IPID_SEQ_ZERO:
+ if(class_icmp == IPID_SEQ_INCR)
+ /* Different IPID counter in different TCP session but set zero
+ * in the TCP negotiation process. And different IPID counter in
+ * different protocol. Found in linux 2.6.
+ */
+ result = IPID_SEQ_LINUX;
+ else if(num_icmp>0 && ipid_icmp[num_icmp]!=0)
+ /* not all zero. */
+ result = IPID_SEQ_UNKNOWN;
+ break;
+
+ case IPID_SEQ_INCR:
+ if(class_icmp == IPID_SEQ_INCR) {
+ if(diff>=1 && diff<=9) {
+ /* all ipids are incremental */
+ result = IPID_SEQ_INCR;
+ } else {
+ /* different ipid counter in tcp, icmp */
+ result = IPID_SEQ_VBP;
+ }
+ } else {
+ result = IPID_SEQ_UNKNOWN;
+ }
+ break;
+
+ case IPID_SEQ_UNKNOWN:
+ /* use class_icmp */
+ result = class_icmp;
+ break;
+
+ default:
+ result = class_tcp;
+ break;
+ }
+
+ return result;
+}
+
+/* This is the function for tuning the major values that affect
+ scan performance */
+static void init_perf_values() {
+ memset(&perf, 0, sizeof(perf));
+ /* TODO: I should revisit these values for tuning. They should probably
+ at least be affected by -T. */
+ perf.low_cwnd = MAX(o.min_parallelism, 1);
+ perf.max_cwnd = o.max_parallelism? o.max_parallelism : 300;
+ perf.group_initial_cwnd = box(o.min_parallelism, perf.max_cwnd, 10);
+ perf.host_initial_cwnd = perf.group_initial_cwnd;
+ perf.quick_incr = 1;
+ perf.cc_incr = 1;
+ perf.initial_ccthresh = 50;
+ perf.ping_magnifier = 3;
+ perf.pingtime = 5000000;
+ perf.group_drop_cwnd_divisor = 2.0;
+ perf.group_drop_ccthresh_divisor = (o.timing_level < 4)? 2.0 : 1.5;
+ perf.host_drop_ccthresh_divisor = (o.timing_level < 4)? 2.0 : 1.5;
+ perf.tryno_cap = 12;
+}
+
+/* Start the timeout clocks of any targets that aren't already timedout
+ */
+static void startTimeOutClocks(OsScanInfo *OSI) {
+ list::iterator hostI;
+
+ gettimeofday(&now, NULL);
+ for(hostI = OSI->incompleteHosts.begin();
+ hostI != OSI->incompleteHosts.end(); hostI++) {
+ if (!(*hostI)->target->timedOut(NULL))
+ (*hostI)->target->startTimeOutClock(&now);
+ }
+}
+
+static void begin_sniffer(HostOsScan *HOS, vector &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 (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");
+ }
+ filterlen = 0;
+
+ HOS->pd = my_pcap_open_live(Targets[0]->deviceName(), 8192, (o.spoofsource)? 1 : 0, 2);
+
+ if (doIndividual)
+ len = snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s and (icmp or (tcp and (%s",
+ inet_ntoa(Targets[0]->v4source()), dst_hosts);
+ else
+ len = snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s and (icmp or tcp)",
+ inet_ntoa(Targets[0]->v4source()));
+ if (len < 0 || len >= (int) sizeof(pcap_filter))
+ fatal("ran out of space in pcap filter");
+ filterlen = len;
+
+ if (o.debugging > 2) printf("Pcap filter: %s\n", pcap_filter);
+ set_pcap_filter(Targets[0]->deviceName(), HOS->pd, pcap_filter);
+
+ return;
+}
+
+static void startRound(OsScanInfo *OSI, HostOsScan *HOS, int roundNum) {
+ list::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();
+ }
+}
+
+static void doSeqTests(OsScanInfo *OSI, HostOsScan *HOS) {
+ list::iterator hostI;
+ HostOsScanInfo *hsi = NULL;
+ HostOsScanStats *hss = NULL;
+ unsigned int unableToSend; /* # of times in a row that hosts were unable to send probe */
+ unsigned int expectReplies;
+ long to_usec;
+ int timeToSleep = 0;
+
+ struct ip *ip = NULL;
+ struct link_header linkhdr;
+ struct sockaddr_in sin;
+ 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->buildSeqProbeList(hss);
+ }
+
+ do {
+ if(timeToSleep > 0) {
+ if(o.debugging > 1) {
+ printf("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;
+ printf("Host %s. ProbesToSend %d: \tProbesActive %d\n",
+ hss->target->targetipstr(), hss->numProbesToSend(),
+ hss->numProbesActive());
+ }
+ }
+
+ /* Send a seq probe to each host. */
+ while(unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) {
+ hsi = OSI->nextIncompleteHost();
+ hss = hsi->hss;
+ gettimeofday(&now, NULL);
+ if (hss->numProbesToSend()>0 && HOS->hostSeqSendOK(hss, NULL)) {
+ HOS->sendNextProbe(hss);
+ expectReplies++;
+ unableToSend = 0;
+ } else {
+ unableToSend++;
+ }
+ }
+
+ HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent;
+
+ gettimeofday(&now, NULL);
+
+ /* Count the pcap wait time. */
+ if(!HOS->stats->sendOK()) {
+ TIMEVAL_MSEC_ADD(stime, now, 1000);
+
+ for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) {
+ if (HOS->nextTimeout((*hostI)->hss, &tmptv)) {
+ if (TIMEVAL_SUBTRACT(tmptv, stime) < 0)
+ stime = tmptv;
+ }
+ }
+ }
+ else {
+ foundgood = false;
+ for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) {
+ thisHostGood = HOS->hostSeqSendOK((*hostI)->hss, &tmptv);
+ if (thisHostGood) {
+ stime = tmptv;
+ foundgood = true;
+ break;
+ }
+
+ if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) {
+ stime = tmptv;
+ foundgood = true;
+ }
+ }
+ }
+
+ do {
+ to_usec = TIMEVAL_SUBTRACT(stime, now);
+ if(to_usec < 2000) to_usec = 2000;
+
+ if(o.debugging > 2)
+ printf("pcap wait time is %ld.\n", to_usec);
+
+ ip = (struct ip*) readip_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr);
+
+ gettimeofday(&now, NULL);
+
+ if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) {
+ timedout = true;
+ break;
+ } else if (!ip) {
+ continue;
+ }
+
+ if (TIMEVAL_SUBTRACT(now, stime) > 200000) {
+ /* While packets are still being received, I'll be generous and give
+ an extra 1/5 sec. But we have to draw the line somewhere */
+ timedout = true;
+ }
+
+ if(bytes < 20 || bytes < (4 * ip->ip_hl) + 4U)
+ continue;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_addr.s_addr = ip->ip_src.s_addr;
+ sin.sin_family = AF_INET;
+ hsi = OSI->findIncompleteHost((struct sockaddr_storage *) &sin);
+ if (!hsi) continue; /* Not from one of our targets. */
+ setTargetMACIfAvailable(hsi->target, &linkhdr, ip, 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) {
+ list::iterator hostI;
+ HostOsScanInfo *hsi = NULL;
+ HostOsScanStats *hss = NULL;
+ unsigned int unableToSend; /* # of times in a row that hosts were unable to send probe */
+ unsigned int expectReplies;
+ long to_usec;
+ int timeToSleep = 0;
+
+ struct ip *ip = NULL;
+ struct link_header linkhdr;
+ struct sockaddr_in sin;
+ 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) {
+ printf("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;
+ printf("Host %s. ProbesToSend %d: \tProbesActive %d\n",
+ hss->target->targetipstr(), hss->numProbesToSend(),
+ hss->numProbesActive());
+ }
+ }
+
+ while(unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) {
+ hsi = OSI->nextIncompleteHost();
+ hss = hsi->hss;
+ gettimeofday(&now, NULL);
+ if (hss->numProbesToSend()>0 && HOS->hostSendOK(hss, NULL)) {
+ HOS->sendNextProbe(hss);
+ expectReplies++;
+ unableToSend = 0;
+ } else {
+ unableToSend++;
+ }
+ }
+
+ HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent;
+
+ gettimeofday(&now, NULL);
+
+ /* Count the pcap wait time. */
+ if(!HOS->stats->sendOK()) {
+ TIMEVAL_MSEC_ADD(stime, now, 1000);
+
+ for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end();
+ hostI++) {
+ if (HOS->nextTimeout((*hostI)->hss, &tmptv)) {
+ if (TIMEVAL_SUBTRACT(tmptv, stime) < 0)
+ stime = tmptv;
+ }
+ }
+ }
+ else {
+ foundgood = false;
+ for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) {
+ thisHostGood = HOS->hostSendOK((*hostI)->hss, &tmptv);
+ if (thisHostGood) {
+ stime = tmptv;
+ foundgood = true;
+ break;
+ }
+
+ if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) {
+ stime = tmptv;
+ foundgood = true;
+ }
+ }
+ }
+
+ do {
+ to_usec = TIMEVAL_SUBTRACT(stime, now);
+ if(to_usec < 2000) to_usec = 2000;
+
+ if(o.debugging > 2)
+ printf("pcap wait time is %ld.\n", to_usec);
+
+ ip = (struct ip*) readip_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr);
+
+ gettimeofday(&now, NULL);
+
+ if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) {
+ timedout = true;
+ break;
+ } else if (!ip) {
+ continue;
+ }
+
+ if (TIMEVAL_SUBTRACT(now, stime) > 200000) {
+ /* While packets are still being received, I'll be generous and give
+ an extra 1/5 sec. But we have to draw the line somewhere */
+ timedout = true;
+ }
+
+ if(bytes < 20 || bytes < (4 * ip->ip_hl) + 4U)
+ continue;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_addr.s_addr = ip->ip_src.s_addr;
+ sin.sin_family = AF_INET;
+ hsi = OSI->findIncompleteHost((struct sockaddr_storage *) &sin);
+ if (!hsi) continue; /* Not from one of our targets. */
+ setTargetMACIfAvailable(hsi->target, &linkhdr, ip, 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) {
+ list::iterator hostI;
+ HostOsScanInfo *hsi = NULL;
+
+ for(hostI = OSI->incompleteHosts.begin();
+ hostI != OSI->incompleteHosts.end(); hostI++) {
+
+ hsi = *hostI;
+ HOS->makeFP(hsi->hss);
+
+ hsi->FPs[roundNum] = hsi->hss->getFP();
+ hsi->target->FPR->FPs[roundNum] = hsi->FPs[roundNum];
+ 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) {
+ hsi->target->FPR->numFPs = roundNum + 1;
+ memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info));
+ if (roundNum > 0) {
+ if(o.verbose) error("WARNING: OS didn't match until the try #%d", roundNum + 1);
+ }
+ hsi->target->FPR->goodFP = roundNum;
+ match_fingerprint(hsi->target->FPR->FPs[roundNum], hsi->target->FPR,
+ o.reference_FPs, OSSCAN_GUESS_THRESHOLD);
+ hsi->isCompleted = true;
+ }
+
+ if(hsi->hss->distance != -1)
+ hsi->target->distance = hsi->target->FPR->distance = hsi->hss->distance;
+ }
+ OSI->removeCompletedHosts();
+}
+
+/* Stop the timeout clocks of the targets
+ */
+static void stopTimeOutClocks(OsScanInfo *OSI) {
+ list::iterator hostI;
+
+ gettimeofday(&now, NULL);
+ for(hostI = OSI->incompleteHosts.begin();
+ hostI != OSI->incompleteHosts.end(); hostI++) {
+ (*hostI)->target->stopTimeOutClock(&now);
+ }
+}
+
+static void findBestFPs(OsScanInfo *OSI, int numFPs) {
+ list::iterator hostI;
+ HostOsScanInfo *hsi = NULL;
+ int i;
+
+ double bestacc;
+ int bestaccidx;
+
+ for(hostI = OSI->incompleteHosts.begin();
+ hostI != OSI->incompleteHosts.end(); hostI++) {
+ hsi = *hostI;
+ hsi->target->FPR->numFPs = numFPs;
+ 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->target->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;
+ }
+ }
+
+ hsi->target->FPR->goodFP = bestaccidx;
+
+ // 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.
+ if (hsi->target->FPR->goodFP >= 0)
+ match_fingerprint(hsi->target->FPR->FPs[bestaccidx], hsi->target->FPR,
+ o.reference_FPs, OSSCAN_GUESS_THRESHOLD);
+ }
+}
+
+static void printFP(OsScanInfo *OSI) {
+ list::iterator hostI;
+ HostOsScanInfo *hsi = NULL;
+ FingerPrintResults *FPR;
+ int distance = -1;
+
+ for(hostI = OSI->incompleteHosts.begin();
+ hostI != OSI->incompleteHosts.end(); hostI++) {
+ hsi = *hostI;
+ FPR = hsi->target->FPR;
+
+ if (islocalhost(hsi->target->v4hostip())) {
+ /* scanning localhost */
+ distance = 0;
+ } else if (hsi->target->MACAddress()) {
+ /* on the same network segment */
+ distance = 1;
+ } else if (hsi->target->distance!=-1) {
+ distance = hsi->target->distance;
+ }
+
+ 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->v4hostip(), distance, hsi->target->MACAddress(),
+ FPR->osscan_opentcpport, FPR->osscan_closedtcpport, FPR->osscan_closedudpport,
+ false));
+ }
+}
+
+static void doOsScan1(OsScanInfo *OSI) {
+ list::iterator hostI;
+
+ if(!o.reference_FPs1)
+ o.reference_FPs1 = parse_fingerprint_reference_file("nmap-os-fingerprints");
+
+ for(hostI = OSI->incompleteHosts.begin();
+ hostI != OSI->incompleteHosts.end(); hostI++) {
+ if(o.verbose)
+ log_write(LOG_STDOUT, "OSScan against host %s now falls back on the old OS scan system\n",
+ (*hostI)->target->targetipstr());
+ os_scan((*hostI)->target);
+ }
+}
+
+int os_scan_2(vector &Targets) {
+ OsScanInfo *OSI;
+ HostOsScan *HOS;
+ int itry;
+
+ if (Targets.size() == 0) {
+ return 1;
+ }
+
+ init_perf_values();
+
+ OSI = new OsScanInfo(Targets);
+ if(OSI->numIncompleteHosts() == 0) {
+ /* no one will be scanned */
+ delete OSI;
+ return 1;
+ }
+ OSI->starttimems = o.TimeSinceStartMS();
+
+ HOS = new HostOsScan(Targets[0]);
+ startTimeOutClocks(OSI);
+
+ itry = 0;
+ begin_sniffer(HOS, Targets); /* initial the pcap session handler in HOS */
+ while(OSI->numIncompleteHosts() != 0 && itry 0) sleep(1);
+ startRound(OSI, HOS, itry);
+ doSeqTests(OSI, HOS);
+ doTUITests(OSI, HOS);
+ endRound(OSI, HOS, itry);
+ itry++;
+ }
+
+ if (OSI->numIncompleteHosts() > 0 && itry == MAX_SCAN_ROUND) {
+ /* For host that doesn't have a perfect match, we do the following
+ things. */
+
+ stopTimeOutClocks(OSI);
+
+ /* Find the most matching item in the db. */
+ findBestFPs(OSI, MAX_SCAN_ROUND);
+
+ /* Print the fp in debug mode.
+ Normally let output.cc to print the FP. */
+ if(o.debugging > 1)
+ printFP(OSI);
+
+ /*
+ * OK. Now let's fall back on the former os_scan engine which has
+ * a larger os-fingerprint db.
+ */
+ if(o.osscan != OS_SCAN_SYS_2_ONLY)
+ doOsScan1(OSI);
+ }
+
+ delete HOS;
+ delete OSI;
+ return 0;
+}
diff --git a/osscan2.h b/osscan2.h
new file mode 100644
index 000000000..98f68ccad
--- /dev/null
+++ b/osscan2.h
@@ -0,0 +1,25 @@
+
+#ifndef OSSCAN2_H
+#define OSSCAN2_H
+
+#include "nmap.h"
+#include "tcpip.h"
+#include "global_structures.h"
+#include "FingerPrintResults.h"
+#include "osscan.h"
+
+/********************** PROTOTYPES ***********************************/
+
+int os_scan_2(std::vector &Targets);
+
+int send_closedudp_probe_2(struct udpprobeinfo &upi, int sd,
+ struct eth_nfo *eth, const struct in_addr *victim,
+ int ttl, u16 sport, u16 dport);
+int send_icmp_echo_probe(int sd, struct eth_nfo *eth, const struct in_addr *victim,
+ u8 tos, bool df, u8 pcode, unsigned short id, u16 seq, u16 datalen);
+
+int get_initial_ttl_guess(u8 ttl);
+int get_ipid_sequence(struct ipid_info *ipid, int islocalhost);
+
+#endif /*OSSCAN2_H*/
+