From c9cefab5fc3a191175600fae330f30fe3e99fb35 Mon Sep 17 00:00:00 2001 From: luis Date: Tue, 26 Jul 2011 12:03:18 +0000 Subject: [PATCH] Reorganized source file so methods of the same class are grouped together --- osscan2.cc | 2261 +++++++++++++++++++++++++++------------------------- osscan2.h | 1 - 2 files changed, 1176 insertions(+), 1086 deletions(-) diff --git a/osscan2.cc b/osscan2.cc index 7f86bd03d..825d84666 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -152,7 +152,851 @@ static struct timeval now; os_scan_performance_vars_t perf; +/****************************************************************************** + * Miscellaneous functions * + ******************************************************************************/ +/* Fill in a struct AVal with a value based on the IP ID sequence generation + class (one of the IPID_SEQ_* constants). If ipid_seqclass is such that the + test result should be omitted, the function returns NULL and doesn't modify + *av. Otherwise, it returns av after filling in the information. */ +static struct AVal *make_aval_ipid_seq(struct AVal *av, const char *attribute, + int ipid_seqclass, int ipids[NUM_SEQ_SAMPLES]) { + switch(ipid_seqclass) { + case IPID_SEQ_CONSTANT: + av->value = string_pool_sprintf("%X", ipids[0]); + break; + case IPID_SEQ_INCR: + av->value = "I"; + break; + case IPID_SEQ_BROKEN_INCR: + av->value = "BI"; + break; + case IPID_SEQ_RPI: + av->value = "RI"; + break; + case IPID_SEQ_RD: + av->value = "RD"; + break; + case IPID_SEQ_ZERO: + av->value = "Z"; + break; + default: + /* Signal to omit test result. */ + return NULL; + break; + } + + av->attribute = string_pool_insert(attribute); + + return av; +} + + +int get_initial_ttl_guess(u8 ttl) { + if (ttl <= 32) + return 32; + else if (ttl <= 64) + return 64; + else if (ttl <= 128) + return 128; + else + return 255; +} + + +/* This function takes an array of "numSamples" IP IDs and analyzes + them to determine their sequenceability classification. It returns + one of the IPID_SEQ_* classifications defined in nmap.h . If the + function cannot determine the sequence, IPID_SEQ_UNKNOWN is returned. + This islocalhost argument is a boolean specifying whether these + numbers were generated by scanning localhost. NOTE: the "ipids" argument + may be modified if localhost is set to true. */ +int get_ipid_sequence(int numSamples, int *ipids, int islocalhost) { + u16 ipid_diffs[32]; + int i; + int allipideqz = 1; /* Flag that means "All IP.IDs returned during + sequencing are zero. This is unset if we + find a nonzero */ + int j,k; + + assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); + if (numSamples < 2) return IPID_SEQ_UNKNOWN; + + for(i = 1; i < numSamples; i++) { + if (ipids[i-1] != 0 || ipids[i] != 0) + allipideqz = 0; /* All IP.ID values do *NOT* equal zero */ + + if (ipids[i-1] <= ipids[i]) { + ipid_diffs[i-1] = ipids[i] - ipids[i-1]; + } else { + ipid_diffs[i-1] = (u16) (ipids[i] - ipids[i-1] + 65536); + } + + /* Random */ + if (numSamples > 2 && ipid_diffs[i-1] > 20000) + return IPID_SEQ_RD; + } + + /* ZERO */ + if (allipideqz) return IPID_SEQ_ZERO; + + if (islocalhost) { + int allgto = 1; /* ALL diffs greater than one */ + + for(i=0; i < numSamples - 1; i++) { + if (ipid_diffs[i] < 2) { + allgto = 0; break; + } + } + + if (allgto) { + for(i=0; i < numSamples - 1; i++) { + if (ipid_diffs[i] % 256 == 0) /* Stupid MS */ + ipid_diffs[i] -= 256; + else + ipid_diffs[i]--; /* Because on localhost the RST sent back use an IPID */ + } + } + } + + /* Constant */ + j = 1; /* j is a flag meaning "all differences seen are zero" */ + for(i=0; i < numSamples - 1; i++) { + if (ipid_diffs[i] != 0) { + j = 0; + break; + } + } + if (j) { + return IPID_SEQ_CONSTANT; + } + + /* Random Positive Increments */ + for(i=0; i < numSamples - 1; i++) { + if (ipid_diffs[i] > 1000 && + (ipid_diffs[i] % 256 != 0 || + (ipid_diffs[i] % 256 == 0 && ipid_diffs[i] >= 25600))) { + return IPID_SEQ_RPI; + } + } + + j = 1; /* j is a flag meaning "all differences seen are < 10" */ + k = 1; /* k is a flag meaning "all difference seen are multiples of + 256 and no greater than 5120" */ + for(i=0; i < numSamples - 1; i++) { + if (k && (ipid_diffs[i] > 5120 || ipid_diffs[i] % 256 != 0)) { + k = 0; + } + + if (j && ipid_diffs[i] > 9) { + j = 0; + } + } + + /* Broken Increment */ + if (k == 1) { + return IPID_SEQ_BROKEN_INCR; + } + + /* Incremental */ + if (j == 1) + return IPID_SEQ_INCR; + + return IPID_SEQ_UNKNOWN; +} + + +/* 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.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; +} + + +/* 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; + + if((HOS->pd=my_pcap_open_live(Targets[0]->deviceName(), 8192, (o.spoofsource)? 1 : 0, pcap_selectable_fd_valid()? 200 : 2))==NULL) + fatal("%s", PCAP_OPEN_ERRMSG); + + 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) log_write(LOG_PLAIN, "Packet capture filter (device %s): %s\n", Targets[0]->deviceFullName(), pcap_filter); + set_pcap_filter(Targets[0]->deviceFullName(), HOS->pd, pcap_filter); + + return; +} + + +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_storage ss; + unsigned int bytes; + struct timeval rcvdtime; + + struct timeval stime, tmptv; + bool timedout = false; + bool thisHostGood; + bool foundgood; + bool goodResponse; + int numProbesLeft = 0; + + memset(&stime, 0, sizeof(stime)); + memset(&tmptv, 0, sizeof(tmptv)); + + for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + hss = hsi->hss; + HOS->buildSeqProbeList(hss); + } + + do { + if(timeToSleep > 0) { + if(o.debugging > 1) { + log_write(LOG_PLAIN, "Sleep %dus for next sequence probe\n", timeToSleep); + } + usleep(timeToSleep); + } + + gettimeofday(&now, NULL); + expectReplies = 0; + unableToSend = 0; + + if(o.debugging > 2) { + for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + log_write(LOG_PLAIN, "Host %s. ProbesToSend %d: \tProbesActive %d\n", + hss->target->targetipstr(), hss->numProbesToSend(), + hss->numProbesActive()); + } + } + + /* Send a seq probe to each host. */ + while(unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) { + hsi = OSI->nextIncompleteHost(); + hss = hsi->hss; + gettimeofday(&now, NULL); + if (hss->numProbesToSend()>0 && HOS->hostSeqSendOK(hss, NULL)) { + HOS->sendNextProbe(hss); + expectReplies++; + unableToSend = 0; + } else { + unableToSend++; + } + } + + HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent; + + gettimeofday(&now, NULL); + + /* Count the pcap wait time. */ + if(!HOS->stats->sendOK()) { + TIMEVAL_MSEC_ADD(stime, now, 1000); + + for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + if (HOS->nextTimeout((*hostI)->hss, &tmptv)) { + if (TIMEVAL_SUBTRACT(tmptv, stime) < 0) + stime = tmptv; + } + } + } + else { + foundgood = false; + for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + thisHostGood = HOS->hostSeqSendOK((*hostI)->hss, &tmptv); + if (thisHostGood) { + stime = tmptv; + foundgood = true; + break; + } + + if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) { + stime = tmptv; + foundgood = true; + } + } + } + + do { + to_usec = TIMEVAL_SUBTRACT(stime, now); + if(to_usec < 2000) to_usec = 2000; + + if(o.debugging > 2) + log_write(LOG_PLAIN, "pcap wait time is %ld.\n", to_usec); + + ip = (struct ip*) readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); + + gettimeofday(&now, NULL); + + if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) { + timedout = true; + break; + } else if (!ip) { + continue; + } + + if (TIMEVAL_SUBTRACT(now, stime) > 200000) { + /* While packets are still being received, I'll be generous and give + an extra 1/5 sec. But we have to draw the line somewhere */ + timedout = true; + } + + if(bytes < (4 * ip->ip_hl) + 4U) + continue; + + memset(&ss, 0, sizeof(ss)); + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; + ss.ss_family = AF_INET; + hsi = OSI->findIncompleteHost(&ss); + if (!hsi) continue; /* Not from one of our targets. */ + setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); + + goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); + + if(goodResponse) + expectReplies--; + + } while(!timedout && expectReplies > 0); + + /* Remove any timeout hosts during the scan. */ + OSI->removeCompletedHosts(); + + numProbesLeft = 0; + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + HOS->updateActiveSeqProbes(hss); + numProbesLeft += hss->numProbesToSend(); + numProbesLeft += hss->numProbesActive(); + } + + gettimeofday(&now, NULL); + + if(expectReplies == 0) { + timeToSleep = TIMEVAL_SUBTRACT(stime, now); + } else { + timeToSleep = 0; + } + + } while(numProbesLeft > 0); + +} + + +/* TCP, UDP, ICMP Tests */ +static void doTUITests(OsScanInfo *OSI, HostOsScan *HOS) { + 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_storage ss; + unsigned int bytes; + struct timeval rcvdtime; + + struct timeval stime, tmptv; + + bool timedout = false; + bool thisHostGood; + bool foundgood; + bool goodResponse; + int numProbesLeft = 0; + + memset(&stime, 0, sizeof(stime)); + memset(&tmptv, 0, sizeof(tmptv)); + + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + hss = hsi->hss; + HOS->buildTUIProbeList(hss); + } + + do { + + if(timeToSleep > 0) { + if(o.debugging > 1) { + log_write(LOG_PLAIN, "Time to sleep %d. Sleeping. \n", timeToSleep); + } + + usleep(timeToSleep); + } + + gettimeofday(&now, NULL); + expectReplies = 0; + unableToSend = 0; + + if(o.debugging > 2) { + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + log_write(LOG_PLAIN, "Host %s. ProbesToSend %d: \tProbesActive %d\n", + hss->target->targetipstr(), hss->numProbesToSend(), + hss->numProbesActive()); + } + } + + while(unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) { + hsi = OSI->nextIncompleteHost(); + hss = hsi->hss; + gettimeofday(&now, NULL); + if (hss->numProbesToSend()>0 && HOS->hostSendOK(hss, NULL)) { + HOS->sendNextProbe(hss); + expectReplies++; + unableToSend = 0; + } else { + unableToSend++; + } + } + + HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent; + + gettimeofday(&now, NULL); + + /* Count the pcap wait time. */ + if(!HOS->stats->sendOK()) { + TIMEVAL_MSEC_ADD(stime, now, 1000); + + for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); + hostI++) { + if (HOS->nextTimeout((*hostI)->hss, &tmptv)) { + if (TIMEVAL_SUBTRACT(tmptv, stime) < 0) + stime = tmptv; + } + } + } + else { + foundgood = false; + for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { + thisHostGood = HOS->hostSendOK((*hostI)->hss, &tmptv); + if (thisHostGood) { + stime = tmptv; + foundgood = true; + break; + } + + if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) { + stime = tmptv; + foundgood = true; + } + } + } + + do { + to_usec = TIMEVAL_SUBTRACT(stime, now); + if(to_usec < 2000) to_usec = 2000; + + if(o.debugging > 2) + log_write(LOG_PLAIN, "pcap wait time is %ld.\n", to_usec); + + ip = (struct ip*) readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); + + gettimeofday(&now, NULL); + + if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) { + timedout = true; + break; + } else if (!ip) { + continue; + } + + if (TIMEVAL_SUBTRACT(now, stime) > 200000) { + /* While packets are still being received, I'll be generous and give + an extra 1/5 sec. But we have to draw the line somewhere */ + timedout = true; + } + + if(bytes < (4 * ip->ip_hl) + 4U) + continue; + + memset(&ss, 0, sizeof(ss)); + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; + ss.ss_family = AF_INET; + hsi = OSI->findIncompleteHost(&ss); + if (!hsi) continue; /* Not from one of our targets. */ + setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); + + goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); + + if(goodResponse) + expectReplies--; + + } while(!timedout && expectReplies > 0); + + /* Remove any timeout hosts during the scan. */ + OSI->removeCompletedHosts(); + + numProbesLeft = 0; + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hss = (*hostI)->hss; + HOS->updateActiveTUIProbes(hss); + numProbesLeft += hss->numProbesToSend(); + numProbesLeft += hss->numProbesActive(); + } + + gettimeofday(&now, NULL); + + if(expectReplies == 0) { + timeToSleep = TIMEVAL_SUBTRACT(stime, now); + } else { + timeToSleep = 0; + } + + } while (numProbesLeft > 0); +} + + +static void endRound(OsScanInfo *OSI, HostOsScan *HOS, int roundNum) { + list::iterator hostI; + HostOsScanInfo *hsi = NULL; + int distance = -1; + enum dist_calc_method distance_calculation_method = DIST_METHOD_NONE; + + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + distance = -1; + hsi = *hostI; + HOS->makeFP(hsi->hss); + + hsi->FPs[roundNum] = hsi->hss->getFP(); + hsi->target->FPR->FPs[roundNum] = hsi->FPs[roundNum]; + hsi->target->FPR->numFPs = roundNum + 1; + double tr = hsi->hss->timingRatio(); + hsi->target->FPR->maxTimingRatio = MAX(hsi->target->FPR->maxTimingRatio, tr); + match_fingerprint(hsi->FPs[roundNum], &hsi->FP_matches[roundNum], + o.reference_FPs, OSSCAN_GUESS_THRESHOLD); + + if (hsi->FP_matches[roundNum].overall_results == OSSCAN_SUCCESS && + hsi->FP_matches[roundNum].num_perfect_matches > 0) { + memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info)); + if (roundNum > 0) { + if(o.verbose) log_write(LOG_STDOUT, "WARNING: OS didn't match until try #%d\n", roundNum + 1); + } + match_fingerprint(hsi->target->FPR->FPs[roundNum], hsi->target->FPR, + o.reference_FPs, OSSCAN_GUESS_THRESHOLD); + hsi->isCompleted = true; + } + + if (islocalhost(hsi->target->TargetSockAddr())) { + /* scanning localhost */ + distance = 0; + distance_calculation_method = DIST_METHOD_LOCALHOST; + } else if (hsi->target->MACAddress()) { + /* on the same network segment */ + distance = 1; + distance_calculation_method = DIST_METHOD_DIRECT; + } else if (hsi->hss->distance!=-1) { + distance = hsi->hss->distance; + distance_calculation_method = DIST_METHOD_ICMP; + } + + hsi->target->distance = hsi->target->FPR->distance = distance; + hsi->target->distance_calculation_method = distance_calculation_method; + hsi->target->FPR->distance_guess = hsi->hss->distance_guess; + + } + + OSI->removeCompletedHosts(); +} + + +static void findBestFPs(OsScanInfo *OSI) { + list::iterator hostI; + HostOsScanInfo *hsi = NULL; + int i; + + double bestacc; + int bestaccidx; + + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info)); + + /* Now lets find the best match */ + bestacc = 0; + bestaccidx = 0; + for(i=0; i < hsi->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; + } + } + + // Now we redo the match, since target->FPR has various data (such as + // target->FPR->numFPs) which is not in FP_matches[bestaccidx]. This is + // kinda ugly. + match_fingerprint(hsi->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; + + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI++) { + hsi = *hostI; + FPR = hsi->target->FPR; + + log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT, + "No OS matches for %s by new os scan system.\n\nTCP/IP fingerprint:\n%s", + hsi->target->targetipstr(), + mergeFPs(FPR->FPs, FPR->numFPs, true, + hsi->target->v4hostip(), hsi->target->distance, + hsi->target->distance_calculation_method, + hsi->target->MACAddress(), + FPR->osscan_opentcpport, FPR->osscan_closedtcpport, + FPR->osscan_closedudpport, false)); + } +} + + +/* Goes through every unmatched host in OSI. If a host has completed + the maximum number of OS detection tries allowed for it without + matching, it is transferred to the passed in unMatchedHosts list. + Returns the number of hosts moved to unMatchedHosts. */ +static int expireUnmatchedHosts(OsScanInfo *OSI, + list *unMatchedHosts) { + list::iterator hostI, nextHost; + int hostsRemoved = 0; + HostOsScanInfo *HOS; + + gettimeofday(&now, NULL); + for(hostI = OSI->incompleteHosts.begin(); + hostI != OSI->incompleteHosts.end(); hostI = nextHost) { + HOS = *hostI; + nextHost = hostI; + nextHost++; + + int max_tries = o.maxOSTries(); /* The amt. if print is suitable for submission */ + if (HOS->target->FPR->OmitSubmissionFP()) + max_tries = min(max_tries, STANDARD_OS2_TRIES); + + if (HOS->target->FPR->numFPs >= max_tries) { + /* We've done all the OS2 tries we're going to do ... move this + to unMatchedHosts */ + HOS->target->stopTimeOutClock(&now); + OSI->incompleteHosts.erase(hostI); + /* We need to adjust nextI if necessary */ + OSI->resetHostIterator(); + hostsRemoved++; + unMatchedHosts->push_back(HOS); + } + } + return hostsRemoved; +} + + +/* You should call os_scan2 rather than this function, as that version handles + chunking so you don't do too many targets in parallel */ +static int os_scan_2(vector &Targets) { + OsScanInfo *OSI; + HostOsScan *HOS; + + // Hosts which haven't matched and have been removed from + // incompleteHosts because they have exceeded the number of + // retransmissions the host is allowed. + list unMatchedHosts; + 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->starttime = o.TimeSinceStart(); + + HOS = new HostOsScan(Targets[0]); + startTimeOutClocks(OSI); + + itry = 0; + + + + begin_sniffer(HOS, Targets); /* initial the pcap session handler in HOS */ + while(OSI->numIncompleteHosts() != 0) { + if (itry > 0) sleep(1); + if (itry == 3) usleep(1500000); /* Try waiting a little longer just in case it matters */ + if (o.verbose) { + char targetstr[128]; + bool plural = (OSI->numIncompleteHosts() != 1); + if (!plural) { + (*(OSI->incompleteHosts.begin()))->target->NameIP(targetstr, sizeof(targetstr)); + } else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) OSI->numIncompleteHosts()); + log_write(LOG_STDOUT, "%s OS detection (try #%d) against %s\n", (itry == 0)? "Initiating" : "Retrying", itry + 1, targetstr); + log_flush_all(); + } + startRound(OSI, HOS, itry); + doSeqTests(OSI, HOS); + doTUITests(OSI, HOS); + endRound(OSI, HOS, itry); + expireUnmatchedHosts(OSI, &unMatchedHosts); + itry++; + } + + /* Now move the unMatchedHosts array back to IncompleteHosts */ + if (!unMatchedHosts.empty()) + OSI->incompleteHosts.splice(OSI->incompleteHosts.begin(), unMatchedHosts); + + if (OSI->numIncompleteHosts()) { + /* For host that doesn't have a perfect match, we do the following + things. */ + + /* Find the most matching item in the db. */ + findBestFPs(OSI); + + /* Print the fp in debug mode. + Normally let output.cc to print the FP. */ + if(o.debugging > 1) + printFP(OSI); + } + + delete HOS; + delete OSI; + return 0; +} + + +/* This is the primary OS detection function. If many Targets are + passed in (the threshold is based on timing level), they are + processed as smaller groups to improve accuracy */ +void os_scan2(vector &Targets) { + unsigned int max_os_group_sz = 20; + double fudgeratio = 1.2; /* Allow a slightly larger final group rather than finish with a tiny one */ + vector tmpTargets; + unsigned int startidx = 0; + + if (o.timing_level == 4) + max_os_group_sz = (unsigned int) (max_os_group_sz * 1.5); + + if (o.timing_level > 4 || Targets.size() <= max_os_group_sz * fudgeratio) { + os_scan_2(Targets); + return; + } + + /* We need to split it up */ + while(startidx < Targets.size()) { + int diff = Targets.size() - startidx; + if (diff > max_os_group_sz * fudgeratio) { + diff = max_os_group_sz; + } + tmpTargets.assign(Targets.begin() + startidx, + Targets.begin() + startidx + diff); + os_scan_2(tmpTargets); + startidx += diff; + } + return; +} + + +/****************************************************************************** + * Implementation of class OFProbe * + ******************************************************************************/ OFProbe::OFProbe() { type = OFP_UNSET; @@ -163,6 +1007,7 @@ OFProbe::OFProbe() { memset(&prevSent, 0, sizeof(prevSent)); } + const char *OFProbe::typestr() { switch(type) { case OFP_UNSET: @@ -185,6 +1030,11 @@ const char *OFProbe::typestr() { } } + +/****************************************************************************** + * Implementation of class HostOsScanStats * + ******************************************************************************/ + HostOsScanStats::HostOsScanStats(Target * t) { int i; @@ -221,6 +1071,7 @@ HostOsScanStats::HostOsScanStats(Target * t) { distance_guess = -1; } + HostOsScanStats::~HostOsScanStats() { int i; @@ -248,6 +1099,7 @@ HostOsScanStats::~HostOsScanStats() { if (icmpEchoReply) free(icmpEchoReply); } + void HostOsScanStats::initScanStats() { Port *tport = NULL; Port port; @@ -351,6 +1203,7 @@ void HostOsScanStats::initScanStats() { memset(&upi, 0, sizeof(upi)); } + /* Fill in an eth_nfo struct with the appropriate source and destination MAC addresses and a given Ethernet handle. The return value is suitable to pass to send_ip_packet: if ethsd is NULL, returns NULL; otherwise returns eth. */ @@ -366,6 +1219,7 @@ struct eth_nfo *HostOsScanStats::fill_eth_nfo(struct eth_nfo *eth, eth_t *ethsd) return eth; } + /* Add a probe to the probe list. */ void HostOsScanStats::addNewProbe(OFProbeType type, int subid) { OFProbe *probe = new OFProbe(); @@ -374,6 +1228,7 @@ void HostOsScanStats::addNewProbe(OFProbeType type, int subid) { probesToSend.push_back(probe); } + /* Remove a probe from the probesActive. */ void HostOsScanStats::removeActiveProbe(list::iterator probeI) { OFProbe *probe = *probeI; @@ -381,6 +1236,7 @@ void HostOsScanStats::removeActiveProbe(list::iterator 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) { @@ -403,18 +1259,21 @@ list::iterator HostOsScanStats::getActiveProbe(OFProbeType type, int 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); } + /* Compute the ratio of amount of time taken between sending 1st TSEQ probe and 1st ICMP probe compared to the amount of time it should have taken. Ratios far from 1 can cause bogus results */ @@ -432,10 +1291,13 @@ double HostOsScanStats::timingRatio() { } +/****************************************************************************** + * Implementation of class HostOsScan * + ******************************************************************************/ + /* If there are pending probe timeouts, fills in when with the time of * the earliest one and returns true. Otherwise returns false and - * puts now in when. - */ + * puts now in when. */ bool HostOsScan::nextTimeout(HostOsScanStats *hss, struct timeval *when) { assert(hss); struct timeval probe_to, earliest_to; @@ -458,6 +1320,7 @@ bool HostOsScan::nextTimeout(HostOsScanStats *hss, struct timeval *when) { return (firstgood)? false : true; } + void HostOsScan::adjust_times(HostOsScanStats *hss, OFProbe *probe, struct timeval *rcvdtime) { assert(hss); assert(probe); @@ -508,29 +1371,6 @@ void HostOsScan::adjust_times(HostOsScanStats *hss, OFProbe *probe, struct timev } } -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; @@ -563,6 +1403,7 @@ HostOsScan::HostOsScan(Target *t) { stats = new ScanStats(); } + HostOsScan::~HostOsScan() { if (rawsd >= 0) { close(rawsd); rawsd = -1; } if (pd) { pcap_close(pd); pd = NULL; } @@ -573,6 +1414,7 @@ HostOsScan::~HostOsScan() { delete stats; } + void HostOsScan::reInitScanSystem() { tcpSeqBase = get_random_u32(); tcpAck = get_random_u32(); @@ -582,6 +1424,7 @@ void HostOsScan::reInitScanSystem() { udpttl = (time(NULL) % 14) + 51; } + /* Initiate seq probe list */ void HostOsScan::buildSeqProbeList(HostOsScanStats *hss) { assert(hss); @@ -593,9 +1436,9 @@ void HostOsScan::buildSeqProbeList(HostOsScanStats *hss) { hss->addNewProbe(OFP_TSEQ, i); } + /* Update the seq probes in the active probe list: - * o Remove the timedout seq probes. - */ + * o Remove the timedout seq probes. */ void HostOsScan::updateActiveSeqProbes(HostOsScanStats *hss) { assert(hss); list::iterator probeI, nxt; @@ -606,7 +1449,7 @@ void HostOsScan::updateActiveSeqProbes(HostOsScanStats *hss) { nxt = probeI; nxt++; probe = *probeI; - + /* Is the probe timedout? */ if (TIMEVAL_SUBTRACT(now, probe->sent) > (long) timeProbeTimeout(hss)) { hss->removeActiveProbe(probeI); @@ -616,6 +1459,7 @@ void HostOsScan::updateActiveSeqProbes(HostOsScanStats *hss) { } } + /* initiate the normal tcp/udp/icmp probe list */ void HostOsScan::buildTUIProbeList(HostOsScanStats *hss) { assert(hss); @@ -630,7 +1474,7 @@ void HostOsScan::buildTUIProbeList(HostOsScanStats *hss) { * * **** Should be done in a more elegant way. ***** */ - + /* ticmp */ if(!hss->FP_TIcmp) { for(i=0; i<2; i++) { @@ -642,7 +1486,7 @@ void HostOsScan::buildTUIProbeList(HostOsScanStats *hss) { if(!hss->FP_TUdp) { hss->addNewProbe(OFP_TUDP, 0); } - + if(hss->openTCPPort != -1) { /* tops/twin probes. We send the probe again if we didn't get a response by the corresponding seq probe. @@ -675,10 +1519,10 @@ void HostOsScan::buildTUIProbeList(HostOsScanStats *hss) { } } + /* 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; - */ + * 2) Move timedout probes to probeNeedToSend; */ void HostOsScan::updateActiveTUIProbes(HostOsScanStats *hss) { assert(hss); list::iterator probeI, nxt; @@ -689,7 +1533,7 @@ void HostOsScan::updateActiveTUIProbes(HostOsScanStats *hss) { nxt = probeI; nxt++; probe = *probeI; - + if(TIMEVAL_SUBTRACT(now, probe->sent) > (long) timeProbeTimeout(hss)) { if(probe->tryno >= 3) { /* The probe is expired. */ @@ -707,10 +1551,10 @@ void HostOsScan::updateActiveTUIProbes(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. - */ + * return true. */ bool HostOsScan::hostSendOK(HostOsScanStats *hss, struct timeval *when) { assert(hss); list::iterator probeI; @@ -748,14 +1592,14 @@ bool HostOsScan::hostSendOK(HostOsScanStats *hss, struct timeval *when) { 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 */ @@ -772,10 +1616,10 @@ bool HostOsScan::hostSendOK(HostOsScanStats *hss, struct timeval *when) { 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. - */ + * false; else, fill it with now and return true. */ bool HostOsScan::hostSeqSendOK(HostOsScanStats *hss, struct timeval *when) { assert(hss); list::iterator probeI; @@ -789,7 +1633,7 @@ bool HostOsScan::hostSeqSendOK(HostOsScanStats *hss, struct timeval *when) { } packTime = TIMEVAL_SUBTRACT(now, hss->lastProbeSent); - + /* * If the user insist a larger sendDelayMs, use it. But * the seq result may be inaccurate. @@ -799,7 +1643,7 @@ bool HostOsScan::hostSeqSendOK(HostOsScanStats *hss, struct timeval *when) { if (when) { TIMEVAL_ADD(*when, hss->lastProbeSent, maxWait); } return false; } - + if (hss->timing.cwnd >= hss->numProbesActive() + .5) { if (when) *when = now; return true; @@ -817,12 +1661,12 @@ bool HostOsScan::hostSeqSendOK(HostOsScanStats *hss, struct timeval *when) { 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 */ @@ -838,6 +1682,7 @@ bool HostOsScan::hostSeqSendOK(HostOsScanStats *hss, struct timeval *when) { return false; } + unsigned long HostOsScan::timeProbeTimeout(HostOsScanStats *hss) { assert(hss); if (hss->target->to.srtt > 0) { @@ -851,15 +1696,16 @@ unsigned long HostOsScan::timeProbeTimeout(HostOsScanStats *hss) { } } + void HostOsScan::sendNextProbe(HostOsScanStats *hss) { assert(hss); list::iterator probeI; OFProbe *probe = NULL; - + if(hss->probesToSend.empty()) return; - - probeI = hss->probesToSend.begin(); + + probeI = hss->probesToSend.begin(); probe = *probeI; switch(probe->type) { @@ -892,7 +1738,7 @@ void HostOsScan::sendNextProbe(HostOsScanStats *hss) { probe->prevSent = probe->sent; } probe->sent = now; - + hss->lastProbeSent = now; hss->num_probes_sent++; stats->num_probes_sent++; @@ -904,9 +1750,10 @@ void HostOsScan::sendNextProbe(HostOsScanStats *hss) { log_write(LOG_PLAIN, "Send probe (type: %s, subid: %d) to %s\n", probe->typestr(), probe->subid, hss->target->targetipstr()); } - + } + void HostOsScan::sendTSeqProbe(HostOsScanStats *hss, int probeNo) { assert(hss); assert(probeNo >= 0 && probeNo < NUM_SEQ_SAMPLES); @@ -919,15 +1766,16 @@ void HostOsScan::sendTSeqProbe(HostOsScanStats *hss, int probeNo) { 0, TH_SYN, prbWindowSz[probeNo], 0, prbOpts[probeNo].val, prbOpts[probeNo].len, NULL, 0); - hss->seq_send_times[probeNo] = now; + hss->seq_send_times[probeNo] = now; } + void HostOsScan::sendTOpsProbe(HostOsScanStats *hss, int probeNo) { assert(hss); assert(probeNo>=0 && probeNo< NUM_SEQ_SAMPLES); if(hss->openTCPPort == -1) return; - + send_tcp_probe(hss, o.ttl, false, NULL, 0, tcpPortBase + NUM_SEQ_SAMPLES + probeNo, hss->openTCPPort, tcpSeqBase, tcpAck, @@ -935,11 +1783,12 @@ void HostOsScan::sendTOpsProbe(HostOsScanStats *hss, int probeNo) { prbOpts[probeNo].val, prbOpts[probeNo].len, NULL, 0); } + void HostOsScan::sendTEcnProbe(HostOsScanStats *hss) { assert(hss); - + if(hss->openTCPPort == -1) return; - + send_tcp_probe(hss, o.ttl, false, NULL, 0, tcpPortBase + NUM_SEQ_SAMPLES + 6, hss->openTCPPort, tcpSeqBase, 0, @@ -947,10 +1796,11 @@ void HostOsScan::sendTEcnProbe(HostOsScanStats *hss) { prbOpts[6].val, prbOpts[6].len, NULL, 0); } + void HostOsScan::sendT1_7Probe(HostOsScanStats *hss, int probeNo) { assert(hss); assert(probeNo>=0&&probeNo<7); - + int port_base = tcpPortBase + NUM_SEQ_SAMPLES + 7; switch(probeNo) { @@ -1015,6 +1865,7 @@ void HostOsScan::sendT1_7Probe(HostOsScanStats *hss, int probeNo) { } } + void HostOsScan::sendTIcmpProbe(HostOsScanStats *hss, int probeNo) { assert(hss); assert(probeNo>=0&&probeNo<2); @@ -1028,13 +1879,15 @@ void HostOsScan::sendTIcmpProbe(HostOsScanStats *hss, int probeNo) { } } + void HostOsScan::sendTUdpProbe(HostOsScanStats *hss, int probeNo) { assert(hss); - + if(hss->closedUDPPort == -1) return; send_closedudp_probe(hss, udpttl, udpPortBase + probeNo, hss->closedUDPPort); } + bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int len, struct timeval *rcvdtime) { struct ip *ip2; struct tcp_hdr *tcp; @@ -1048,14 +1901,14 @@ bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int l return false; len -= 4 * ip->ip_hl; - + if (ip->ip_p == IPPROTO_TCP) { if(len < 20) return false; tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); if(len < (unsigned int)(4 * tcp->th_off)) return false; testno = ntohs(tcp->th_dport) - tcpPortBase; - - if (testno >= 0 && testno < NUM_SEQ_SAMPLES) { + + if (testno >= 0 && testno < NUM_SEQ_SAMPLES) { /* TSeq */ isPktUseful = processTSeqResp(hss, ip, testno); @@ -1077,28 +1930,28 @@ bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int l 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); @@ -1110,7 +1963,7 @@ bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int l } } else if (ip->ip_p == IPPROTO_ICMP) { - if(len < 8) return false; + if(len < 8) return false; icmp = ((struct icmp *)(((char *) ip) + 4 * ip->ip_hl)); /* Is it an icmp echo reply? */ @@ -1125,7 +1978,7 @@ bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int l if(isPktUseful && probeI != hss->probesActive.end() && !(*probeI)->retransmitted) { /* Retransmitted ipid is useless. */ hss->ipid.icmp_ipids[testno] = ntohs(ip->ip_id); /* printf("icmp ipid = %d\n", ntohs(ip->ip_id)); */ - } + } } } @@ -1162,19 +2015,20 @@ bool HostOsScan::processResp(HostOsScanStats *hss, struct ip *ip, unsigned int l return true; } - + return false; } + void HostOsScan::makeFP(HostOsScanStats *hss) { assert(hss); - + int i; struct AVal AV; std::vector::iterator it; - + int ttl; - + if(!hss->FP_TSeq) makeTSeqFP(hss); @@ -1183,14 +2037,14 @@ void HostOsScan::makeFP(HostOsScanStats *hss) { if(!hss->FP_TWin) makeTWinFP(hss); - + for(i=3; i < NUM_FPTESTS; i++) { if (!hss->FPtests[i] && ((i>=3 && i<=7 && hss->openTCPPort != -1) || (i>=8 && i<=10 && hss->target->FPR->osscan_closedtcpport != -1) || i>=11)) { /* We create a Resp (response) attribute with value of N (no) because - it is important here to note whether responses were or were not + it is important here to note whether responses were or were not received */ hss->FPtests[i] = new FingerTest; AV.attribute = "R"; @@ -1237,42 +2091,183 @@ void HostOsScan::makeFP(HostOsScanStats *hss) { } } -/* Fill in a struct AVal with a value based on the IP ID sequence generation - class (one of the IPID_SEQ_* constants). If ipid_seqclass is such that the - test result should be omitted, the function returns NULL and doesn't modify - *av. Otherwise, it returns av after filling in the information. */ -static struct AVal *make_aval_ipid_seq(struct AVal *av, const char *attribute, - int ipid_seqclass, int ipids[NUM_SEQ_SAMPLES]) { - switch(ipid_seqclass) { - case IPID_SEQ_CONSTANT: - av->value = string_pool_sprintf("%X", ipids[0]); - break; - case IPID_SEQ_INCR: - av->value = "I"; - break; - case IPID_SEQ_BROKEN_INCR: - av->value = "BI"; - break; - case IPID_SEQ_RPI: - av->value = "RI"; - break; - case IPID_SEQ_RD: - av->value = "RD"; - break; - case IPID_SEQ_ZERO: - av->value = "Z"; - break; - default: - /* Signal to omit test result. */ - return NULL; - break; + +/* Send a TCP probe. This takes care of decoys and filling in Ethernet + * addresses if necessary. Used for the SEQ, OPS, WIN, ECN, and T1-T7 probes. */ +int HostOsScan::send_tcp_probe(HostOsScanStats *hss, + int ttl, bool df, u8* ipopt, int ipoptlen, + u16 sport, u16 dport, u32 seq, u32 ack, + u8 reserved, u8 flags, u16 window, u16 urp, + u8 *options, int optlen, + char *data, u16 datalen) { + struct eth_nfo eth, *ethptr; + + ethptr = hss->fill_eth_nfo(ð, ethsd); + + return send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), + ttl, df, ipopt, ipoptlen, sport, dport, seq, ack, + reserved, flags, window, urp, + options, optlen, data, datalen); +} + + +/* Send an echo probe. This takes care of decoys and filling in Ethernet + * addresses if necessary. Used for the IE probes. */ +int HostOsScan::send_icmp_echo_probe(HostOsScanStats *hss, + u8 tos, bool df, u8 pcode, + unsigned short id, u16 seq, u16 datalen) { + u8 *packet = NULL; + u32 packetlen = 0; + int decoy; + int res = -1; + struct eth_nfo eth, *ethptr; + + ethptr = hss->fill_eth_nfo(ð, ethsd); + + for(decoy = 0; decoy < o.numdecoys; decoy++) { + packet = build_icmp_raw(&o.decoys[decoy], hss->target->v4hostip(), + o.ttl, get_random_u16(), tos, df, + NULL, 0, + seq, id, ICMP_ECHO, pcode, + NULL, datalen, &packetlen); + if(!packet) return -1; + res = send_ip_packet(rawsd, ethptr, packet, packetlen); + free(packet); + if(res==-1) return -1; } - av->attribute = string_pool_insert(attribute); - - return av; + return 0; } + +/* Send a UDP probe. This takes care of decoys and filling in Ethernet + * addresses if necessary. Used for the U1 probe. */ +int HostOsScan::send_closedudp_probe(HostOsScanStats *hss, + int ttl, u16 sport, u16 dport) { + static int myttl = 0; + static u8 patternbyte = 0x43; /* character 'C' */ + static u16 id = 0x1042; + u8 packet[328]; /* 20 IP hdr + 8 UDP hdr + 300 data */ + struct ip *ip = (struct ip *) packet; + struct udp_hdr *udp = (struct udp_hdr *) (packet + sizeof(struct ip)); + struct in_addr *source; + int datalen = 300; + unsigned char *data = packet + 28; + unsigned short realcheck; /* the REAL checksum */ + int res; + int decoy; + struct eth_nfo eth, *ethptr; + + ethptr = hss->fill_eth_nfo(ð, ethsd); + + /* if (!patternbyte) patternbyte = (get_random_uint() % 60) + 65; */ + memset(data, patternbyte, datalen); + + /* while(!id) id = get_random_uint(); */ + + if (ttl == -1) { + myttl = (time(NULL) % 14) + 51; + } else { + myttl = ttl; + } + + /* check that required fields are there and not too silly */ + if (!sport || !dport) { + error("%s: One or more of your parameters suck!", __func__); + return 1; + } + + for(decoy=0; decoy < o.numdecoys; decoy++) { + source = &o.decoys[decoy]; + + memset((char *) packet, 0, sizeof(struct ip) + sizeof(struct udp_hdr)); + + udp->uh_sport = htons(sport); + udp->uh_dport = htons(dport); + udp->uh_ulen = htons(8 + datalen); + + /* OK, now we should be able to compute a valid checksum */ + realcheck = ipv4_pseudoheader_cksum(source, hss->target->v4hostip(), IPPROTO_UDP, + sizeof(struct udp_hdr) + datalen, (char *) udp); +#if STUPID_SOLARIS_CHECKSUM_BUG + udp->uh_sum = sizeof(struct udp_hdr) + datalen; +#else + udp->uh_sum = realcheck; +#endif + + /* Now for the ip header */ + ip->ip_v = 4; + ip->ip_hl = 5; + ip->ip_len = htons(sizeof(struct ip) + sizeof(struct udp_hdr) + datalen); + ip->ip_id = htons(id); + ip->ip_ttl = myttl; + ip->ip_p = IPPROTO_UDP; + ip->ip_src.s_addr = source->s_addr; + ip->ip_dst.s_addr= hss->target->v4hostip()->s_addr; + + hss->upi.ipck = in_cksum((unsigned short *)ip, sizeof(struct ip)); +#if HAVE_IP_IP_SUM + ip->ip_sum = hss->upi.ipck; +#endif + + /* OK, now if this is the real she-bang (ie not a decoy) then + we stick all the inph0 in our upi */ + if (decoy == o.decoyturn) { + hss->upi.iptl = 28 + datalen; + hss->upi.ipid = id; + hss->upi.sport = sport; + hss->upi.dport = dport; + hss->upi.udpck = realcheck; + hss->upi.udplen = 8 + datalen; + hss->upi.patternbyte = patternbyte; + hss->upi.target.s_addr = ip->ip_dst.s_addr; + } + + if ((res = send_ip_packet(rawsd, ethptr, packet, ntohs(ip->ip_len))) == -1) + { + gh_perror("send_ip_packet in %s", __func__); + return 1; + } + } + + return 0; +} + + +/****************************************************************************** + * Implementation of class ScanStats * + ******************************************************************************/ + +ScanStats::ScanStats() { + /* init timing val */ + timing.cwnd = perf.group_initial_cwnd; + timing.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; +} + + +/****************************************************************************** + * Implementation of class HostOsScan * + ******************************************************************************/ + void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { int i,j; u32 seq_diffs[NUM_SEQ_SAMPLES]; @@ -1318,7 +2313,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { j++; } /* Otherwise nothing good in this slot to copy */ } - + hss->si.responses = j; /* Just for assurance */ /* Time to look at the TCP ISN predictability */ @@ -1352,7 +2347,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { if (seq_gcd > 9) div_gcd = seq_gcd; - for(i=0; i < hss->si.responses - 1; i++) { + for(i=0; i < hss->si.responses - 1; i++) { double rtmp = seq_rates[i] / div_gcd - seq_avg_rate / div_gcd; seq_stddev += rtmp * rtmp; } @@ -1382,7 +2377,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { AV.attribute = "GCD"; AV.value = string_pool_sprintf("%X", seq_gcd); seq_AVs.push_back(AV); - AV.attribute = "ISR"; + AV.attribute = "ISR"; AV.value = string_pool_sprintf("%X", (unsigned int) seq_rate); seq_AVs.push_back(AV); } else if (hss->si.responses > 0) { @@ -1394,7 +2389,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { good_tcp_ipid_num = 0; good_tcp_closed_ipid_num = 0; good_icmp_ipid_num = 0; - + for(i=0; i < NUM_SEQ_SAMPLES; i++) { if (hss->ipid.tcp_ipids[i] != -1) { if (good_tcp_ipid_num < i) { @@ -1402,14 +2397,14 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } good_tcp_ipid_num++; } - + if (hss->ipid.tcp_closed_ipids[i] != -1) { if (good_tcp_closed_ipid_num < i) { hss->ipid.tcp_closed_ipids[good_tcp_closed_ipid_num] = hss->ipid.tcp_closed_ipids[i]; } good_tcp_closed_ipid_num++; } - + if (hss->ipid.icmp_ipids[i] != -1) { if (good_icmp_ipid_num < i) { hss->ipid.icmp_ipids[good_icmp_ipid_num] = hss->ipid.icmp_ipids[i]; @@ -1417,7 +2412,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { good_icmp_ipid_num++; } } - + if (good_tcp_ipid_num >= 3) { tcp_ipid_seqclass = get_ipid_sequence(good_tcp_ipid_num, hss->ipid.tcp_ipids, islocalhost(hss->target->TargetSockAddr())); } else { @@ -1425,13 +2420,13 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } /* Only print open tcp ipid seqclass in the final report. */ hss->si.ipid_seqclass = tcp_ipid_seqclass; - + if (good_tcp_closed_ipid_num >= 2) { tcp_closed_ipid_seqclass = get_ipid_sequence(good_tcp_closed_ipid_num, hss->ipid.tcp_closed_ipids, islocalhost(hss->target->TargetSockAddr())); } else { tcp_closed_ipid_seqclass = IPID_SEQ_UNKNOWN; } - + if (good_icmp_ipid_num >= 2) { icmp_ipid_seqclass = get_ipid_sequence(good_icmp_ipid_num, hss->ipid.icmp_ipids, islocalhost(hss->target->TargetSockAddr())); } else { @@ -1467,7 +2462,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { /* Now we look at TCP Timestamp sequence prediction */ /* Battle plan: - 1) Compute average increments per second, and variance in incr. per second + 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 @@ -1486,8 +2481,8 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { if (avg_ts_hz > 0 && avg_ts_hz < 5.66) { /* relatively wide range because sampling time so short and frequency so slow */ hss->si.ts_seqclass = TS_SEQ_2HZ; - lastboot = (double) hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 2); - hss->si.lastboot = hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 2); + lastboot = (double) hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 2); + hss->si.lastboot = hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 2); } else if (avg_ts_hz > 70 && avg_ts_hz < 150) { hss->si.ts_seqclass = TS_SEQ_100HZ; @@ -1495,17 +2490,17 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } else if (avg_ts_hz > 724 && avg_ts_hz < 1448) { hss->si.ts_seqclass = TS_SEQ_1000HZ; - lastboot = (double) hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / 1000); + lastboot = (double) 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; lastboot = (double) hss->seq_send_times[0].tv_sec - (hss->si.timestamps[0] / (unsigned int)(0.5 + avg_ts_hz)); } - + if (lastboot != 0.0 && (hss->seq_send_times[0].tv_sec - lastboot > 63072000)) { /* Up 2 years? Perhaps, but they're probably lying. */ if (o.debugging) { - log_write(LOG_STDOUT, "Ignoring claimed %s uptime of %lu days", + log_write(LOG_STDOUT, "Ignoring claimed %s uptime of %lu days", hss->target->targetipstr(), (hss->seq_send_times[0].tv_sec - hss->si.lastboot) / 86400); } @@ -1537,7 +2532,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { match slow frequencies exactly. */ tsnewval = 1; } else if (avg_ts_hz > 70 && avg_ts_hz <= 150) { - /* mathematically 7 would be 90.51 - 181, but we change to 70-150 to + /* mathematically 7 would be 90.51 - 181, but we change to 70-150 to better align with common freq 100 */ tsnewval = 7; } else if (avg_ts_hz > 150 && avg_ts_hz <= 350) { @@ -1567,6 +2562,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } } + void HostOsScan::makeTOpsFP(HostOsScanStats *hss) { assert(hss); std::vector AVs; @@ -1595,6 +2591,7 @@ void HostOsScan::makeTOpsFP(HostOsScanStats *hss) { hss->FP_TOps->name = "OPS"; } + void HostOsScan::makeTWinFP(HostOsScanStats *hss) { assert(hss); std::vector AVs; @@ -1623,6 +2620,7 @@ void HostOsScan::makeTWinFP(HostOsScanStats *hss) { hss->FP_TWin->name = "WIN"; } + bool HostOsScan::processTSeqResp(HostOsScanStats *hss, struct ip *ip, int replyNo) { assert(replyNo>=0 && replyNolastipid = ip->ip_id; - + tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); if ((tcp->th_flags & TH_RST)) { - if (hss->si.responses == 0) { + if (hss->si.responses == 0) { error("WARNING: RST from %s port %d -- is this port really open?", hss->target->targetipstr(), hss->openTCPPort); } @@ -1665,13 +2663,13 @@ bool HostOsScan::processTSeqResp(HostOsScanStats *hss, struct ip *ip, int replyN } 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 { @@ -1689,24 +2687,25 @@ bool HostOsScan::processTSeqResp(HostOsScanStats *hss, struct ip *ip, int replyN return false; } + bool HostOsScan::processTOpsResp(HostOsScanStats *hss, struct tcp_hdr *tcp, int replyNo) { assert(replyNo>=0 || replyNo<6); char ops_buf[256]; 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, ops_buf, sizeof(ops_buf)); if (!opsParseResult) { - if (o.debugging) + if (o.debugging) error("Option parse error for TOps response %d from %s.", replyNo, hss->target->targetipstr()); hss->TOps_AVs[replyNo]->value = ""; } hss->TOps_AVs[replyNo]->value = string_pool_insert(ops_buf); - + switch(replyNo) { case 0: hss->TOps_AVs[replyNo]->attribute = "O1"; @@ -1727,14 +2726,15 @@ bool HostOsScan::processTOpsResp(HostOsScanStats *hss, struct tcp_hdr *tcp, int hss->TOps_AVs[replyNo]->attribute = "O6"; break; } - + hss->TOpsReplyNum++; return true; } + bool HostOsScan::processTWinResp(HostOsScanStats *hss, struct tcp_hdr *tcp, int replyNo) { assert(replyNo>=0 || replyNo<6); - + if (hss->FP_TWin || hss->TWin_AVs[replyNo]) return false; hss->TWin_AVs[replyNo] = (struct AVal *) safe_zalloc(sizeof(struct AVal)); @@ -1760,11 +2760,12 @@ bool HostOsScan::processTWinResp(HostOsScanStats *hss, struct tcp_hdr *tcp, int hss->TWin_AVs[replyNo]->attribute = "W6"; break; } - + hss->TWinReplyNum++; return true; } + bool HostOsScan::processTEcnResp(HostOsScanStats *hss, struct ip *ip) { std::vector AVs; struct AVal AV; @@ -1776,7 +2777,7 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, struct ip *ip) { bool opsParseResult; if (hss->FP_TEcn) return false; - + /* Create the Avals */ AVs.reserve(numtests); @@ -1807,7 +2808,7 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, struct ip *ip) { opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); if (!opsParseResult) { - if (o.debugging) + if (o.debugging) error("Option parse error for ECN response from %s.", hss->target->targetipstr()); AV.value = ""; } @@ -1854,14 +2855,15 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, struct ip *ip) { return true; } + bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyNo) { std::vector AVs; struct AVal AV; assert(replyNo>=0 && replyNo<7); - + int numtests; struct tcp_hdr *tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); - + int i; bool opsParseResult; int length; @@ -1873,7 +2875,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyN if(replyNo == 0) numtests = 8; /* T1 doesn't has 'Win','Ops' tests. */ else numtests = 10; - + /* Create the Avals */ AVs.reserve(numtests); @@ -1919,7 +2921,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyN else AV.value = "O"; AVs.push_back(AV); - + /* ACK test values: Z = zero S = same as syn @@ -1929,7 +2931,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyN AV.attribute = "A"; if (ntohl(tcp->th_ack) == 0) AV.value = "Z"; - else if (ntohl(tcp->th_ack) == tcpSeqBase) + else if (ntohl(tcp->th_ack) == tcpSeqBase) AV.value = "S"; else if (ntohl(tcp->th_ack) == tcpSeqBase + 1) AV.value = "S+"; @@ -1976,7 +2978,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyN AV.attribute = "O"; opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); if (!opsParseResult) { - if (o.debugging) + if (o.debugging) error("Option parse error for T%d response from %s.", replyNo, hss->target->targetipstr()); AV.value = ""; } @@ -1984,7 +2986,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyN AV.value = string_pool_insert(ops_buf); AVs.push_back(AV); } - + /* Rst Data CRC32 */ AV.attribute = "RD"; length = (int) ntohs(ip->ip_len) - 4 * ip->ip_hl -4 * tcp->th_off; @@ -2015,10 +3017,11 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, struct ip *ip, int replyN hss->FPtests[FP_T1_7_OFF+replyNo] = new FingerTest; 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) { std::vector AVs; struct AVal AV; @@ -2043,12 +3046,12 @@ bool HostOsScan::processTUdpResp(HostOsScanStats *hss, struct ip *ip) { #endif if (hss->FP_TUdp) return false; - + icmp = ((struct icmp *)(((char *) ip) + 4 * ip->ip_hl)); - /* Make sure this is icmp port unreachable. */ + /* Make sure this is icmp port unreachable. */ assert(icmp->icmp_type == 3 && icmp->icmp_code == 3); - + ip2 = (struct ip*)((char *)icmp + 8); udp = (struct udp_hdr *)((char *)ip2 + 4 * ip2->ip_hl); @@ -2156,22 +3159,23 @@ bool HostOsScan::processTUdpResp(HostOsScanStats *hss, struct ip *ip) { AV.attribute = "RUD"; if (datastart < dataend) AV.value = "I"; /* They fucked it up */ - else + else AV.value = "G"; AVs.push_back(AV); - + hss->FP_TUdp = new FingerTest; - hss->FP_TUdp->name = "U1"; + 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); @@ -2183,7 +3187,7 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, struct ip *ip, int reply unsigned short value1, value2; if (hss->FP_TIcmp) return false; - + if (hss->icmpEchoReply == NULL) { /* This is the first icmp reply we get, store it and return. */ hss->icmpEchoReply = (struct ip *) safe_malloc(ntohs(ip->ip_len)); @@ -2240,7 +3244,7 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, struct ip *ip, int reply AVs.push_back(AV); /* TTL */ - + AV.attribute = "T"; AV.value = string_pool_sprintf("%d", ip1->ip_ttl); AVs.push_back(AV); @@ -2262,17 +3266,18 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, struct ip *ip, int reply else if (value1 == 9 && value2 == 0) /* both the same as in the corresponding probe */ AV.value = "S"; - else + else AV.value = "O"; AVs.push_back(AV); hss->FP_TIcmp= new FingerTest; hss->FP_TIcmp->name = "IE"; hss->FP_TIcmp->results = AVs; - + return true; } + bool HostOsScan::get_tcpopt_string(struct tcp_hdr *tcp, int mss, char *result, int maxlen) { char *p,*q; u16 tmpshort; @@ -2356,11 +3361,16 @@ bool HostOsScan::get_tcpopt_string(struct tcp_hdr *tcp, int mss, char *result, i *result = '\0'; return false; } - + *p = '\0'; return true; } + +/****************************************************************************** + * Implementation of class HostOsScanInfo * + ******************************************************************************/ + HostOsScanInfo::HostOsScanInfo(Target *t, OsScanInfo *OsSI) { target = t; OSI = OsSI; @@ -2373,25 +3383,31 @@ HostOsScanInfo::HostOsScanInfo(Target *t, OsScanInfo *OsSI) { if (target->FPR == NULL) target->FPR = new FingerPrintResults; target->osscanSetFlag(OS_PERF); - + hss = new HostOsScanStats(t); } + HostOsScanInfo::~HostOsScanInfo() { delete hss; free(FPs); free(FP_matches); } + +/****************************************************************************** + * Implementation of class OsScanInfo * + ******************************************************************************/ + OsScanInfo::OsScanInfo(vector &Targets) { unsigned int targetno; HostOsScanInfo *hsi; int num_timedout = 0; gettimeofday(&now, NULL); - + numInitialTargets=0; - + /* build up incompleteHosts list */ for(targetno = 0; targetno < Targets.size(); targetno++) { /* check if Targets[targetno] is good to be scanned @@ -2416,7 +3432,7 @@ OsScanInfo::OsScanInfo(vector &Targets) { if (o.verbose) log_write(LOG_PLAIN, "Skipping OS Scan against %s due to absence of open (or perhaps closed) ports\n", Targets[targetno]->NameIP()); continue; - } else { + } else { Targets[targetno]->osscanSetFlag(OS_PERF_UNREL); } } @@ -2429,6 +3445,7 @@ OsScanInfo::OsScanInfo(vector &Targets) { nextI = incompleteHosts.begin(); } + OsScanInfo::~OsScanInfo() { while(!incompleteHosts.empty()) { @@ -2437,6 +3454,7 @@ OsScanInfo::~OsScanInfo() } } + /* Find a HostScanStats by IP its address in the incomplete list. Returns NULL if none are found. */ HostOsScanInfo *OsScanInfo::findIncompleteHost(struct sockaddr_storage *ss) { @@ -2453,6 +3471,7 @@ HostOsScanInfo *OsScanInfo::findIncompleteHost(struct sockaddr_storage *ss) { 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 @@ -2471,6 +3490,7 @@ HostOsScanInfo *OsScanInfo::nextIncompleteHost() { return nxt; } + /* Removes any hosts that have completed their scans from the incompleteHosts list. Returns the number of hosts removed. */ int OsScanInfo::removeCompletedHosts() { @@ -2497,7 +3517,7 @@ int OsScanInfo::removeCompletedHosts() { 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(), + hsi->target->targetipstr(), o.TimeSinceStart() - this->starttime, remain, (remain == 1)? "host left" : "hosts left"); else if (timedout) @@ -2513,932 +3533,3 @@ int OsScanInfo::removeCompletedHosts() { } return hostsRemoved; } - -/* Send a TCP probe. This takes care of decoys and filling in Ethernet - addresses if necessary. Used for the SEQ, OPS, WIN, ECN, and T1-T7 probes. */ -int HostOsScan::send_tcp_probe(HostOsScanStats *hss, - int ttl, bool df, u8* ipopt, int ipoptlen, - u16 sport, u16 dport, u32 seq, u32 ack, - u8 reserved, u8 flags, u16 window, u16 urp, - u8 *options, int optlen, - char *data, u16 datalen) { - struct eth_nfo eth, *ethptr; - - ethptr = hss->fill_eth_nfo(ð, ethsd); - - return send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), - ttl, df, ipopt, ipoptlen, sport, dport, seq, ack, - reserved, flags, window, urp, - options, optlen, data, datalen); -} - -/* Send an echo probe. This takes care of decoys and filling in Ethernet - addresses if necessary. Used for the IE probes. */ -int HostOsScan::send_icmp_echo_probe(HostOsScanStats *hss, - u8 tos, bool df, u8 pcode, - unsigned short id, u16 seq, u16 datalen) { - u8 *packet = NULL; - u32 packetlen = 0; - int decoy; - int res = -1; - struct eth_nfo eth, *ethptr; - - ethptr = hss->fill_eth_nfo(ð, ethsd); - - for(decoy = 0; decoy < o.numdecoys; decoy++) { - packet = build_icmp_raw(&o.decoys[decoy], hss->target->v4hostip(), - o.ttl, get_random_u16(), tos, df, - NULL, 0, - seq, id, ICMP_ECHO, pcode, - NULL, datalen, &packetlen); - if(!packet) return -1; - res = send_ip_packet(rawsd, ethptr, packet, packetlen); - free(packet); - if(res==-1) return -1; - } - - return 0; -} - -/* Send a UDP probe. This takes care of decoys and filling in Ethernet - addresses if necessary. Used for the U1 probe. */ -int HostOsScan::send_closedudp_probe(HostOsScanStats *hss, - int ttl, u16 sport, u16 dport) { - static int myttl = 0; - static u8 patternbyte = 0x43; /* character 'C' */ - static u16 id = 0x1042; - u8 packet[328]; /* 20 IP hdr + 8 UDP hdr + 300 data */ - struct ip *ip = (struct ip *) packet; - struct udp_hdr *udp = (struct udp_hdr *) (packet + sizeof(struct ip)); - struct in_addr *source; - int datalen = 300; - unsigned char *data = packet + 28; - unsigned short realcheck; /* the REAL checksum */ - int res; - int decoy; - struct eth_nfo eth, *ethptr; - - ethptr = hss->fill_eth_nfo(ð, ethsd); - - /* if (!patternbyte) patternbyte = (get_random_uint() % 60) + 65; */ - memset(data, patternbyte, datalen); - - /* while(!id) id = get_random_uint(); */ - - if (ttl == -1) { - myttl = (time(NULL) % 14) + 51; - } else { - myttl = ttl; - } - - /* check that required fields are there and not too silly */ - if (!sport || !dport) { - error("%s: One or more of your parameters suck!", __func__); - return 1; - } - - for(decoy=0; decoy < o.numdecoys; decoy++) { - source = &o.decoys[decoy]; - - memset((char *) packet, 0, sizeof(struct ip) + sizeof(struct udp_hdr)); - - udp->uh_sport = htons(sport); - udp->uh_dport = htons(dport); - udp->uh_ulen = htons(8 + datalen); - - /* OK, now we should be able to compute a valid checksum */ - realcheck = ipv4_pseudoheader_cksum(source, hss->target->v4hostip(), IPPROTO_UDP, - sizeof(struct udp_hdr) + datalen, (char *) udp); -#if STUPID_SOLARIS_CHECKSUM_BUG - udp->uh_sum = sizeof(struct udp_hdr) + datalen; -#else - udp->uh_sum = realcheck; -#endif - - /* Now for the ip header */ - ip->ip_v = 4; - ip->ip_hl = 5; - ip->ip_len = htons(sizeof(struct ip) + sizeof(struct udp_hdr) + datalen); - ip->ip_id = htons(id); - ip->ip_ttl = myttl; - ip->ip_p = IPPROTO_UDP; - ip->ip_src.s_addr = source->s_addr; - ip->ip_dst.s_addr= hss->target->v4hostip()->s_addr; - - hss->upi.ipck = in_cksum((unsigned short *)ip, sizeof(struct ip)); -#if HAVE_IP_IP_SUM - ip->ip_sum = hss->upi.ipck; -#endif - - /* OK, now if this is the real she-bang (ie not a decoy) then - we stick all the inph0 in our upi */ - if (decoy == o.decoyturn) { - hss->upi.iptl = 28 + datalen; - hss->upi.ipid = id; - hss->upi.sport = sport; - hss->upi.dport = dport; - hss->upi.udpck = realcheck; - hss->upi.udplen = 8 + datalen; - hss->upi.patternbyte = patternbyte; - hss->upi.target.s_addr = ip->ip_dst.s_addr; - } - - if ((res = send_ip_packet(rawsd, ethptr, packet, ntohs(ip->ip_len))) == -1) - { - gh_perror("send_ip_packet in %s", __func__); - 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; -} - -/* This function takes an array of "numSamples" IP IDs and analyzes - them to determine their sequenceability classification. It returns - one of the IPID_SEQ_* classifications defined in nmap.h . If the - function cannot determine the sequence, IPID_SEQ_UNKNOWN is returned. - This islocalhost argument is a boolean specifying whether these - numbers were generated by scanning localhost. NOTE: the "ipids" argument - may be modified if localhost is set to true. */ -int get_ipid_sequence(int numSamples, int *ipids, int islocalhost) { - u16 ipid_diffs[32]; - int i; - int allipideqz = 1; /* Flag that means "All IP.IDs returned during - sequencing are zero. This is unset if we - find a nonzero */ - int j,k; - - assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); - if (numSamples < 2) return IPID_SEQ_UNKNOWN; - - for(i = 1; i < numSamples; i++) { - if (ipids[i-1] != 0 || ipids[i] != 0) - allipideqz = 0; /* All IP.ID values do *NOT* equal zero */ - - if (ipids[i-1] <= ipids[i]) { - ipid_diffs[i-1] = ipids[i] - ipids[i-1]; - } else { - ipid_diffs[i-1] = (u16) (ipids[i] - ipids[i-1] + 65536); - } - - /* Random */ - if (numSamples > 2 && ipid_diffs[i-1] > 20000) - return IPID_SEQ_RD; - } - - /* ZERO */ - if (allipideqz) return IPID_SEQ_ZERO; - - if (islocalhost) { - int allgto = 1; /* ALL diffs greater than one */ - - for(i=0; i < numSamples - 1; i++) { - if (ipid_diffs[i] < 2) { - allgto = 0; break; - } - } - - if (allgto) { - for(i=0; i < numSamples - 1; i++) { - if (ipid_diffs[i] % 256 == 0) /* Stupid MS */ - ipid_diffs[i] -= 256; - else - ipid_diffs[i]--; /* Because on localhost the RST sent back use an IPID */ - } - } - } - - /* Constant */ - j = 1; /* j is a flag meaning "all differences seen are zero" */ - for(i=0; i < numSamples - 1; i++) { - if (ipid_diffs[i] != 0) { - j = 0; - break; - } - } - if (j) { - return IPID_SEQ_CONSTANT; - } - - /* Random Positive Increments */ - for(i=0; i < numSamples - 1; i++) { - if (ipid_diffs[i] > 1000 && - (ipid_diffs[i] % 256 != 0 || - (ipid_diffs[i] % 256 == 0 && ipid_diffs[i] >= 25600))) { - return IPID_SEQ_RPI; - } - } - - j = 1; /* j is a flag meaning "all differences seen are < 10" */ - k = 1; /* k is a flag meaning "all difference seen are multiples of - 256 and no greater than 5120" */ - for(i=0; i < numSamples - 1; i++) { - if (k && (ipid_diffs[i] > 5120 || ipid_diffs[i] % 256 != 0)) { - k = 0; - } - - if (j && ipid_diffs[i] > 9) { - j = 0; - } - } - - /* Broken Increment */ - if (k == 1) { - return IPID_SEQ_BROKEN_INCR; - } - - /* Incremental */ - if (j == 1) - return IPID_SEQ_INCR; - - return IPID_SEQ_UNKNOWN; -} - -/* 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.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; -} - -/* 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; - - if((HOS->pd=my_pcap_open_live(Targets[0]->deviceName(), 8192, (o.spoofsource)? 1 : 0, pcap_selectable_fd_valid()? 200 : 2))==NULL) - fatal("%s", PCAP_OPEN_ERRMSG); - - 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) log_write(LOG_PLAIN, "Packet capture filter (device %s): %s\n", Targets[0]->deviceFullName(), pcap_filter); - set_pcap_filter(Targets[0]->deviceFullName(), HOS->pd, pcap_filter); - - return; -} - -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_storage ss; - unsigned int bytes; - struct timeval rcvdtime; - - struct timeval stime, tmptv; - bool timedout = false; - bool thisHostGood; - bool foundgood; - bool goodResponse; - int numProbesLeft = 0; - - memset(&stime, 0, sizeof(stime)); - memset(&tmptv, 0, sizeof(tmptv)); - - for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { - hsi = *hostI; - hss = hsi->hss; - HOS->buildSeqProbeList(hss); - } - - do { - if(timeToSleep > 0) { - if(o.debugging > 1) { - log_write(LOG_PLAIN, "Sleep %dus for next sequence probe\n", timeToSleep); - } - usleep(timeToSleep); - } - - gettimeofday(&now, NULL); - expectReplies = 0; - unableToSend = 0; - - if(o.debugging > 2) { - for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { - hss = (*hostI)->hss; - log_write(LOG_PLAIN, "Host %s. ProbesToSend %d: \tProbesActive %d\n", - hss->target->targetipstr(), hss->numProbesToSend(), - hss->numProbesActive()); - } - } - - /* Send a seq probe to each host. */ - while(unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) { - hsi = OSI->nextIncompleteHost(); - hss = hsi->hss; - gettimeofday(&now, NULL); - if (hss->numProbesToSend()>0 && HOS->hostSeqSendOK(hss, NULL)) { - HOS->sendNextProbe(hss); - expectReplies++; - unableToSend = 0; - } else { - unableToSend++; - } - } - - HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent; - - gettimeofday(&now, NULL); - - /* Count the pcap wait time. */ - if(!HOS->stats->sendOK()) { - TIMEVAL_MSEC_ADD(stime, now, 1000); - - for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { - if (HOS->nextTimeout((*hostI)->hss, &tmptv)) { - if (TIMEVAL_SUBTRACT(tmptv, stime) < 0) - stime = tmptv; - } - } - } - else { - foundgood = false; - for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { - thisHostGood = HOS->hostSeqSendOK((*hostI)->hss, &tmptv); - if (thisHostGood) { - stime = tmptv; - foundgood = true; - break; - } - - if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) { - stime = tmptv; - foundgood = true; - } - } - } - - do { - to_usec = TIMEVAL_SUBTRACT(stime, now); - if(to_usec < 2000) to_usec = 2000; - - if(o.debugging > 2) - log_write(LOG_PLAIN, "pcap wait time is %ld.\n", to_usec); - - ip = (struct ip*) readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); - - gettimeofday(&now, NULL); - - if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) { - timedout = true; - break; - } else if (!ip) { - continue; - } - - if (TIMEVAL_SUBTRACT(now, stime) > 200000) { - /* While packets are still being received, I'll be generous and give - an extra 1/5 sec. But we have to draw the line somewhere */ - timedout = true; - } - - if(bytes < (4 * ip->ip_hl) + 4U) - continue; - - memset(&ss, 0, sizeof(ss)); - ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; - ss.ss_family = AF_INET; - hsi = OSI->findIncompleteHost(&ss); - if (!hsi) continue; /* Not from one of our targets. */ - setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); - - goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); - - if(goodResponse) - expectReplies--; - - } while(!timedout && expectReplies > 0); - - /* Remove any timeout hosts during the scan. */ - OSI->removeCompletedHosts(); - - numProbesLeft = 0; - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - hss = (*hostI)->hss; - HOS->updateActiveSeqProbes(hss); - numProbesLeft += hss->numProbesToSend(); - numProbesLeft += hss->numProbesActive(); - } - - gettimeofday(&now, NULL); - - if(expectReplies == 0) { - timeToSleep = TIMEVAL_SUBTRACT(stime, now); - } else { - timeToSleep = 0; - } - - } while(numProbesLeft > 0); - -} - -/* TCP, UDP, ICMP Tests */ -static void doTUITests(OsScanInfo *OSI, HostOsScan *HOS) { - 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_storage ss; - unsigned int bytes; - struct timeval rcvdtime; - - struct timeval stime, tmptv; - - bool timedout = false; - bool thisHostGood; - bool foundgood; - bool goodResponse; - int numProbesLeft = 0; - - memset(&stime, 0, sizeof(stime)); - memset(&tmptv, 0, sizeof(tmptv)); - - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - hsi = *hostI; - hss = hsi->hss; - HOS->buildTUIProbeList(hss); - } - - do { - - if(timeToSleep > 0) { - if(o.debugging > 1) { - log_write(LOG_PLAIN, "Time to sleep %d. Sleeping. \n", timeToSleep); - } - - usleep(timeToSleep); - } - - gettimeofday(&now, NULL); - expectReplies = 0; - unableToSend = 0; - - if(o.debugging > 2) { - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - hss = (*hostI)->hss; - log_write(LOG_PLAIN, "Host %s. ProbesToSend %d: \tProbesActive %d\n", - hss->target->targetipstr(), hss->numProbesToSend(), - hss->numProbesActive()); - } - } - - while(unableToSend < OSI->numIncompleteHosts() && HOS->stats->sendOK()) { - hsi = OSI->nextIncompleteHost(); - hss = hsi->hss; - gettimeofday(&now, NULL); - if (hss->numProbesToSend()>0 && HOS->hostSendOK(hss, NULL)) { - HOS->sendNextProbe(hss); - expectReplies++; - unableToSend = 0; - } else { - unableToSend++; - } - } - - HOS->stats->num_probes_sent_at_last_wait = HOS->stats->num_probes_sent; - - gettimeofday(&now, NULL); - - /* Count the pcap wait time. */ - if(!HOS->stats->sendOK()) { - TIMEVAL_MSEC_ADD(stime, now, 1000); - - for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); - hostI++) { - if (HOS->nextTimeout((*hostI)->hss, &tmptv)) { - if (TIMEVAL_SUBTRACT(tmptv, stime) < 0) - stime = tmptv; - } - } - } - else { - foundgood = false; - for(hostI = OSI->incompleteHosts.begin(); hostI != OSI->incompleteHosts.end(); hostI++) { - thisHostGood = HOS->hostSendOK((*hostI)->hss, &tmptv); - if (thisHostGood) { - stime = tmptv; - foundgood = true; - break; - } - - if (!foundgood || TIMEVAL_SUBTRACT(tmptv, stime) < 0) { - stime = tmptv; - foundgood = true; - } - } - } - - do { - to_usec = TIMEVAL_SUBTRACT(stime, now); - if(to_usec < 2000) to_usec = 2000; - - if(o.debugging > 2) - log_write(LOG_PLAIN, "pcap wait time is %ld.\n", to_usec); - - ip = (struct ip*) readipv4_pcap(HOS->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true); - - gettimeofday(&now, NULL); - - if (!ip && TIMEVAL_SUBTRACT(stime, now) < 0) { - timedout = true; - break; - } else if (!ip) { - continue; - } - - if (TIMEVAL_SUBTRACT(now, stime) > 200000) { - /* While packets are still being received, I'll be generous and give - an extra 1/5 sec. But we have to draw the line somewhere */ - timedout = true; - } - - if(bytes < (4 * ip->ip_hl) + 4U) - continue; - - memset(&ss, 0, sizeof(ss)); - ((struct sockaddr_in *) &ss)->sin_addr.s_addr = ip->ip_src.s_addr; - ss.ss_family = AF_INET; - hsi = OSI->findIncompleteHost(&ss); - if (!hsi) continue; /* Not from one of our targets. */ - setTargetMACIfAvailable(hsi->target, &linkhdr, &ss, 0); - - goodResponse = HOS->processResp(hsi->hss, ip, bytes, &rcvdtime); - - if(goodResponse) - expectReplies--; - - } while(!timedout && expectReplies > 0); - - /* Remove any timeout hosts during the scan. */ - OSI->removeCompletedHosts(); - - numProbesLeft = 0; - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - hss = (*hostI)->hss; - HOS->updateActiveTUIProbes(hss); - numProbesLeft += hss->numProbesToSend(); - numProbesLeft += hss->numProbesActive(); - } - - gettimeofday(&now, NULL); - - if(expectReplies == 0) { - timeToSleep = TIMEVAL_SUBTRACT(stime, now); - } else { - timeToSleep = 0; - } - - } while (numProbesLeft > 0); -} - -static void endRound(OsScanInfo *OSI, HostOsScan *HOS, int roundNum) { - list::iterator hostI; - HostOsScanInfo *hsi = NULL; - int distance = -1; - enum dist_calc_method distance_calculation_method = DIST_METHOD_NONE; - - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - distance = -1; - hsi = *hostI; - HOS->makeFP(hsi->hss); - - hsi->FPs[roundNum] = hsi->hss->getFP(); - hsi->target->FPR->FPs[roundNum] = hsi->FPs[roundNum]; - hsi->target->FPR->numFPs = roundNum + 1; - double tr = hsi->hss->timingRatio(); - hsi->target->FPR->maxTimingRatio = MAX(hsi->target->FPR->maxTimingRatio, tr); - match_fingerprint(hsi->FPs[roundNum], &hsi->FP_matches[roundNum], - o.reference_FPs, OSSCAN_GUESS_THRESHOLD); - - if (hsi->FP_matches[roundNum].overall_results == OSSCAN_SUCCESS && - hsi->FP_matches[roundNum].num_perfect_matches > 0) { - memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info)); - if (roundNum > 0) { - if(o.verbose) log_write(LOG_STDOUT, "WARNING: OS didn't match until try #%d\n", roundNum + 1); - } - match_fingerprint(hsi->target->FPR->FPs[roundNum], hsi->target->FPR, - o.reference_FPs, OSSCAN_GUESS_THRESHOLD); - hsi->isCompleted = true; - } - - if (islocalhost(hsi->target->TargetSockAddr())) { - /* scanning localhost */ - distance = 0; - distance_calculation_method = DIST_METHOD_LOCALHOST; - } else if (hsi->target->MACAddress()) { - /* on the same network segment */ - distance = 1; - distance_calculation_method = DIST_METHOD_DIRECT; - } else if (hsi->hss->distance!=-1) { - distance = hsi->hss->distance; - distance_calculation_method = DIST_METHOD_ICMP; - } - - hsi->target->distance = hsi->target->FPR->distance = distance; - hsi->target->distance_calculation_method = distance_calculation_method; - hsi->target->FPR->distance_guess = hsi->hss->distance_guess; - - } - - OSI->removeCompletedHosts(); -} - -static void findBestFPs(OsScanInfo *OSI) { - list::iterator hostI; - HostOsScanInfo *hsi = NULL; - int i; - - double bestacc; - int bestaccidx; - - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - hsi = *hostI; - memcpy(&(hsi->target->seq), &hsi->hss->si, sizeof(struct seq_info)); - - /* Now lets find the best match */ - bestacc = 0; - bestaccidx = 0; - for(i=0; i < hsi->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; - } - } - - // Now we redo the match, since target->FPR has various data (such as - // target->FPR->numFPs) which is not in FP_matches[bestaccidx]. This is - // kinda ugly. - match_fingerprint(hsi->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; - - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI++) { - hsi = *hostI; - FPR = hsi->target->FPR; - - log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT, - "No OS matches for %s by new os scan system.\n\nTCP/IP fingerprint:\n%s", - hsi->target->targetipstr(), - mergeFPs(FPR->FPs, FPR->numFPs, true, - hsi->target->v4hostip(), hsi->target->distance, - hsi->target->distance_calculation_method, - hsi->target->MACAddress(), - FPR->osscan_opentcpport, FPR->osscan_closedtcpport, - FPR->osscan_closedudpport, false)); - } -} - -/* Goes through every unmatched host in OSI. If a host has completed - the maximum number of OS detection tries allowed for it without - matching, it is transferred to the passed in unMatchedHosts list. - Returns the number of hosts moved to unMatchedHosts. */ -static int expireUnmatchedHosts(OsScanInfo *OSI, - list *unMatchedHosts) { - list::iterator hostI, nextHost; - int hostsRemoved = 0; - HostOsScanInfo *HOS; - - gettimeofday(&now, NULL); - for(hostI = OSI->incompleteHosts.begin(); - hostI != OSI->incompleteHosts.end(); hostI = nextHost) { - HOS = *hostI; - nextHost = hostI; - nextHost++; - - int max_tries = o.maxOSTries(); /* The amt. if print is suitable for submission */ - if (HOS->target->FPR->OmitSubmissionFP()) - max_tries = min(max_tries, STANDARD_OS2_TRIES); - - if (HOS->target->FPR->numFPs >= max_tries) { - /* We've done all the OS2 tries we're going to do ... move this - to unMatchedHosts */ - HOS->target->stopTimeOutClock(&now); - OSI->incompleteHosts.erase(hostI); - /* We need to adjust nextI if necessary */ - OSI->resetHostIterator(); - hostsRemoved++; - unMatchedHosts->push_back(HOS); - } - } - return hostsRemoved; -} - - -/* You should call os_scan2 rather than this function, as that version handles - chunking so you don't do too many targets in parallel */ -static int os_scan_2(vector &Targets) { - OsScanInfo *OSI; - HostOsScan *HOS; - - // Hosts which haven't matched and have been removed from - // incompleteHosts because they have exceeded the number of - // retransmissions the host is allowed. - list unMatchedHosts; - 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->starttime = o.TimeSinceStart(); - - HOS = new HostOsScan(Targets[0]); - startTimeOutClocks(OSI); - - itry = 0; - - - - begin_sniffer(HOS, Targets); /* initial the pcap session handler in HOS */ - while(OSI->numIncompleteHosts() != 0) { - if (itry > 0) sleep(1); - if (itry == 3) usleep(1500000); /* Try waiting a little longer just in case it matters */ - if (o.verbose) { - char targetstr[128]; - bool plural = (OSI->numIncompleteHosts() != 1); - if (!plural) { - (*(OSI->incompleteHosts.begin()))->target->NameIP(targetstr, sizeof(targetstr)); - } else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) OSI->numIncompleteHosts()); - log_write(LOG_STDOUT, "%s OS detection (try #%d) against %s\n", (itry == 0)? "Initiating" : "Retrying", itry + 1, targetstr); - log_flush_all(); - } - startRound(OSI, HOS, itry); - doSeqTests(OSI, HOS); - doTUITests(OSI, HOS); - endRound(OSI, HOS, itry); - expireUnmatchedHosts(OSI, &unMatchedHosts); - itry++; - } - - /* Now move the unMatchedHosts array back to IncompleteHosts */ - if (!unMatchedHosts.empty()) - OSI->incompleteHosts.splice(OSI->incompleteHosts.begin(), unMatchedHosts); - - if (OSI->numIncompleteHosts()) { - /* For host that doesn't have a perfect match, we do the following - things. */ - - /* Find the most matching item in the db. */ - findBestFPs(OSI); - - /* Print the fp in debug mode. - Normally let output.cc to print the FP. */ - if(o.debugging > 1) - printFP(OSI); - } - - delete HOS; - delete OSI; - return 0; -} - -/* This is the primary OS detection function. If many Targets are - passed in (the threshold is based on timing level), they are - processed as smaller groups to improve accuracy */ -void os_scan2(vector &Targets) { - unsigned int max_os_group_sz = 20; - double fudgeratio = 1.2; /* Allow a slightly larger final group rather than finish with a tiny one */ - vector tmpTargets; - unsigned int startidx = 0; - - if (o.timing_level == 4) - max_os_group_sz = (unsigned int) (max_os_group_sz * 1.5); - - if (o.timing_level > 4 || Targets.size() <= max_os_group_sz * fudgeratio) { - os_scan_2(Targets); - return; - } - - /* We need to split it up */ - while(startidx < Targets.size()) { - int diff = Targets.size() - startidx; - if (diff > max_os_group_sz * fudgeratio) { - diff = max_os_group_sz; - } - tmpTargets.assign(Targets.begin() + startidx, - Targets.begin() + startidx + diff); - os_scan_2(tmpTargets); - startidx += diff; - } - return; -} - diff --git a/osscan2.h b/osscan2.h index c968f0850..865fb66b7 100644 --- a/osscan2.h +++ b/osscan2.h @@ -519,6 +519,5 @@ class HostOsScanInfo { HostOsScanStats *hss; /* Scan status of the host in one scan round */ }; - #endif /*OSSCAN2_H*/