mirror of
https://github.com/nmap/nmap.git
synced 2025-12-15 20:29:03 +00:00
Optimizations for IPv4 OS scan
Since the number, names, and preferred order of OS detection tests are known, we can use fixed indices to put each test/value type in a predictable location. Previously, we would insert the tests in an arbitrary order, sort them, and then use strcmp() in each comparison to ensure the sort order holds and any missing tests are skipped over. Keeping test names in one location (MatchPoints) saves memory and keeps the string pool small, which improves performance by reducing lookups and making existing lookups faster. Using a dedicated class (FingerPrintDef) for MatchPoints avoids calling strtol() to obtain the points value for every comparison.
This commit is contained in:
525
osscan.cc
525
osscan.cc
@@ -78,6 +78,88 @@
|
||||
|
||||
extern NmapOps o;
|
||||
|
||||
template<u8 _MaxStrLen> void ShortStr<_MaxStrLen>::setStr(const char *in) {
|
||||
const char *end = in;
|
||||
while (end - in < _MaxStrLen && *++end);
|
||||
setStr(in, end);
|
||||
trunc = trunc || *end;
|
||||
}
|
||||
template<u8 _MaxStrLen> 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<FingerPrint *>::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<struct AVal>;
|
||||
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<FPstr, TestID>::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<FPstr, u8>::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<FingerTest>::iterator t = this->tests.begin();
|
||||
t != this->tests.end(); t++) {
|
||||
t->erase();
|
||||
for (int i=0; i < NUM_FPTESTS; i++) {
|
||||
tests[i].erase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,48 +284,34 @@ 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<struct AVal>::const_iterator current_ref, prev_ref;
|
||||
std::vector<struct AVal>::const_iterator current_fp, prev_fp;
|
||||
std::vector<struct AVal>::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<Attr> &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<const char *> &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;
|
||||
}
|
||||
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);
|
||||
const std::vector<const char *> &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->value, current_ref->value)) {
|
||||
if (expr_match(current_fp, current_ref)) {
|
||||
subtests_succeeded += pointsThisTest;
|
||||
} else {
|
||||
if (shortcut) {
|
||||
@@ -236,19 +320,9 @@ static int AVal_match(const FingerTest *reference, const FingerTest *fprint, con
|
||||
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++;
|
||||
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,57 +339,24 @@ 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<FingerTest>::const_iterator current_ref, prev_ref;
|
||||
std::vector<FingerTest>::const_iterator current_fp, prev_fp;
|
||||
std::vector<FingerTest>::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,
|
||||
AVal_match(current_ref, current_fp, 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++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_subtests_succeeded <= num_subtests);
|
||||
return (num_subtests) ? (num_subtests_succeeded / (double) num_subtests) : 0;
|
||||
}
|
||||
@@ -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<struct AVal>::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<const char *> &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)
|
||||
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;
|
||||
*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)
|
||||
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)
|
||||
goto error;
|
||||
// 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 = '\0';
|
||||
|
||||
@@ -566,101 +592,94 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::vector<struct AVal> *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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
|
||||
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<const char *> &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<FPstr, u8>::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 = 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<struct AVal>::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;
|
||||
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
/* 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);
|
||||
else if (b->results == NULL) {
|
||||
return false;
|
||||
}
|
||||
const std::vector<const char *> &av_a = *a->results;
|
||||
size_t numtests = av_a.size();
|
||||
const std::vector<const char *> &av_b = *b->results;
|
||||
assert(av_b.size() == numtests);
|
||||
|
||||
return ia < ib;
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/* 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<const FingerTest *> tests;
|
||||
std::list<const FingerTest *>::iterator iter;
|
||||
std::vector<FingerTest>::iterator ft;
|
||||
std::set<const FingerTest *, FingerTestCmp> tests;
|
||||
std::set<const FingerTest *, FingerTestCmp>::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<const FingerTest *>::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<FingerTest>::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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
115
osscan.h
115
osscan.h
@@ -67,6 +67,7 @@
|
||||
|
||||
#include <nbase.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
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<u8 _MaxStrLen>
|
||||
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<FP_MAX_NAME_LEN> 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<FPstr, u8> AttrIdx;
|
||||
std::vector<Attr> Attrs;
|
||||
|
||||
FingerTestDef() : name(), numAttrs(0), hasR(false) {}
|
||||
FingerTestDef(const FPstr &n, const char *a[]);
|
||||
};
|
||||
|
||||
#define ID2INT(_i) static_cast<int>(_i)
|
||||
#define INT2ID(_i) static_cast<FingerPrintDef::TestID>(_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<FPstr, TestID> TestIdx;
|
||||
std::vector<FingerTestDef> TestDefs;
|
||||
};
|
||||
|
||||
struct OS_Classification {
|
||||
@@ -124,30 +185,46 @@ struct FingerMatch {
|
||||
}
|
||||
};
|
||||
|
||||
struct FingerPrintDB;
|
||||
struct FingerTest {
|
||||
const char *name;
|
||||
std::vector<struct AVal> *results;
|
||||
FingerTest(bool allocResults=false);
|
||||
FingerPrintDef::TestID id;
|
||||
const FingerTestDef *def;
|
||||
std::vector<const char *> *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<const char *>(def->numAttrs, NULL);
|
||||
}
|
||||
FingerTest(FingerPrintDef::TestID testid, FingerPrintDef &Defs)
|
||||
: id(testid), results(NULL) {
|
||||
def = &Defs.getTestDef(id);
|
||||
results = new std::vector<const char *>(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<FingerTest> 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<FingerPrint *> 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
|
||||
|
||||
428
osscan2.cc
428
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<struct AVal>::iterator it;
|
||||
|
||||
int ttl;
|
||||
|
||||
if (!hss->FP_TSeq)
|
||||
makeTSeqFP(hss);
|
||||
@@ -2051,19 +2031,16 @@ 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
|
||||
FingerTest &test = *hss->FPtests[i];
|
||||
/* The value for this attribute is the
|
||||
* received TTL. We replace it with the initial TTL. */
|
||||
ttl = strtol(it->value, NULL, 16);
|
||||
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;
|
||||
@@ -2073,13 +2050,12 @@ void HostOsScan::makeFP(HostOsScanStats *hss) {
|
||||
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);
|
||||
test.setAVal("T", 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;
|
||||
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<struct AVal> *seq_AVs = new std::vector<struct AVal>;
|
||||
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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
AVs->reserve(n);
|
||||
hss->FP_TOps = new FingerTest(FingerPrintDef::OPS, *o.reference_FPs->MatchPoints);
|
||||
std::vector<const char *> &results = *hss->FP_TOps->results;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
AVs->reserve(n);
|
||||
hss->FP_TWin = new FingerTest(FingerPrintDef::WIN, *o.reference_FPs->MatchPoints);
|
||||
std::vector<const char *> &results = *hss->FP_TWin->results;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
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<struct AVal> *AVs = new std::vector<struct AVal>;
|
||||
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;
|
||||
}
|
||||
|
||||
42
osscan2.h
42
osscan2.h
@@ -71,8 +71,7 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#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<Target *> &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. */
|
||||
|
||||
Reference in New Issue
Block a user