diff --git a/osscan.cc b/osscan.cc index 97ce5d006..a3f505b77 100644 --- a/osscan.cc +++ b/osscan.cc @@ -78,6 +78,88 @@ extern NmapOps o; +template void ShortStr<_MaxStrLen>::setStr(const char *in) { + const char *end = in; + while (end - in < _MaxStrLen && *++end); + setStr(in, end); + trunc = trunc || *end; +} +template void ShortStr<_MaxStrLen>::setStr(const char *in, const char *end) { + assert(end > in && in != NULL); + int len = end - in; + len = MIN(len, _MaxStrLen); + + int i = 0; + for (; i < len; i++) { + char c = in[i]; + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')) { + str[i] = c; + } + else + break; + } + str[i] = '\0'; + trunc = i < (end - in); +} + +const char *FingerPrintDef::test_attrs[NUM_FPTESTS][FP_MAX_TEST_ATTRS] = { + /* SEQ */ {"SP", "GCD", "ISR", "TI", "CI", "II", "SS", "TS"}, + /* OPS */ {"O1", "O2", "O3", "O4", "O5", "O6"}, + /* WIN */ {"W1", "W2", "W3", "W4", "W5", "W6"}, + /* ECN */ {"R", "DF", "T", "TG", "W", "O", "CC", "Q"}, + /* T1 */ {"R", "DF", "T", "TG", "S", "A", "F", "RD", "Q"}, + /* T2 */ {"R", "DF", "T", "TG", "W", "S", "A", "F", "O", "RD", "Q"}, + /* T3 */ {"R", "DF", "T", "TG", "W", "S", "A", "F", "O", "RD", "Q"}, + /* T4 */ {"R", "DF", "T", "TG", "W", "S", "A", "F", "O", "RD", "Q"}, + /* T5 */ {"R", "DF", "T", "TG", "W", "S", "A", "F", "O", "RD", "Q"}, + /* T6 */ {"R", "DF", "T", "TG", "W", "S", "A", "F", "O", "RD", "Q"}, + /* T7 */ {"R", "DF", "T", "TG", "W", "S", "A", "F", "O", "RD", "Q"}, + /* U1 */ {"R", "DF", "T", "TG", "IPL", "UN", "RIPL", "RID", "RIPCK", "RUCK", "RUD"}, + /* IE */ {"R", "DFI", "T", "TG", "CD"} + }; + +FingerPrintDef::FingerPrintDef() { + TestDefs.reserve(NUM_FPTESTS); + int i = 0; + FPstr name; +#define ADD_TEST_DEF(_Name) \ + i = ID2INT(_Name); \ + name = FPstr(#_Name); \ + TestDefs.push_back(FingerTestDef(name, test_attrs[i])); \ + assert(TestDefs[i].name == name); \ + TestIdx.insert(std::make_pair(name, _Name)); + + ADD_TEST_DEF(SEQ); + ADD_TEST_DEF(OPS); + ADD_TEST_DEF(WIN); + ADD_TEST_DEF(ECN); + ADD_TEST_DEF(T1); + ADD_TEST_DEF(T2); + ADD_TEST_DEF(T3); + ADD_TEST_DEF(T4); + ADD_TEST_DEF(T5); + ADD_TEST_DEF(T6); + ADD_TEST_DEF(T7); + ADD_TEST_DEF(U1); + ADD_TEST_DEF(IE); + + assert(FingerPrintDef::INVALID == INT2ID(NUM_FPTESTS)); + assert(TestDefs.size() == NUM_FPTESTS); + assert(TestIdx.size() == NUM_FPTESTS); +}; + +FingerTestDef::FingerTestDef(const FPstr &n, const char *a[]) + : name(n), numAttrs(0) { + hasR = (0 == strcmp(a[0], "R")); + Attrs.reserve(FP_MAX_TEST_ATTRS); + while (numAttrs < FP_MAX_TEST_ATTRS && a[numAttrs] != NULL) { + Attr attr(a[numAttrs]); + Attrs.push_back(attr); + AttrIdx.insert(std::make_pair(attr.name, numAttrs)); + numAttrs++; + } +} + FingerPrintDB::FingerPrintDB() : MatchPoints(NULL) { } @@ -85,7 +167,6 @@ FingerPrintDB::~FingerPrintDB() { std::vector::iterator current; if (MatchPoints != NULL) { - MatchPoints->erase(); delete MatchPoints; } for (current = prints.begin(); current != prints.end(); current++) { @@ -94,9 +175,35 @@ FingerPrintDB::~FingerPrintDB() { } } -FingerTest::FingerTest(bool allocResults) : name(NULL), results(NULL) { - if (allocResults) - this->results = new std::vector; +bool FingerPrintDef::parseTestStr(const char *str, const char *end) { + const char *p = str; + const char *q = strchr_p(p, end, '('); + if (!q) + return false; + + std::map::iterator t_i = TestIdx.find(FPstr(p, q)); + if (t_i == TestIdx.end()) + return false; + + FingerTestDef &test = getTestDef(t_i->second); + p = q + 1; + while ((q = strchr_p(p, end, '='))) { + std::map::iterator a_i = test.AttrIdx.find(FPstr(p, q)); + if (a_i == test.AttrIdx.end()) + return false; + Attr &attr = test.Attrs[a_i->second]; + + p = q + 1; + errno = 0; + attr.points = strtol(p, NULL, 10); + if (errno != 0 || attr.points <= 0) + return false; + + if (NULL == (p = strchr_p(q, end, '%'))) + break; + p++; + } + return true; } void FingerTest::erase() { @@ -106,18 +213,9 @@ void FingerTest::erase() { } } -void FingerPrint::sort() { - unsigned int i; - - for (i = 0; i < tests.size(); i++) - std::stable_sort(tests[i].results->begin(), tests[i].results->end()); - std::stable_sort(tests.begin(), tests.end()); -} - void FingerPrint::erase() { - for (std::vector::iterator t = this->tests.begin(); - t != this->tests.end(); t++) { - t->erase(); + for (int i=0; i < NUM_FPTESTS; i++) { + tests[i].erase(); } } @@ -186,69 +284,45 @@ static bool expr_match(const char *val, const char *expr) { case, you may also pass in the group name (SEQ, T1, etc) to have that extra info printed. If you pass 0 for verbose, you might as well pass NULL for testGroupName as it won't be used. */ -static int AVal_match(const FingerTest *reference, const FingerTest *fprint, const FingerTest *points, +static int AVal_match(const FingerTest &reference, const FingerTest &fprint, const FingerTestDef &points, unsigned long *num_subtests, unsigned long *num_subtests_succeeded, int shortcut, int verbose) { - std::vector::const_iterator current_ref, prev_ref; - std::vector::const_iterator current_fp, prev_fp; - std::vector::const_iterator current_points; int subtests = 0, subtests_succeeded=0; - int pointsThisTest = 1; - char *endptr; + if (!reference.results || !fprint.results) + return 0; - /* We rely on AVals being sorted by attribute. */ - prev_ref = reference->results->end(); - prev_fp = fprint->results->end(); - current_ref = reference->results->begin(); - current_fp = fprint->results->begin(); - current_points = points->results->begin(); - while (current_ref != reference->results->end() - && current_fp != fprint->results->end()) { - int d; + const std::vector &pointsV = points.Attrs; - /* Check for sortedness. */ - if (prev_ref != reference->results->end()) - assert(*prev_ref < *current_ref); - if (prev_fp != fprint->results->end()) - assert(*prev_fp < *current_fp); + const std::vector &refV = *reference.results; + assert(refV.size() == points.numAttrs); - d = strcmp(current_ref->attribute, current_fp->attribute); - if (d == 0) { - for (; current_points != points->results->end(); current_points++) { - if (strcmp(current_ref->attribute, current_points->attribute) == 0) - break; + const std::vector &fpV = *fprint.results; + assert(refV.size() == points.numAttrs); + + for (size_t i = 0; i < points.numAttrs; i++) { + const char *current_ref = refV[i]; + const char *current_fp = fpV[i]; + const Attr &aDef = pointsV[i]; + if (current_ref == NULL || current_fp == NULL) + continue; + int pointsThisTest = aDef.points; + if (pointsThisTest < 0) + fatal("%s: Got bogus point amount (%d) for test %s.%s", __func__, pointsThisTest, points.name.str, aDef.name.str); + subtests += pointsThisTest; + + if (expr_match(current_fp, current_ref)) { + subtests_succeeded += pointsThisTest; + } else { + if (shortcut) { + if (num_subtests) + *num_subtests += subtests; + return 0; } - if (current_points == points->results->end()) - fatal("%s: Failed to find point amount for test %s.%s", __func__, reference->name ? reference->name : "", current_ref->attribute); - errno = 0; - pointsThisTest = strtol(current_points->value, &endptr, 10); - if (errno != 0 || *endptr != '\0' || pointsThisTest < 0) - fatal("%s: Got bogus point amount (%s) for test %s.%s", __func__, current_points->value, reference->name ? reference->name : "", current_ref->attribute); - subtests += pointsThisTest; - - if (expr_match(current_fp->value, current_ref->value)) { - subtests_succeeded += pointsThisTest; - } else { - if (shortcut) { - if (num_subtests) - *num_subtests += subtests; - return 0; - } - if (verbose) - log_write(LOG_PLAIN, "%s.%s: \"%s\" NOMATCH \"%s\" (%d %s)\n", reference->name, - current_ref->attribute, current_fp->value, - current_ref->value, pointsThisTest, (pointsThisTest == 1) ? "point" : "points"); - } - } - - if (d <= 0) { - prev_ref = current_ref; - current_ref++; - } - if (d >= 0) { - prev_fp = current_fp; - current_fp++; + if (verbose) + log_write(LOG_PLAIN, "%s.%s: \"%s\" NOMATCH \"%s\" (%d %s)\n", points.name.str, + aDef.name.str, current_fp, + current_ref, pointsThisTest, (pointsThisTest == 1) ? "point" : "points"); } } if (num_subtests) @@ -265,55 +339,22 @@ static int AVal_match(const FingerTest *reference, const FingerTest *fprint, con accuracy (between 0 and 1) is returned). If MatchPoints is not NULL, it is a special "fingerprints" which tells how many points each test is worth. */ double compare_fingerprints(const FingerPrint *referenceFP, const FingerPrint *observedFP, - const FingerPrint *MatchPoints, int verbose) { - std::vector::const_iterator current_ref, prev_ref; - std::vector::const_iterator current_fp, prev_fp; - std::vector::const_iterator current_points; + const FingerPrintDef *MatchPoints, int verbose) { unsigned long num_subtests = 0, num_subtests_succeeded = 0; - unsigned long new_subtests, new_subtests_succeeded; assert(referenceFP); assert(observedFP); - /* We rely on tests being sorted by name. */ - prev_ref = referenceFP->tests.end(); - prev_fp = observedFP->tests.end(); - current_ref = referenceFP->tests.begin(); - current_fp = observedFP->tests.begin(); - current_points = MatchPoints->tests.begin(); - while (current_ref != referenceFP->tests.end() - && current_fp != observedFP->tests.end()) { - int d; + for (int i = 0; i < NUM_FPTESTS; i++) { + const FingerTest ¤t_ref = referenceFP->tests[i]; + const FingerTest ¤t_fp = observedFP->tests[i]; + const FingerTestDef &points = MatchPoints->getTestDef(INT2ID(i)); - /* Check for sortedness. */ - if (prev_ref != referenceFP->tests.end()) - assert(strcmp(prev_ref->name, current_ref->name) < 0); - if (prev_fp != observedFP->tests.end()) - assert(strcmp(prev_fp->name, current_fp->name) < 0); + unsigned long new_subtests = 0, new_subtests_succeeded = 0; - d = strcmp(current_ref->name, current_fp->name); - if (d == 0) { - new_subtests = new_subtests_succeeded = 0; - for (; current_points != MatchPoints->tests.end(); current_points++) { - if (strcmp(current_ref->name, current_points->name) == 0) - break; - } - if (current_points == MatchPoints->tests.end()) - fatal("%s: Failed to locate test %s in MatchPoints directive of fingerprint file", __func__, current_ref->name); - - AVal_match(&*current_ref, &*current_fp, &*current_points, - &new_subtests, &new_subtests_succeeded, 0, verbose); - num_subtests += new_subtests; - num_subtests_succeeded += new_subtests_succeeded; - } - - if (d <= 0) { - prev_ref = current_ref; - current_ref++; - } - if (d >= 0) { - prev_fp = current_fp; - current_fp++; - } + AVal_match(current_ref, current_fp, points, + &new_subtests, &new_subtests_succeeded, 0, verbose); + num_subtests += new_subtests; + num_subtests_succeeded += new_subtests_succeeded; } assert(num_subtests_succeeded <= num_subtests); @@ -347,7 +388,6 @@ void match_fingerprint(const FingerPrint *FP, FingerPrintResultsIPv4 *FPR, assert(accuracy_threshold >= 0 && accuracy_threshold <= 1); FP_copy = *FP; - FP_copy.sort(); FPR->overall_results = OSSCAN_SUCCESS; @@ -356,7 +396,6 @@ void match_fingerprint(const FingerPrint *FP, FingerPrintResultsIPv4 *FPR, acc = compare_fingerprints(*current_os, &FP_copy, DB->MatchPoints, 0); - /* error("Comp to %s: %li/%li=%f", o.reference_FPs1[i]->OS_name, num_subtests_succeeded, num_subtests, acc); */ if (acc >= FPR_entrance_requirement || acc == 1.0) { state = 0; @@ -510,10 +549,8 @@ void WriteSInfo(char *ostr, int ostrlen, bool isGoodFP, null-terminated. Returns the number of bytes written, excluding the terminator. */ static int test2str(const FingerTest *test, char *s, const size_t n) { - std::vector::const_iterator av; char *p; char *end; - size_t len; if (n == 0) return 0; @@ -521,40 +558,29 @@ static int test2str(const FingerTest *test, char *s, const size_t n) { p = s; end = s + n - 1; - len = strlen(test->name); - if (p + len > end) + std::vector &results = *test->results; + p += Snprintf(p, n, "%s(", test->getTestName()); + if (p > end) goto error; - memcpy(p, test->name, len); - p += len; - if (p + 1 > end) - goto error; - *p++ = '('; - - for (av = test->results->begin(); av != test->results->end(); av++) { - if (av != test->results->begin()) { - if (p + 1 > end) - goto error; - *p++ = '%'; - } - len = strlen(av->attribute); - if (p + len > end) +assert(results.size() == test->def->numAttrs); + for (u8 i = 0; i < results.size(); i++) { + if (results[i] == NULL) + continue; + p += Snprintf(p, end - p, "%s=%s%%", test->getAValName(i), results[i]); + if (p > end) goto error; - memcpy(p, av->attribute, len); - p += len; - if (p + 1 > end) - goto error; - *p++ = '='; - len = strlen(av->value); - if (p + len > end) - goto error; - memcpy(p, av->value, len); - p += len; } - if (p + 1 > end) + // overwrite last '%' with ')' + if (*(p - 1) == '%') + *(p - 1) = ')'; + // if there were no results and there is space for it, close parenthesis + else if (*(p - 1) == '(' && p < end) + *p++ = ')'; + // otherwise, something went wrong. + else goto error; - *p++ = ')'; *p = '\0'; @@ -566,101 +592,94 @@ error: return -1; } -static std::vector *str2AVal(const char *str, const char *end) { - int i = 1; - int count = 1; +bool FingerTest::str2AVal(const char *str, const char *end) { + assert(results); + assert(def); const char *q = str, *p=str; - std::vector *AVs = new std::vector; - - if (!*str || str == end) - return AVs; - - /* count the AVals */ - while ((q = strchr_p(q, end, '%'))) { - count++; - q++; + if (!def->hasR && 0 == strncmp("R=N", str, end - str)) { + return true; } + u8 count = def->numAttrs; + std::vector &AVs = *results; - AVs->reserve(count); - for (i = 0; i < count; i++) { - struct AVal av; - + for (u8 i = 0; i < count && p < end; i++) { q = strchr_p(p, end, '='); if (!q) { - fatal("Parse error with AVal string (%s) in nmap-os-db file", str); + error("Parse error with AVal string (%s) in nmap-os-db file", str); + return false; + } + std::map::const_iterator idx = def->AttrIdx.find(FPstr(p, q)); + if (idx == def->AttrIdx.end() || AVs[idx->second] != NULL) { + error("Parse error with AVal string (%s) in nmap-os-db file", str); + return false; } - av.attribute = string_pool_substr(p, q); p = q+1; - if (i < count - 1) { - q = strchr_p(p, end, '%'); - if (!q) { - fatal("Parse error with AVal string (%s) in nmap-os-db file", str); - } - av.value = string_pool_substr(p, q); - } else { - av.value = string_pool_substr(p, end); + q = strchr_p(p, end, '%'); + if (!q) { + q = end; } + if (p != q) // empty? use NULL + AVs[idx->second] = string_pool_substr(p, q); p = q + 1; - AVs->push_back(av); } - - return AVs; + return true; } -/* Compare two AVal chains literally, without evaluating the value of either one - as an expression. This is used by mergeFPs. Unlike with AVal_match, it is - always the case that test_match_literal(a, b) == test_match_literal(b, a). */ -static bool test_match_literal(const FingerTest *a, const FingerTest *b) { - std::vector::const_iterator ia, ib; +void FingerTest::setAVal(const char *attr, const char *value) { + u8 idx = def->AttrIdx.at(attr); + assert(idx < results->size()); + (*results)[idx] = value; +} - for (ia = a->results->begin(), ib = b->results->begin(); - ia != a->results->end() && ib != b->results->end(); - ia++, ib++) { - if (strcmp(ia->attribute, ib->attribute) != 0) - return false; - } - if (ia != a->results->end() || ib != b->results->end()) - return false; +const char *FingerTest::getAValName(u8 index) const { + return def->Attrs.at(index).name; +} - return true; +const char *FingerTest::getAVal(const char *attr) { + if (!results) + return NULL; + + u8 idx = def->AttrIdx.at(attr); + return results->at(idx); } /* This is a less-than relation predicate that establishes the preferred order of tests when they are displayed. Returns true if and only if the test a should come before the test b. */ -static bool FingerTest_lessthan(const FingerTest* a, const FingerTest* b) { - /* This defines the order in which test lines should appear. */ - const char *TEST_ORDER[] = { - "SEQ", "OPS", "WIN", "ECN", - "T1", "T2", "T3", "T4", "T5", "T6", "T7", - "U1", "IE" - }; - unsigned int i; - int ia, ib; +struct FingerTestCmp { + bool operator()(const FingerTest* a, const FingerTest* b) { + if (a->id != b->id) + return a->id < b->id; + if (a->results == NULL) { + return b->results != NULL; + } + else if (b->results == NULL) { + return false; + } + const std::vector &av_a = *a->results; + size_t numtests = av_a.size(); + const std::vector &av_b = *b->results; + assert(av_b.size() == numtests); - /* The indices at which the test names were found in the list. -1 means "not - found." */ - ia = -1; - ib = -1; - /* Look up the test names in the list. */ - for (i = 0; i < sizeof(TEST_ORDER) / sizeof(*TEST_ORDER); i++) { - if (ia == -1 && strcmp(a->name, TEST_ORDER[i]) == 0) - ia = i; - if (ib == -1 && strcmp(b->name, TEST_ORDER[i]) == 0) - ib = i; - /* Once we've found both tests we can stop searching. */ - if (ia != -1 && ib != -1) - break; + for (size_t i = 0; i < numtests; i++) { + if (av_a[i] == NULL) { + if (av_b[i] == NULL) + continue; + else + return true; + } + else if (av_b[i] == NULL) { + return false; + } + int cmp = strcmp(av_a[i], av_b[i]); + if (cmp == 0) + continue; + else + return cmp < 0; + } + return false; } - /* If a test name was not found, it probably indicates an error in another - part of the code. */ - if (ia == -1) - fatal("%s received an unknown test name \"%s\".\n", __func__, a->name); - if (ib == -1) - fatal("%s received an unknown test name \"%s\".\n", __func__, b->name); - - return ia < ib; -} +}; /* Merges the tests from several fingerprints into a character string representation. Tests that are identical between more than one fingerprint @@ -675,58 +694,22 @@ const char *mergeFPs(FingerPrint *FPs[], int numFPs, bool isGoodFP, static char wrapstr[10240]; char *p; - int i; char *end = str + sizeof(str) - 1; /* Last byte allowed to write into */ - std::list tests; - std::list::iterator iter; - std::vector::iterator ft; + std::set tests; + std::set::iterator iter; if (numFPs <= 0) return "(None)"; else if (numFPs > 32) return "(Too many)"; - /* Copy the tests from each fingerprint into a flat list. */ - for (i = 0; i < numFPs; i++) { - for (ft = FPs[i]->tests.begin(); ft != FPs[i]->tests.end(); ft++) - tests.push_back(&*ft); - } - /* Put the tests in the proper order and ensure that tests with identical names are contiguous. */ - tests.sort(FingerTest_lessthan); - - /* Delete duplicate tests to ensure that all the tests are unique. One test is - a duplicate of the other if it has the same name as the first and the two - results lists match. */ - for (iter = tests.begin(); iter != tests.end(); iter++) { - std::list::iterator tmp_i, next; - tmp_i = iter; - tmp_i++; - while (tmp_i != tests.end() && strcmp((*iter)->name, (*tmp_i)->name) == 0) { - next = tmp_i; - next++; - if (test_match_literal(*iter, *tmp_i)) { - /* This is a duplicate test. Remove it. */ - tests.erase(tmp_i); - } - tmp_i = next; - } - } - - /* A safety check to make sure that no tests were lost in merging. */ - for (i = 0; i < numFPs; i++) { - for (ft = FPs[i]->tests.begin(); ft != FPs[i]->tests.end(); ft++) { - for (iter = tests.begin(); iter != tests.end(); iter++) { - if (strcmp((*iter)->name, ft->name) == 0 && test_match_literal(*iter, &*ft)) { - break; - } - } - if (iter == tests.end()) { - char buf[200]; - test2str(&*ft, buf, sizeof(buf)); - fatal("The test %s was somehow lost in %s.\n", buf, __func__); - } + for (int i = 0; i < numFPs; i++) { + for (int j = 0; j < NUM_FPTESTS; j++) { + const FingerTest &ft = FPs[i]->tests[j]; + if (ft.id != FingerPrintDef::INVALID) + tests.insert(&ft); } } @@ -794,16 +777,18 @@ const char *mergeFPs(FingerPrint *FPs[], int numFPs, bool isGoodFP, const char *fp2ascii(const FingerPrint *FP) { static char str[2048]; - std::vector::const_iterator iter; char *p = str; if (!FP) return "(None)"; - for (iter = FP->tests.begin(); iter != FP->tests.end(); iter++) { + for (int j = 0; j < NUM_FPTESTS; j++) { + const FingerTest &ft = FP->tests[j]; + if (ft.id == FingerPrintDef::INVALID) + continue; int len; - len = test2str(&*iter, p, sizeof(str) - (p - str)); + len = test2str(&ft, p, sizeof(str) - (p - str)); if (len == -1) break; p += len; @@ -887,7 +872,7 @@ static void parse_cpeline(FingerPrint *FP, const char *thisline, const char *lin which some partial fingerpritns are OK. */ /* This function is not currently used by Nmap, but it is present here because it is used by fingerprint utilities that link with Nmap object files. */ -FingerPrint *parse_single_fingerprint(const char *fprint) { +FingerPrint *parse_single_fingerprint(const FingerPrintDB *DB, const char *fprint) { int lineno = 0; const char *p, *q; const char *thisline, *nextline; @@ -938,15 +923,16 @@ FingerPrint *parse_single_fingerprint(const char *fprint) { parse_cpeline(FP, thisline, nextline, lineno); } else if ((q = strchr_p(thisline, nextline, '('))) { - FingerTest test; - test.name = string_pool_substr(thisline, q); + FingerTest test(FPstr(thisline, q), *DB->MatchPoints); p = q+1; q = strchr_p(p, nextline, ')'); if (!q) { fatal("Parse error on line %d of fingerprint: %.*s\n", lineno, (int)(nextline - thisline), thisline); } - test.results = str2AVal(p, q); - FP->tests.push_back(test); + if (!test.str2AVal(p, q)) { + fatal("Parse error on line %d of fingerprint: %.*s\n", lineno, (int)(nextline - thisline), thisline); + } + FP->setTest(test); } else { fatal("Parse error on line %d of fingerprint: %.*s\n", lineno, (int)(nextline - thisline), thisline); } @@ -985,6 +971,7 @@ top: fparse: if (strncmp(line, "Fingerprint", 11) == 0) { parsingMatchPoints = false; + current = new FingerPrint; } else if (strncmp(line, "MatchPoints", 11) == 0) { if (DB->MatchPoints) fatal("Found MatchPoints directive on line %d of %s even though it has previously been seen in the file", lineno, fname); @@ -994,11 +981,8 @@ fparse: continue; } - current = new FingerPrint; - if (parsingMatchPoints) { - current->match.OS_name = NULL; - DB->MatchPoints = current; + DB->MatchPoints = new FingerPrintDef(); } else { DB->prints.push_back(current); p = line + 12; @@ -1016,9 +1000,9 @@ fparse: fatal("Parse error on line %d of fingerprint: %s", lineno, line); current->match.OS_name = cp_strndup(p, q - p + 1); - } - current->match.line = lineno; + current->match.line = lineno; + } /* Now we read the fingerprint itself */ while (fgets(line, sizeof(line), fp)) { @@ -1030,34 +1014,37 @@ fparse: q = strchr(line, '\n'); - if (!strncmp(line, "Fingerprint ",12)) { + if (0 == strncmp(line, "Fingerprint ",12)) { goto fparse; + } else if (parsingMatchPoints) { + if (!DB->MatchPoints->parseTestStr(line, q)) { + fatal("Parse error in MatchPoints on line %d of nmap-os-db file: %s", lineno, line); + } } else if (strncmp(line, "Class ", 6) == 0) { parse_classline(current, line, q, lineno); } else if (strncmp(line, "CPE ", 4) == 0) { parse_cpeline(current, line, q, lineno); } else { - FingerTest test; p = line; q = strchr(line, '('); if (!q) { error("Parse error on line %d of nmap-os-db file: %s", lineno, line); goto top; } - test.name = string_pool_substr(p, q); + FingerTest test(FPstr(p, q), *DB->MatchPoints); p = q+1; q = strchr(p, ')'); if (!q) { error("Parse error on line %d of nmap-os-db file: %s", lineno, line); goto top; } - test.results = str2AVal(p, q); - current->tests.push_back(test); + if (!test.str2AVal(p, q)) { + error("Parse error on line %d of nmap-os-db file: %s", lineno, line); + goto top; + } + current->setTest(test); } } - /* This sorting is important for later comparison of FingerPrints and - FingerTests. */ - current->sort(); } fclose(fp); diff --git a/osscan.h b/osscan.h index 30f72c24c..bc462567b 100644 --- a/osscan.h +++ b/osscan.h @@ -67,6 +67,7 @@ #include #include +#include class Target; class FingerPrintResultsIPv4; @@ -90,14 +91,74 @@ enum dist_calc_method { /********************** STRUCTURES ***********************************/ -struct AVal { - const char *attribute; - const char *value; - AVal() : attribute(NULL), value(NULL) {} +#define NUM_FPTESTS 13 + // T2-T7 and U1 have 11 attributes each +#define FP_MAX_TEST_ATTRS 11 + // RIPCK +#define FP_MAX_NAME_LEN 5 - bool operator<(const AVal& other) const { - return strcmp(attribute, other.attribute) < 0; +// Short alphanumeric strings. +template +struct ShortStr { + char str[_MaxStrLen+1]; + bool trunc; + ShortStr() : trunc(false) {memset(str, 0, sizeof(str));} + ShortStr(const char *s) { setStr(s); } + ShortStr(const char *s, const char *e) { setStr(s, e); } + void setStr(const char *in); + void setStr(const char *in, const char *end); + // Helpers for type conversion + operator const char *() const {return this->str;} + operator char *() {return this->str;} + bool operator==(const ShortStr &other) const { + return (!trunc && !other.trunc + && strncmp(str, other.str, _MaxStrLen) == 0); } + bool operator!=(const ShortStr &other) const { + return (trunc || other.trunc + || strncmp(str, other.str, _MaxStrLen) != 0); + } + bool operator<(const ShortStr &other) const { + return (trunc < other.trunc || strncmp(str, other.str, _MaxStrLen) < 0); + } +}; + +typedef ShortStr FPstr; + +struct Attr { + FPstr name; + int points; + Attr() : name(), points(0) {} + Attr(const char *n) : name(n), points(0) {} +}; + +struct FingerTestDef { + FPstr name; + u8 numAttrs; + bool hasR; + std::map AttrIdx; + std::vector Attrs; + + FingerTestDef() : name(), numAttrs(0), hasR(false) {} + FingerTestDef(const FPstr &n, const char *a[]); +}; + +#define ID2INT(_i) static_cast(_i) +#define INT2ID(_i) static_cast(_i) +class FingerPrintDef { + public: + enum TestID { SEQ, OPS, WIN, ECN, T1, T2, T3, T4, T5, T6, T7, U1, IE, INVALID }; + static const char *test_attrs[NUM_FPTESTS][FP_MAX_TEST_ATTRS]; + FingerPrintDef(); + bool parseTestStr(const char *str, const char *end); + FingerTestDef &getTestDef(TestID id) { return TestDefs[ID2INT(id)]; } + const FingerTestDef &getTestDef(TestID id) const { return TestDefs[ID2INT(id)]; } + int getTestIndex(FPstr testname) { return ID2INT(TestIdx.at(testname)); } + TestID str2TestID(FPstr testname) { return TestIdx.at(testname); } + + private: + std::map TestIdx; + std::vector TestDefs; }; struct OS_Classification { @@ -124,30 +185,46 @@ struct FingerMatch { } }; +struct FingerPrintDB; struct FingerTest { - const char *name; - std::vector *results; - FingerTest(bool allocResults=false); + FingerPrintDef::TestID id; + const FingerTestDef *def; + std::vector *results; + FingerTest() : id(FingerPrintDef::INVALID), def(NULL), results(NULL) {} + FingerTest(const FPstr &testname, FingerPrintDef &Defs) { + id = Defs.str2TestID(testname); + def = &Defs.getTestDef(id); + results = new std::vector(def->numAttrs, NULL); + } + FingerTest(FingerPrintDef::TestID testid, FingerPrintDef &Defs) + : id(testid), results(NULL) { + def = &Defs.getTestDef(id); + results = new std::vector(def->numAttrs, NULL); + } ~FingerTest() { - // name is allocated from string_pool // results must be freed manually } - bool operator<(const FingerTest& other) const { - return strcmp(name, other.name) < 0; - } void erase(); + bool str2AVal(const char *str, const char *end); + void setAVal(const char *attr, const char *value); + const char *getAVal(const char *attr); + const char *getAValName(u8 index) const; + const char *getTestName() const { return def->name.str; } }; struct FingerPrint { FingerMatch match; - std::vector tests; - void sort(); + FingerTest tests[NUM_FPTESTS]; void erase(); + void setTest(const FingerTest &test) { + tests[ID2INT(test.id)] = test; + } }; + /* This structure contains the important data from the fingerprint database (nmap-os-db) */ struct FingerPrintDB { - FingerPrint *MatchPoints; + FingerPrintDef *MatchPoints; std::vector prints; FingerPrintDB(); @@ -162,7 +239,7 @@ const char *fp2ascii(const FingerPrint *FP); non-null fingerprint is returned, the user is in charge of freeing it when done. This function does not require the fingerprint to be 100% complete since it is used by scripts such as scripts/fingerwatch for - which some partial fingerpritns are OK. */ + which some partial fingerprints are OK. */ FingerPrint *parse_single_fingerprint(const char *fprint_orig); /* These functions take a file/db name and open+parse it, returning an @@ -176,10 +253,10 @@ void free_fingerprint_file(FingerPrintDB *DB); /* Compares 2 fingerprints -- a referenceFP (can have expression attributes) with an observed fingerprint (no expressions). If verbose is nonzero, differences will be printed. The comparison - accuracy (between 0 and 1) is returned). If MatchPoints is not NULL, it is + accuracy (between 0 and 1) is returned). MatchPoints is a special "fingerprints" which tells how many points each test is worth. */ double compare_fingerprints(const FingerPrint *referenceFP, const FingerPrint *observedFP, - const FingerPrint *MatchPoints, int verbose); + const FingerPrintDef *MatchPoints, int verbose); /* Takes a fingerprint and looks for matches inside the passed in reference fingerprint DB. The results are stored in in FPR (which diff --git a/osscan2.cc b/osscan2.cc index f9c9f87ac..05c204757 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -134,42 +134,36 @@ static struct scan_performance_vars perf; * Miscellaneous functions * ******************************************************************************/ -/* Fill in a struct AVal with a value based on the IP ID sequence generation +/* Return a value based on the IP ID sequence generation class (one of the IPID_SEQ_* constants). If ipid_seqclass is such that the - test result should be omitted, the function returns NULL 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, u32 ipids[NUM_SEQ_SAMPLES], + test result should be omitted, the function returns NULL. */ +static const char *make_aval_ipid_seq(int ipid_seqclass, u32 ipids[NUM_SEQ_SAMPLES], HostOsScanStats *hss) { switch (ipid_seqclass) { case IPID_SEQ_CONSTANT: - av->value = hss->target->FPR->cp_hex(ipids[0]); + return hss->target->FPR->cp_hex(ipids[0]); break; case IPID_SEQ_INCR_BY_2: case IPID_SEQ_INCR: - av->value = "I"; + return "I"; break; case IPID_SEQ_BROKEN_INCR: - av->value = "BI"; + return "BI"; break; case IPID_SEQ_RPI: - av->value = "RI"; + return "RI"; break; case IPID_SEQ_RD: - av->value = "RD"; + return "RD"; break; case IPID_SEQ_ZERO: - av->value = "Z"; + return "Z"; break; default: /* Signal to omit test result. */ return NULL; break; } - - av->attribute = attribute; - - return av; } @@ -1034,12 +1028,6 @@ HostOsScanStats::~HostOsScanStats() { FPtests[i] = NULL; } } - 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(); @@ -1156,10 +1144,6 @@ void HostOsScanStats::initScanStats() { } } for (i = 0; i < 6; i++) { - if (TOps_AVs[i]) - free(TOps_AVs[i]); - if (TWin_AVs[i]) - free(TWin_AVs[i]); TOps_AVs[i] = NULL; TWin_AVs[i] = NULL; } @@ -2029,10 +2013,6 @@ void HostOsScan::makeFP(HostOsScanStats *hss) { assert(hss); int i; - struct AVal AV; - std::vector::iterator it; - - int ttl; if (!hss->FP_TSeq) makeTSeqFP(hss); @@ -2051,35 +2031,31 @@ void HostOsScan::makeFP(HostOsScanStats *hss) { /* We create a Resp (response) attribute with value of N (no) because it is important here to note whether responses were or were not received */ - hss->FPtests[i] = new FingerTest(true); - AV.attribute = "R"; - AV.value = "N"; - hss->FPtests[i]->results->push_back(AV); - 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"; + hss->FPtests[i] = new FingerTest(INT2ID(i), *o.reference_FPs->MatchPoints); + hss->FPtests[i]->setAVal("R", "N"); } else if (hss->FPtests[i]) { - /* Replace TTL with initial TTL. */ - for (it = hss->FPtests[i]->results->begin(); it != hss->FPtests[i]->results->end(); it++) { - if (strcmp(it->attribute, "T") == 0) { - /* Found TTL item. The value for this attribute is the - * received TTL. We replace it with the initial TTL. */ - ttl = strtol(it->value, NULL, 16); + FingerTest &test = *hss->FPtests[i]; + /* The value for this attribute is the + * received TTL. We replace it with the initial TTL. */ + const char *recvTTL = test.getAVal("T"); + if (recvTTL) { + int ttl = strtol(recvTTL, NULL, 16); - if (hss->distance_guess == -1) - hss->distance_guess = get_initial_ttl_guess(ttl) - ttl; + if (hss->distance_guess == -1) + hss->distance_guess = get_initial_ttl_guess(ttl) - ttl; - if (hss->distance != -1) { - /* We've gotten response for the UDP probe and thus have - the "true" hop count. Add the number of hops between - us and the target (hss->distance - 1) to the received - TTL to get the initial TTL. */ - it->value = hss->target->FPR->cp_hex(ttl + hss->distance - 1); - } else { - /* Guess the initial TTL value */ - it->attribute = "TG"; - it->value = hss->target->FPR->cp_hex(get_initial_ttl_guess(ttl)); - } - break; + if (hss->distance != -1) { + /* We've gotten response for the UDP probe and thus have + the "true" hop count. Add the number of hops between + us and the target (hss->distance - 1) to the received + TTL to get the initial TTL. */ + test.setAVal("T", hss->target->FPR->cp_hex(ttl + hss->distance - 1)); + } else { + /* Guess the initial TTL value */ + test.setAVal("TG", hss->target->FPR->cp_hex(get_initial_ttl_guess(ttl))); + /* Delete this unknown-distance-dependent value */ + test.setAVal("T", NULL); } } } @@ -2090,7 +2066,7 @@ void HostOsScan::makeFP(HostOsScanStats *hss) { for (i = 0; i < NUM_FPTESTS; i++) { if (hss->FPtests[i] == NULL) continue; - hss->FP->tests.push_back(*hss->FPtests[i]); + hss->FP->tests[i] = *hss->FPtests[i]; } } @@ -2314,11 +2290,8 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { int good_tcp_ipid_num, good_tcp_closed_ipid_num, good_icmp_ipid_num; int tsnewval = 0; - std::vector *seq_AVs = new std::vector; - struct AVal AV; - - /* Need 8 AVals for SP, GCD, ISR, TI, CI, II, SS, TS. */ - seq_AVs->reserve(8); + hss->FP_TSeq = new FingerTest(FingerPrintDef::SEQ, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TSeq; /* Now we make sure there are no gaps in our response array ... */ for (i = 0, j = 0; i < NUM_SEQ_SAMPLES; i++) { @@ -2399,15 +2372,9 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } } - AV.attribute = "SP"; - AV.value = hss->target->FPR->cp_hex(hss->si.index); - seq_AVs->push_back(AV); - AV.attribute = "GCD"; - AV.value = hss->target->FPR->cp_hex(seq_gcd); - seq_AVs->push_back(AV); - AV.attribute = "ISR"; - AV.value = hss->target->FPR->cp_hex((unsigned int) seq_rate); - seq_AVs->push_back(AV); + test.setAVal("SP", hss->target->FPR->cp_hex(hss->si.index)); + test.setAVal("GCD", hss->target->FPR->cp_hex(seq_gcd)); + test.setAVal("ISR", hss->target->FPR->cp_hex((unsigned int) seq_rate)); } else if (hss->si.responses > 0) { if (o.debugging) log_write(LOG_PLAIN, "Insufficient responses from %s for TCP sequencing (%d), OS detection may be less accurate\n", hss->target->targetipstr(), hss->si.responses); @@ -2462,12 +2429,9 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } /* This fills in TI=Z or something like that. */ - if (make_aval_ipid_seq(&AV, "TI", tcp_ipid_seqclass, hss->ipid.tcp_ipids, hss) != NULL) - seq_AVs->push_back(AV); - if (make_aval_ipid_seq(&AV, "CI", tcp_closed_ipid_seqclass, hss->ipid.tcp_closed_ipids, hss) != NULL) - seq_AVs->push_back(AV); - if (make_aval_ipid_seq(&AV, "II", icmp_ipid_seqclass, hss->ipid.icmp_ipids, hss) != NULL) - seq_AVs->push_back(AV); + test.setAVal("TI", make_aval_ipid_seq(tcp_ipid_seqclass, hss->ipid.tcp_ipids, hss)); + test.setAVal("CI", make_aval_ipid_seq(tcp_closed_ipid_seqclass, hss->ipid.tcp_closed_ipids, hss)); + test.setAVal("II", make_aval_ipid_seq(icmp_ipid_seqclass, hss->ipid.icmp_ipids, hss)); /* SS: Shared IP ID sequence boolean */ if ((tcp_ipid_seqclass == IPID_SEQ_INCR || @@ -2478,14 +2442,12 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { icmp_ipid_seqclass == IPID_SEQ_RPI)) { /* Both are incremental. Thus we have "SS" test. Check if they are in the same sequence. */ - AV.attribute = "SS"; u32 avg = (hss->ipid.tcp_ipids[good_tcp_ipid_num - 1] - hss->ipid.tcp_ipids[0]) / (good_tcp_ipid_num - 1); if (hss->ipid.icmp_ipids[0] < hss->ipid.tcp_ipids[good_tcp_ipid_num - 1] + 3 * avg) { - AV.value = "S"; + test.setAVal("SS", "S"); } else { - AV.value = "O"; + test.setAVal("SS", "O"); } - seq_AVs->push_back(AV); } /* Now we look at TCP Timestamp sequence prediction */ @@ -2541,16 +2503,12 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { switch (hss->si.ts_seqclass) { case TS_SEQ_ZERO: - AV.attribute = "TS"; - AV.value = "0"; - seq_AVs->push_back(AV); + test.setAVal("TS", "0"); break; case TS_SEQ_2HZ: case TS_SEQ_100HZ: case TS_SEQ_1000HZ: case TS_SEQ_OTHER_NUM: - AV.attribute = "TS"; - /* Here we "cheat" a little to make the classes correspond more closely to common real-life frequencies (particularly 100) which aren't powers of two. */ @@ -2572,26 +2530,12 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { tsnewval = (unsigned int)(0.5 + log(avg_ts_hz) / log(2.0)); } - AV.value = hss->target->FPR->cp_hex(tsnewval); - seq_AVs->push_back(AV); + test.setAVal("TS", hss->target->FPR->cp_hex(tsnewval)); break; case TS_SEQ_UNSUPPORTED: - AV.attribute = "TS"; - AV.value = "U"; - seq_AVs->push_back(AV); + test.setAVal("TS", "U"); break; } - - /* Now generate the SEQ line of the fingerprint if there are any test results - in seq_AVs. */ - if (!seq_AVs->empty()) { - hss->FP_TSeq = new FingerTest; - hss->FP_TSeq->name = "SEQ"; - hss->FP_TSeq->results = seq_AVs; - } - else { - delete seq_AVs; - } } @@ -2612,15 +2556,11 @@ void HostOsScan::makeTOpsFP(HostOsScanStats *hss) { return; } - std::vector *AVs = new std::vector; - AVs->reserve(n); + hss->FP_TOps = new FingerTest(FingerPrintDef::OPS, *o.reference_FPs->MatchPoints); + std::vector &results = *hss->FP_TOps->results; for (i = 0; i < n; i++) - AVs->push_back(*hss->TOps_AVs[i]); - - hss->FP_TOps = new FingerTest; - hss->FP_TOps->results = AVs; - hss->FP_TOps->name = "OPS"; + results[i] = hss->TOps_AVs[i]; } @@ -2641,15 +2581,11 @@ void HostOsScan::makeTWinFP(HostOsScanStats *hss) { return; } - std::vector *AVs = new std::vector; - AVs->reserve(n); + hss->FP_TWin = new FingerTest(FingerPrintDef::WIN, *o.reference_FPs->MatchPoints); + std::vector &results = *hss->FP_TWin->results; for (i = 0; i < n; i++) - AVs->push_back(*hss->TWin_AVs[i]); - - hss->FP_TWin = new FingerTest; - hss->FP_TWin->results = AVs; - hss->FP_TWin->name = "WIN"; + results[i] = hss->TWin_AVs[i]; } @@ -2727,37 +2663,15 @@ bool HostOsScan::processTOpsResp(HostOsScanStats *hss, const struct tcp_hdr *tcp if (hss->FP_TOps || hss->TOps_AVs[replyNo]) return false; - hss->TOps_AVs[replyNo] = (struct AVal *) safe_zalloc(sizeof(struct AVal)); int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); if (opsParseResult <= 0) { if (opsParseResult < 0 && o.debugging) error("Option parse error for TOps response %d from %s.", replyNo, hss->target->targetipstr()); - hss->TOps_AVs[replyNo]->value = ""; + hss->TOps_AVs[replyNo] = ""; } else { - hss->TOps_AVs[replyNo]->value = hss->target->FPR->cp_dup(ops_buf, opsParseResult); - } - - 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->TOps_AVs[replyNo] = hss->target->FPR->cp_dup(ops_buf, opsParseResult); } hss->TOpsReplyNum++; @@ -2771,29 +2685,7 @@ bool HostOsScan::processTWinResp(HostOsScanStats *hss, const struct tcp_hdr *tcp if (hss->FP_TWin || hss->TWin_AVs[replyNo]) return false; - hss->TWin_AVs[replyNo] = (struct AVal *) safe_zalloc(sizeof(struct AVal)); - hss->TWin_AVs[replyNo]->value = hss->target->FPR->cp_hex(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->TWin_AVs[replyNo] = hss->target->FPR->cp_hex(ntohs(tcp->th_win)); hss->TWinReplyNum++; return true; @@ -2801,73 +2693,58 @@ bool HostOsScan::processTWinResp(HostOsScanStats *hss, const struct tcp_hdr *tcp bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip) { - struct AVal AV; char ops_buf[256]; char quirks_buf[10]; char *p; - int numtests = 7; const struct tcp_hdr *tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); if (hss->FP_TEcn) return false; /* Create the Avals */ - std::vector *AVs = new std::vector; - AVs->reserve(numtests); + hss->FP_TEcn = new FingerTest(FingerPrintDef::ECN, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TEcn; - AV.attribute = "R"; - AV.value = "Y"; - AVs->push_back(AV); + test.setAVal("R", "Y"); /* don't frag flag */ - AV.attribute = "DF"; if (ntohs(ip->ip_off) & IP_DF) - AV.value = "Y"; + test.setAVal("DF", "Y"); else - AV.value = "N"; - AVs->push_back(AV); + test.setAVal("DF", "N"); /* TTL */ - AV.attribute = "T"; - AV.value = hss->target->FPR->cp_hex(ip->ip_ttl); - AVs->push_back(AV); + test.setAVal("T", hss->target->FPR->cp_hex(ip->ip_ttl)); /* TCP Window size */ - AV.attribute = "W"; - AV.value = hss->target->FPR->cp_hex(ntohs(tcp->th_win)); - AVs->push_back(AV); + test.setAVal("W", hss->target->FPR->cp_hex(ntohs(tcp->th_win))); /* Now for the TCP options ... */ - AV.attribute = "O"; int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); if (opsParseResult <= 0) { if (opsParseResult < 0 && o.debugging) error("Option parse error for ECN response from %s.", hss->target->targetipstr()); - AV.value = ""; + test.setAVal("O", ""); } else { - AV.value = hss->target->FPR->cp_dup(ops_buf, opsParseResult); + test.setAVal("O", hss->target->FPR->cp_dup(ops_buf, opsParseResult)); } - AVs->push_back(AV); /* Explicit Congestion Notification support test */ - AV.attribute = "CC"; if ((tcp->th_flags & TH_ECE) && (tcp->th_flags & TH_CWR)) /* echo back */ - AV.value = "S"; + test.setAVal("CC", "S"); else if (tcp->th_flags & TH_ECE) /* support */ - AV.value = "Y"; + test.setAVal("CC", "Y"); else if (!(tcp->th_flags & TH_CWR)) /* not support */ - AV.value = "N"; + test.setAVal("CC", "N"); else - AV.value = "O"; - AVs->push_back(AV); + test.setAVal("CC", "O"); /* TCP miscellaneous quirks test */ - AV.attribute = "Q"; p = quirks_buf; if (tcp->th_x2) { /* Reserved field of TCP is not zero */ @@ -2880,22 +2757,15 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip) { *p++ = 'U'; } *p = '\0'; - AV.value = hss->target->FPR->cp_dup(quirks_buf, p - quirks_buf); - AVs->push_back(AV); - - hss->FP_TEcn = new FingerTest; - hss->FP_TEcn->name = "ECN"; - hss->FP_TEcn->results = AVs; + test.setAVal("Q", hss->target->FPR->cp_dup(quirks_buf, p - quirks_buf)); return true; } bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int replyNo) { - struct AVal AV; assert(replyNo >= 0 && replyNo < 7); - int numtests; const struct tcp_hdr *tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); int i; @@ -2903,42 +2773,30 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int char flags_buf[10]; char quirks_buf[10]; char *p; + FingerPrintDef::TestID testid = INT2ID(ID2INT(FingerPrintDef::T1) + replyNo); if (hss->FPtests[FP_T1_7_OFF + replyNo]) return false; - if (replyNo == 0) - numtests = 8; /* T1 doesn't has 'Win', 'Ops' tests. */ - else numtests = 10; - - /* Create the Avals */ - std::vector *AVs = new std::vector; - AVs->reserve(numtests); + hss->FPtests[FP_T1_7_OFF + replyNo] = new FingerTest(testid, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FPtests[FP_T1_7_OFF + replyNo]; /* First we give the "response" flag to say we did actually receive a packet -- this way we won't match a template with R=N */ - AV.attribute = "R"; - AV.value = "Y"; - AVs->push_back(AV); + test.setAVal("R", "Y"); /* Next we check whether the Don't Fragment bit is set */ - AV.attribute = "DF"; if (ntohs(ip->ip_off) & IP_DF) - AV.value = "Y"; + test.setAVal("DF", "Y"); else - AV.value = "N"; - AVs->push_back(AV); + test.setAVal("DF", "N"); /* TTL */ - AV.attribute = "T"; - AV.value = hss->target->FPR->cp_hex(ip->ip_ttl); - AVs->push_back(AV); + test.setAVal("T", hss->target->FPR->cp_hex(ip->ip_ttl)); if (replyNo != 0) { /* Now we do the TCP Window size */ - AV.attribute = "W"; - AV.value = hss->target->FPR->cp_hex(ntohs(tcp->th_win)); - AVs->push_back(AV); + test.setAVal("W", hss->target->FPR->cp_hex(ntohs(tcp->th_win))); } /* Seq test values: @@ -2947,16 +2805,14 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int A+ = ack + 1 O = other */ - AV.attribute = "S"; if (ntohl(tcp->th_seq) == 0) - AV.value = "Z"; + test.setAVal("S", "Z"); else if (ntohl(tcp->th_seq) == tcpAck) - AV.value = "A"; + test.setAVal("S", "A"); else if (ntohl(tcp->th_seq) == tcpAck + 1) - AV.value = "A+"; + test.setAVal("S", "A+"); else - AV.value = "O"; - AVs->push_back(AV); + test.setAVal("S", "O"); /* ACK test values: Z = zero @@ -2964,16 +2820,14 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int S+ = syn + 1 O = other */ - AV.attribute = "A"; if (ntohl(tcp->th_ack) == 0) - AV.value = "Z"; + test.setAVal("A", "Z"); else if (ntohl(tcp->th_ack) == tcpSeqBase) - AV.value = "S"; + test.setAVal("A", "S"); else if (ntohl(tcp->th_ack) == tcpSeqBase + 1) - AV.value = "S+"; + test.setAVal("A", "S+"); else - AV.value = "O"; - AVs->push_back(AV); + test.setAVal("A", "O"); /* Flags. They must be in this order: E = ECN Echo @@ -2997,45 +2851,38 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int { TH_FIN, 'F' }, }; assert(sizeof(flag_defs) / sizeof(flag_defs[0]) < sizeof(flags_buf)); - AV.attribute = "F"; p = flags_buf; for (i = 0; i < (int) (sizeof(flag_defs) / sizeof(flag_defs[0])); i++) { if (tcp->th_flags & flag_defs[i].flag) *p++ = flag_defs[i].c; } *p = '\0'; - AV.value = hss->target->FPR->cp_dup(flags_buf, p - flags_buf); - AVs->push_back(AV); + test.setAVal("F", hss->target->FPR->cp_dup(flags_buf, p - flags_buf)); if (replyNo != 0) { char ops_buf[256]; /* Now for the TCP options ... */ - AV.attribute = "O"; int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); if (opsParseResult <= 0) { if (opsParseResult < 0 && o.debugging) error("Option parse error for T%d response from %s.", replyNo, hss->target->targetipstr()); - AV.value = ""; + test.setAVal("O", ""); } else { - AV.value = hss->target->FPR->cp_dup(ops_buf, opsParseResult); + test.setAVal("O", hss->target->FPR->cp_dup(ops_buf, opsParseResult)); } - AVs->push_back(AV); } /* Rst Data CRC32 */ - AV.attribute = "RD"; length = (int) ntohs(ip->ip_len) - 4 * ip->ip_hl -4 * tcp->th_off; if ((tcp->th_flags & TH_RST) && length>0) { - AV.value = hss->target->FPR->cp_hex(nbase_crc32(((u8 *)tcp) + 4 * tcp->th_off, length)); + test.setAVal("RD", hss->target->FPR->cp_hex(nbase_crc32(((u8 *)tcp) + 4 * tcp->th_off, length))); } else { - AV.value = "0"; + test.setAVal("RD", "0"); } - AVs->push_back(AV); /* TCP miscellaneous quirks test */ - AV.attribute = "Q"; p = quirks_buf; if (tcp->th_x2) { /* Reserved field of TCP is not zero */ @@ -3048,39 +2895,24 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, int *p++ = 'U'; } *p = '\0'; - AV.value = hss->target->FPR->cp_dup(quirks_buf, p - quirks_buf); - AVs->push_back(AV); - - 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"; + test.setAVal("Q", hss->target->FPR->cp_dup(quirks_buf, p - quirks_buf)); return true; } bool HostOsScan::processTUdpResp(HostOsScanStats *hss, const struct ip *ip) { - struct AVal AV; assert(hss); assert(ip); const struct icmp *icmp; const struct ip *ip2; - int numtests; unsigned short checksum; unsigned short *checksumptr; const struct udp_hdr *udp; const unsigned char *datastart, *dataend; -#if !defined(SOLARIS) && !defined(SUNOS) && !defined(IRIX) && !defined(HPUX) - numtests = 10; -#else - /* We don't do RID test under these operating systems, thus the - number of test is 1 less. */ - numtests = 9; -#endif - if (hss->FP_TUdp) return false; @@ -3097,94 +2929,75 @@ bool HostOsScan::processTUdpResp(HostOsScanStats *hss, const struct ip *ip) { return false; } - /* Create the Avals */ - std::vector *AVs = new std::vector; - AVs->reserve(numtests); + hss->FP_TUdp = new FingerTest(FingerPrintDef::U1, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TUdp; /* First of all, if we got this far the response was yes */ - AV.attribute = "R"; - AV.value = "Y"; - AVs->push_back(AV); + test.setAVal("R", "Y"); /* Also, we now know that the port we reached was closed */ if (hss->target->FPR->osscan_closedudpport == -1) hss->target->FPR->osscan_closedudpport = hss->upi.dport; /* Now let us do an easy one, Don't fragment */ - AV.attribute = "DF"; if (ntohs(ip->ip_off) & IP_DF) - AV.value = "Y"; + test.setAVal("DF", "Y"); else - AV.value = "N"; - AVs->push_back(AV); + test.setAVal("DF", "N"); /* TTL */ - AV.attribute = "T"; - AV.value = hss->target->FPR->cp_hex(ip->ip_ttl); - AVs->push_back(AV); + test.setAVal("T", hss->target->FPR->cp_hex(ip->ip_ttl)); /* Now we look at the IP datagram length that was returned, some machines send more of the original packet back than others */ - AV.attribute = "IPL"; - AV.value = hss->target->FPR->cp_hex(ntohs(ip->ip_len)); - AVs->push_back(AV); + test.setAVal("IPL", hss->target->FPR->cp_hex(ntohs(ip->ip_len))); /* unused filed not zero in Destination Unreachable Message */ - AV.attribute = "UN"; - AV.value = hss->target->FPR->cp_hex(ntohl(icmp->icmp_void)); - AVs->push_back(AV); + test.setAVal("UN", hss->target->FPR->cp_hex(ntohl(icmp->icmp_void))); /* OK, lets check the returned IP length, some systems @$@ this up */ - AV.attribute = "RIPL"; if (ntohs(ip2->ip_len) == 328) - AV.value = "G"; + test.setAVal("RIPL", "G"); else - AV.value = hss->target->FPR->cp_hex(ntohs(ip2->ip_len)); - AVs->push_back(AV); + test.setAVal("RIPL", hss->target->FPR->cp_hex(ntohs(ip2->ip_len))); /* This next test doesn't work on Solaris because the lamers overwrite our ip_id */ #if !defined(SOLARIS) && !defined(SUNOS) && !defined(IRIX) && !defined(HPUX) /* Now lets see how they treated the ID we sent ... */ - AV.attribute = "RID"; if (ntohs(ip2->ip_id) == hss->upi.ipid) - AV.value = "G"; /* The good "expected" value */ + test.setAVal("RID", "G"); /* The good "expected" value */ else - AV.value = hss->target->FPR->cp_hex(ntohs(ip2->ip_id)); - AVs->push_back(AV); + test.setAVal("RID", hss->target->FPR->cp_hex(ntohs(ip2->ip_id))); #endif /* Let us see if the IP checksum we got back computes */ - AV.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) { - AV.value = "Z"; + test.setAVal("RIPCK", "Z"); } else { *checksumptr = 0; if (in_cksum((unsigned short *)ip2, 20) == checksum) { - AV.value = "G"; /* The "expected" good value */ + test.setAVal("RIPCK", "G"); /* The "expected" good value */ } else { - AV.value = "I"; /* They modified it */ + test.setAVal("RIPCK", "I"); /* They modified it */ } *checksumptr = checksum; } - AVs->push_back(AV); /* UDP checksum */ - AV.attribute = "RUCK"; if (udp->uh_sum == hss->upi.udpck) - AV.value = "G"; /* The "expected" good value */ + test.setAVal("RUCK", "G"); /* The "expected" good value */ else - AV.value = hss->target->FPR->cp_hex(ntohs(udp->uh_sum)); - AVs->push_back(AV); + test.setAVal("RUCK", hss->target->FPR->cp_hex(ntohs(udp->uh_sum))); /* Finally we ensure the data is OK */ datastart = ((unsigned char *)udp) + 8; @@ -3195,16 +3008,10 @@ bool HostOsScan::processTUdpResp(HostOsScanStats *hss, const struct ip *ip) { break; datastart++; } - AV.attribute = "RUD"; if (datastart < dataend) - AV.value = "I"; /* They modified it */ + test.setAVal("RUD", "I"); /* They modified it */ else - AV.value = "G"; - AVs->push_back(AV); - - hss->FP_TUdp = new FingerTest; - hss->FP_TUdp->name = "U1"; - hss->FP_TUdp->results = AVs; + test.setAVal("RUD", "G"); /* Count hop count */ if (hss->distance == -1) { @@ -3218,8 +3025,6 @@ bool HostOsScan::processTUdpResp(HostOsScanStats *hss, const struct ip *ip) { bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, int replyNo) { assert(replyNo == 0 || replyNo == 1); - struct AVal AV; - int numtests = 4; const struct ip *ip1, *ip2; const struct icmp *icmp1, *icmp2; unsigned short value1, value2; @@ -3252,13 +3057,10 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, int assert(icmp1->icmp_type == 0 && icmp2->icmp_type == 0); - /* Create the Avals */ - std::vector *AVs = new std::vector; - AVs->reserve(numtests); + hss->FP_TIcmp= new FingerTest(FingerPrintDef::IE, *o.reference_FPs->MatchPoints); + FingerTest &test = *hss->FP_TIcmp; - AV.attribute = "R"; - AV.value = "Y"; - AVs->push_back(AV); + test.setAVal("R", "Y"); /* DFI test values: * Y. Both set DF; @@ -3266,52 +3068,42 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, int * N. Both not set; * O. Other(both different with the sender, -_-b). */ - AV.attribute = "DFI"; value1 = (ntohs(ip1->ip_off) & IP_DF); value2 = (ntohs(ip2->ip_off) & IP_DF); if (value1 && value2) /* both set */ - AV.value = "Y"; + test.setAVal("DFI", "Y"); else if (value1 && !value2) /* echo back */ - AV.value = "S"; + test.setAVal("DFI", "S"); else if (!value1 && !value2) /* neither set */ - AV.value = "N"; + test.setAVal("DFI", "N"); else - AV.value = "O"; - AVs->push_back(AV); + test.setAVal("DFI", "O"); /* TTL */ - AV.attribute = "T"; - AV.value = hss->target->FPR->cp_hex(ip1->ip_ttl); - AVs->push_back(AV); + test.setAVal("T", hss->target->FPR->cp_hex(ip1->ip_ttl)); /* ICMP Code value. Test values: * [Value]. Both set Code to the same value [Value]; * S. Both use the Code that the sender uses; * O. Other. */ - AV.attribute = "CD"; value1 = icmp1->icmp_code; value2 = icmp2->icmp_code; if (value1 == value2) { if (value1 == 0) - AV.value = "Z"; + test.setAVal("CD", "Z"); else - AV.value = hss->target->FPR->cp_hex(value1); + test.setAVal("CD", hss->target->FPR->cp_hex(value1)); } else if (value1 == 9 && value2 == 0) /* both the same as in the corresponding probe */ - AV.value = "S"; + test.setAVal("CD", "S"); else - AV.value = "O"; - AVs->push_back(AV); - - hss->FP_TIcmp= new FingerTest; - hss->FP_TIcmp->name = "IE"; - hss->FP_TIcmp->results = AVs; + test.setAVal("CD", "O"); return true; } diff --git a/osscan2.h b/osscan2.h index b8fc5be8d..5e28bbfcc 100644 --- a/osscan2.h +++ b/osscan2.h @@ -71,8 +71,7 @@ #include #include #include "timing.h" -struct FingerPrint; -struct FingerTest; +#include "osscan.h" class FingerPrintResultsIPv4; class Target; @@ -81,8 +80,6 @@ class Target; * CONSTANT DEFINITIONS * ******************************************************************************/ -#define NUM_FPTESTS 13 - /* The number of tries we normally do. This may be increased if the target looks like a good candidate for fingerprint submission, or fewer if the user gave the --max-os-tries option */ @@ -173,11 +170,6 @@ typedef enum OFProbeType { * FUNCTION PROTOTYPES * ******************************************************************************/ -/* 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(std::vector &Targets); - int get_initial_ttl_guess(u8 ttl); int identify_sequence(int numSamples, u32 *ipid_diffs, int islocalhost, int allipideqz); @@ -303,22 +295,22 @@ class HostOsScanStats { * finally be passed to hs->target->FPR->FPs[x]. */ FingerPrint *FP; FingerTest *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 */ + #define FP_TSeq FPtests[ID2INT(FingerPrintDef::SEQ)] + #define FP_TOps FPtests[ID2INT(FingerPrintDef::OPS)] + #define FP_TWin FPtests[ID2INT(FingerPrintDef::WIN)] + #define FP_TEcn FPtests[ID2INT(FingerPrintDef::ECN)] + #define FP_T1_7_OFF ID2INT(FingerPrintDef::T1) + #define FP_T1 FPtests[ID2INT(FingerPrintDef::T1)] + #define FP_T2 FPtests[ID2INT(FingerPrintDef::T2)] + #define FP_T3 FPtests[ID2INT(FingerPrintDef::T3)] + #define FP_T4 FPtests[ID2INT(FingerPrintDef::T4)] + #define FP_T5 FPtests[ID2INT(FingerPrintDef::T5)] + #define FP_T6 FPtests[ID2INT(FingerPrintDef::T6)] + #define FP_T7 FPtests[ID2INT(FingerPrintDef::T7)] + #define FP_TUdp FPtests[ID2INT(FingerPrintDef::U1)] + #define FP_TIcmp FPtests[ID2INT(FingerPrintDef::IE)] + const char *TOps_AVs[6]; /* 6 AVs of TOps */ + const char *TWin_AVs[6]; /* 6 AVs of TWin */ /* The following are variables to store temporary results * during the os fingerprinting process of this host. */