From 91af49292a8e1977aef9359895efff51e16a866f Mon Sep 17 00:00:00 2001 From: fyodor Date: Mon, 26 Jun 2006 03:39:13 +0000 Subject: [PATCH] add osscan2.* files --- CHANGELOG | 3 + Makefile.in | 2 +- mswin32/nmap.vcproj | 66 +- nmap_winconfig.h | 2 +- osscan2.cc | 3500 +++++++++++++++++++++++++++++++++++++++++++ osscan2.h | 25 + 6 files changed, 3579 insertions(+), 19 deletions(-) create mode 100644 osscan2.cc create mode 100644 osscan2.h 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*/ +