diff --git a/nmap-os-db b/nmap-os-db index 39c8ac685..e7580b17b 100644 --- a/nmap-os-db +++ b/nmap-os-db @@ -20,6 +20,7 @@ # # For a complete description of Nmap OS detection and the format of # fingerprints in this file, see https://nmap.org/book/osdetect.html. +This nmap-os-db is only valid for Nmap 7.94.2 and later # This first element provides the number of points every fingerprint # test is worth. Tests like TTL or Don't fragment are worth less diff --git a/nmap.h b/nmap.h index 276bf2367..a5d6c6529 100644 --- a/nmap.h +++ b/nmap.h @@ -123,7 +123,7 @@ file by the makefiles. */ #define NMAP_MAJOR 7 #define NMAP_MINOR 94 -#define NMAP_BUILD 1 +#define NMAP_BUILD 2 /* SVN, BETA, etc. */ #define NMAP_SPECIAL "SVN" diff --git a/osscan.cc b/osscan.cc index 1b1612520..405cd2045 100644 --- a/osscan.cc +++ b/osscan.cc @@ -292,43 +292,99 @@ void FingerPrint::erase() { | (or) - (range) No parentheses are allowed. */ -bool expr_match(const char *val, size_t vlenx, const char *expr, size_t explen, bool do_nested) { +bool expr_match(const char *val, size_t vlen, const char *expr, size_t explen, bool do_nested) { const char *p, *q, *q1; /* OHHHH YEEEAAAAAHHHH!#!@#$!% */ - size_t vlen = strlen(val); + if (vlen == 0) + vlen = strlen(val); + if (explen == 0) + explen = strlen(expr); + + // If both are empty, match; else if either is empty, no match. + if (vlen == 0) { + return explen == 0; + } + else if (explen == 0) { + return vlen == 0; + } p = expr; do { + const char *nest = NULL; // where the [] nested expr starts + const char *subval = val; // portion of val after previous nest and before the next one + size_t sublen; // length of subval not subject to nested matching q = strchr(p, '|'); - size_t explen = q ? q - p : strlen(p); + + // if we're already in a nested expr, we skip this and just match as usual. + if (do_nested) { + nest = strchr(p, '['); + subval = val; + // As long as we keep finding nested portions, e.g. M[>500]ST11W[1-5] + while (nest) { + q1 = strchr(nest, ']'); + assert(q1); + if (q && q < q1) { + // "AB[C|D]E|XYZ" + q = strchr(q1, '|'); + } + // "AB[C-D]E" or or "AB[C-D]E|F" + sublen = nest - p; + if (strncmp(p, subval, sublen) != 0) { + goto next_expr; + } + nest++; + subval += sublen; + size_t nlen = 0; + while (isxdigit(subval[nlen])) { + nlen++; + } + p = q1 + 1; + // fprintf(stderr, "nest: %-.*s cmp %-.*s\n", nlen, subval, q1 - nest, nest); + if (nlen > 0 && expr_match(subval, nlen, nest, q1 - nest, false)) { + subval += nlen; + nest = strchr(p, '['); + } + else { + goto next_expr; + } + } + // No more nested portions. string match the rest: + sublen = vlen - (subval - val); + if ((explen - (p - expr)) == sublen && !strncmp(subval, p, sublen)) { + return true; + } + } + sublen = q ? q - p : explen - (p - expr); if (*p == '>') { - if ((vlen > explen - 1) - || (vlen == explen - 1 && strncmp(val, p + 1, vlen) > 0)) { + if ((vlen > sublen - 1) + || (vlen == sublen - 1 && strncmp(subval, p + 1, vlen) > 0)) { return true; } } else if (*p == '<') { - if ((vlen < explen - 1) - || (vlen == explen - 1 && strncmp(val, p + 1, vlen) < 0)) { + if ((vlen < sublen - 1) + || (vlen == sublen - 1 && strncmp(subval, p + 1, vlen) < 0)) { return true; } } else { q1 = strchr(p, '-'); if (q1 != NULL) { - size_t explen1 = q1 - p; - if ((vlen > explen1) - || (vlen == explen1 && strncmp(val, p + 1, vlen) >= 0)) { - explen -= (explen1 + 1); - if ((vlen < explen) - || (vlen == explen && strncmp(val, p + 1, vlen) <= 0)) { + size_t sublen1 = q1 - p; + if ((vlen > sublen1) + || (vlen == sublen1 && strncmp(subval, p, vlen) >= 0)) { + p = q1 + 1; + sublen -= (sublen1 + 1); + if ((vlen < sublen) + || (vlen == sublen && strncmp(subval, p, vlen) <= 0)) { return true; } } } - else if (vlen == explen && !strncmp(p, val, vlen)) { + else if (vlen == sublen && !strncmp(p, subval, vlen)) { return true; } } + next_expr: if (q) p = q + 1; } while (q); @@ -348,6 +404,7 @@ static void AVal_match(const FingerTest &reference, const FingerTest &fprint, co return; const std::vector &pointsV = points.Attrs; + bool tcp_opt_match = points.name == "OPS"; const std::vector &refV = *reference.results; assert(refV.size() == points.numAttrs); @@ -366,7 +423,7 @@ static void AVal_match(const FingerTest &reference, const FingerTest &fprint, co 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, 0, current_ref, 0)) { + if (expr_match(current_fp, 0, current_ref, 0, tcp_opt_match || aDef.name == "O")) { subtests_succeeded += pointsThisTest; } else { if (verbose) @@ -1072,6 +1129,15 @@ fparse: if (DB->MatchPoints) fatal("Found MatchPoints directive on line %d of %s even though it has previously been seen in the file", lineno, fname); parsingMatchPoints = true; + } else if (strncmp(line, "This nmap-os-db", 15) == 0) { + p = strstr(line, "Nmap "); + if (!p) + fatal("Parse error on line %d of nmap-os-db file: %s", lineno, line); + q = strchr(p + 5, ' '); + if (strncmp(p + 5, NMAP_NUM_VERSION, q - p) > 0) { + error("%sOS detection results may be inaccurate.", line); + } + continue; } else { error("Parse error on line %d of nmap-os-db file: %s", lineno, line); continue; diff --git a/tests/expr_match_test.cc b/tests/expr_match_test.cc index 390914080..f9069a1c9 100644 --- a/tests/expr_match_test.cc +++ b/tests/expr_match_test.cc @@ -75,7 +75,6 @@ const struct expr_test tests[] = { {"A", "", false}, {"1-9", "", false}, -#if 0 {"M[1-9]", "M2", true}, {"M[<5]S", "M2S", true}, {"M[>A]S", "MFS", true}, @@ -116,7 +115,6 @@ const struct expr_test tests[] = { {"[<5]S", "2B", false}, {"[>A7]S", "FS", false}, {"[>A7]S", "A6S", false}, -#endif {"", "", true} };