mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
688 lines
18 KiB
C++
688 lines
18 KiB
C++
// $Id: OSFinger.cc 5857 2008-06-26 23:00:03Z vern $
|
|
|
|
/*
|
|
Taken with permission from:
|
|
|
|
p0f - passive OS fingerprinting (GNU LESSER GENERAL PUBLIC LICENSE)
|
|
-------------------------------------------------------------------
|
|
|
|
"If you sit down at a poker game and don't see a sucker,
|
|
get up. You're the sucker."
|
|
|
|
(C) Copyright 2000-2003 by Michal Zalewski <lcamtuf@coredump.cx>
|
|
*/
|
|
|
|
// To make it easier to upgrade this file to newer releases of p0f,
|
|
// it remains in the coding style used by p0f rather than Bro.
|
|
|
|
#include "OSFinger.h"
|
|
#include "net_util.h"
|
|
#include "util.h"
|
|
#include "Var.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
|
|
void int_delete_func(void* v)
|
|
{
|
|
delete (int*) v;
|
|
}
|
|
|
|
|
|
// Initializes data structures for fingerprinting in the given mode.
|
|
OSFingerprint::OSFingerprint(FingerprintMode arg_mode)
|
|
{
|
|
err = 0;
|
|
mode = arg_mode;
|
|
|
|
sigcnt=gencnt=0;
|
|
problems=0;
|
|
char* fname;
|
|
|
|
memset(sig, 0, sizeof(struct fp_entry)*MAXSIGS);
|
|
memset(bh, 0, sizeof(struct fp_entry*)*OSHSIZE);
|
|
|
|
os_matches.SetDeleteFunc(int_delete_func);
|
|
|
|
if (mode == SYN_FINGERPRINT_MODE)
|
|
{
|
|
fname = copy_string(internal_val("passive_fingerprint_file")->AsString()->CheckString());
|
|
load_config(fname);
|
|
delete [] fname;
|
|
}
|
|
else if (mode == SYN_ACK_FINGERPRINT_MODE)
|
|
{//not yet supported
|
|
load_config("p0fsynack.sig");
|
|
}
|
|
else if (mode == RST_FINGERPRINT_MODE)
|
|
{//not yet supported
|
|
load_config("p0frst.sig");
|
|
}
|
|
else
|
|
{
|
|
Error("OS fingerprinting: unknown mode!");
|
|
}
|
|
}
|
|
|
|
bool OSFingerprint::CacheMatch(uint32 addr, int id)
|
|
{
|
|
HashKey key = HashKey(&addr, 1);
|
|
int* pid = new int;
|
|
*pid=id;
|
|
int* prev = os_matches.Insert(&key, pid);
|
|
bool ret = (prev ? *prev != id : 1);
|
|
if (prev)
|
|
delete prev;
|
|
return ret;
|
|
}
|
|
|
|
|
|
// Determines whether the signature file had any collisions.
|
|
void OSFingerprint::collide(uint32 id)
|
|
{
|
|
uint32 i,j;
|
|
uint32 cur;
|
|
|
|
if (sig[id].ttl % 32 && sig[id].ttl != 255 && sig[id].ttl % 30)
|
|
{
|
|
problems=1;
|
|
warn(fmt("OS fingerprinting: [!] Unusual TTL (%d) for signature '%s %s' (line %d).",
|
|
sig[id].ttl,sig[id].os,sig[id].desc,sig[id].line));
|
|
}
|
|
|
|
for (i=0;i<id;i++)
|
|
{
|
|
if (!strcmp(sig[i].os,sig[id].os) &&
|
|
!strcmp(sig[i].desc,sig[id].desc)) {
|
|
problems=1;
|
|
warn(fmt("OS fingerprinting: [!] Duplicate signature name: '%s %s' (line %d and %d).",
|
|
sig[i].os,sig[i].desc,sig[i].line,sig[id].line));
|
|
}
|
|
|
|
/* If TTLs are sufficiently away from each other, the risk of
|
|
a collision is lower. */
|
|
if (abs((int)sig[id].ttl - (int)sig[i].ttl) > 25) continue;
|
|
|
|
if (sig[id].df ^ sig[i].df) continue;
|
|
if (sig[id].zero_stamp ^ sig[i].zero_stamp) continue;
|
|
|
|
/* Zero means >= PACKET_BIG */
|
|
if (sig[id].size) { if (sig[id].size ^ sig[i].size) continue; }
|
|
else if (sig[i].size < PACKET_BIG) continue;
|
|
|
|
if (sig[id].optcnt ^ sig[i].optcnt) continue;
|
|
if (sig[id].quirks ^ sig[i].quirks) continue;
|
|
|
|
switch (sig[id].wsize_mod) {
|
|
|
|
case 0: /* Current: const */
|
|
|
|
cur=sig[id].wsize;
|
|
|
|
do_const:
|
|
|
|
switch (sig[i].wsize_mod) {
|
|
|
|
case 0: /* Previous is also const */
|
|
|
|
/* A problem if values match */
|
|
if (cur ^ sig[i].wsize) continue;
|
|
break;
|
|
|
|
case MOD_CONST: /* Current: const, prev: modulo (or *) */
|
|
|
|
/* A problem if current value is a multiple of that modulo */
|
|
if (cur % sig[i].wsize) continue;
|
|
break;
|
|
|
|
case MOD_MSS: /* Current: const, prev: mod MSS */
|
|
|
|
if (sig[i].mss_mod || sig[i].wsize *
|
|
(sig[i].mss ? sig[i].mss : 1460 ) != (int) cur)
|
|
continue;
|
|
|
|
break;
|
|
|
|
case MOD_MTU: /* Current: const, prev: mod MTU */
|
|
|
|
if (sig[i].mss_mod || sig[i].wsize * (
|
|
(sig[i].mss ? sig[i].mss : 1460 )+40) != (int) cur)
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1: /* Current signature is modulo something */
|
|
|
|
/* A problem only if this modulo is a multiple of the
|
|
previous modulo */
|
|
|
|
if (sig[i].wsize_mod != MOD_CONST) continue;
|
|
if (sig[id].wsize % sig[i].wsize) continue;
|
|
|
|
break;
|
|
|
|
case MOD_MSS: /* Current is modulo MSS */
|
|
|
|
/* There's likely a problem only if the previous one is close
|
|
to '*'; we do not check known MTUs, because this particular
|
|
signature can be made with some uncommon MTUs in mind. The
|
|
problem would also appear if current signature has a fixed
|
|
MSS. */
|
|
|
|
if (sig[i].wsize_mod != MOD_CONST || sig[i].wsize >= 8) {
|
|
if (!sig[id].mss_mod) {
|
|
cur = (sig[id].mss ? sig[id].mss : 1460 ) * sig[id].wsize;
|
|
goto do_const;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
|
|
case MOD_MTU: /* Current is modulo MTU */
|
|
|
|
if (sig[i].wsize_mod != MOD_CONST || sig[i].wsize <= 8) {
|
|
if (!sig[id].mss_mod) {
|
|
cur = ( (sig[id].mss ? sig[id].mss : 1460 ) +40) * sig[id].wsize;
|
|
goto do_const;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Same for wsc */
|
|
switch (sig[id].wsc_mod) {
|
|
|
|
case 0: /* Current: const */
|
|
|
|
cur=sig[id].wsc;
|
|
|
|
switch (sig[i].wsc_mod) {
|
|
|
|
case 0: /* Previous is also const */
|
|
|
|
/* A problem if values match */
|
|
if (cur ^ sig[i].wsc) continue;
|
|
break;
|
|
|
|
case 1: /* Current: const, prev: modulo (or *) */
|
|
|
|
/* A problem if current value is a multiple of that modulo */
|
|
if (cur % sig[i].wsc) continue;
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MOD_CONST: /* Current signature is modulo something */
|
|
|
|
/* A problem only if this modulo is a multiple of the
|
|
previous modulo */
|
|
|
|
if (!sig[i].wsc_mod) continue;
|
|
if (sig[id].wsc % sig[i].wsc) continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Same for mss */
|
|
switch (sig[id].mss_mod) {
|
|
|
|
case 0: /* Current: const */
|
|
|
|
cur=sig[id].mss;
|
|
|
|
switch (sig[i].mss_mod) {
|
|
|
|
case 0: /* Previous is also const */
|
|
|
|
/* A problem if values match */
|
|
if (cur ^ sig[i].mss) continue;
|
|
break;
|
|
|
|
case 1: /* Current: const, prev: modulo (or *) */
|
|
|
|
/* A problem if current value is a multiple of that modulo */
|
|
if (cur % sig[i].mss) continue;
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MOD_CONST: /* Current signature is modulo something */
|
|
|
|
/* A problem only if this modulo is a multiple of the
|
|
previous modulo */
|
|
|
|
if (!sig[i].mss_mod) continue;
|
|
if ((sig[id].mss ? sig[id].mss : 1460 ) %
|
|
(sig[i].mss ? sig[i].mss : 1460 )) continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Now check option sequence */
|
|
|
|
for (j=0;j<sig[id].optcnt;j++)
|
|
if (sig[id].opt[j] ^ sig[i].opt[j]) goto reloop;
|
|
|
|
problems=1;
|
|
warn(fmt("OS fingerprinting: [!] Signature '%s %s' (line %d)\n"
|
|
" is already covered by '%s %s' (line %d).",
|
|
sig[id].os,sig[id].desc,sig[id].line,sig[i].os,sig[i].desc,
|
|
sig[i].line));
|
|
|
|
reloop:
|
|
;
|
|
}
|
|
}
|
|
|
|
// Loads a given file into to classes data structures.
|
|
void OSFingerprint::load_config(const char* file)
|
|
{
|
|
uint32 ln=0;
|
|
char buf[MAXLINE];
|
|
char* p;
|
|
FILE* c = search_for_file( file, "osf", 0);
|
|
|
|
if (!c)
|
|
{
|
|
Error("Can't open OS passive fingerprinting signature file", file);
|
|
return;
|
|
}
|
|
sigcnt=0; //every time we read config we reset it to 0;
|
|
while ((p=fgets(buf,sizeof(buf),c)))
|
|
{
|
|
uint32 l;
|
|
|
|
char obuf[MAXLINE],genre[MAXLINE],desc[MAXLINE],quirks[MAXLINE];
|
|
char w[MAXLINE],sb[MAXLINE];
|
|
char* gptr = genre;
|
|
uint32 t,d,s;
|
|
struct fp_entry* e;
|
|
|
|
ln++;
|
|
|
|
/* Remove leading and trailing blanks */
|
|
while (isspace(*p)) p++;
|
|
l=strlen(p);
|
|
while (l && isspace(*(p+l-1))) *(p+(l--)-1)=0;
|
|
|
|
/* Skip empty lines and comments */
|
|
if (!l) continue;
|
|
if (*p == '#') continue;
|
|
|
|
if (sscanf(p,"%[0-9%*()ST]:%d:%d:%[0-9()*]:%[^:]:%[^ :]:%[^:]:%[^:]",
|
|
w, &t,&d,sb, obuf, quirks,genre,desc) != 8)
|
|
Error("OS fingerprinting: Syntax error in p0f signature config line %d.\n",(uint32)ln);
|
|
|
|
gptr = genre;
|
|
|
|
if (*sb != '*') s = atoi(sb); else s = 0;
|
|
|
|
reparse_ptr:
|
|
|
|
switch (*gptr)
|
|
{
|
|
case '-': sig[sigcnt].userland = 1; gptr++; goto reparse_ptr;
|
|
case '*': sig[sigcnt].no_detail = 1; gptr++; goto reparse_ptr;
|
|
case '@': sig[sigcnt].generic = 1; gptr++; gencnt++; goto reparse_ptr;
|
|
case 0: Error("OS fingerprinting: Empty OS genre in line",(uint32)ln);
|
|
}
|
|
|
|
sig[sigcnt].os = strdup(gptr);
|
|
sig[sigcnt].desc = strdup(desc);
|
|
sig[sigcnt].ttl = t;
|
|
sig[sigcnt].size = s;
|
|
sig[sigcnt].df = d;
|
|
|
|
if (w[0] == '*')
|
|
{
|
|
sig[sigcnt].wsize = 1;
|
|
sig[sigcnt].wsize_mod = MOD_CONST;
|
|
}
|
|
else if (tolower(w[0]) == 's')
|
|
{
|
|
sig[sigcnt].wsize_mod = MOD_MSS;
|
|
if (!isdigit(*(w+1)))
|
|
Error("OS fingerprinting: Bad Snn value in WSS in line",(uint32)ln);
|
|
sig[sigcnt].wsize = atoi(w+1);
|
|
}
|
|
else if (tolower(w[0]) == 't')
|
|
{
|
|
sig[sigcnt].wsize_mod = MOD_MTU;
|
|
if (!isdigit(*(w+1)))
|
|
Error("OS fingerprinting: Bad Tnn value in WSS in line",(uint32)ln);
|
|
sig[sigcnt].wsize = atoi(w+1);
|
|
}
|
|
else if (w[0] == '%')
|
|
{
|
|
if (!(sig[sigcnt].wsize = atoi(w+1)))
|
|
Error("OS fingerprinting: Null modulo for window size in config line",(uint32)ln);
|
|
sig[sigcnt].wsize_mod = MOD_CONST;
|
|
}
|
|
else
|
|
sig[sigcnt].wsize = atoi(w);
|
|
|
|
/* Now let's parse options */
|
|
|
|
p=obuf;
|
|
|
|
sig[sigcnt].zero_stamp = 1;
|
|
|
|
if (*p=='.') p++;
|
|
|
|
while (*p)
|
|
{
|
|
uint8 optcnt = sig[sigcnt].optcnt;
|
|
switch (tolower(*p))
|
|
{
|
|
case 'n': sig[sigcnt].opt[optcnt] = TCPOPT_NOP;
|
|
break;
|
|
|
|
case 'e': sig[sigcnt].opt[optcnt] = TCPOPT_EOL;
|
|
if (*(p+1))
|
|
Error("OS fingerprinting: EOL not the last option, line",(uint32)ln);
|
|
break;
|
|
|
|
case 's': sig[sigcnt].opt[optcnt] = TCPOPT_SACK_PERMITTED;
|
|
break;
|
|
|
|
case 't': sig[sigcnt].opt[optcnt] = TCPOPT_TIMESTAMP;
|
|
if (*(p+1)!='0')
|
|
{
|
|
sig[sigcnt].zero_stamp=0;
|
|
if (isdigit(*(p+1)))
|
|
Error("OS fingerprinting: Bogus Tstamp specification in line",(uint32)ln);
|
|
}
|
|
break;
|
|
|
|
case 'w': sig[sigcnt].opt[optcnt] = TCPOPT_WINDOW;
|
|
if (p[1] == '*')
|
|
{
|
|
sig[sigcnt].wsc = 1;
|
|
sig[sigcnt].wsc_mod = MOD_CONST;
|
|
}
|
|
else if (p[1] == '%')
|
|
{
|
|
if (!(sig[sigcnt].wsc = atoi(p+2)))
|
|
Error("OS fingerprinting: Null modulo for wscale in config line",(uint32)ln);
|
|
sig[sigcnt].wsc_mod = MOD_CONST;
|
|
}
|
|
else if (!isdigit(*(p+1)))
|
|
Error("OS fingerprinting: Incorrect W value in line",(uint32)ln);
|
|
else sig[sigcnt].wsc = atoi(p+1);
|
|
break;
|
|
|
|
case 'm': sig[sigcnt].opt[optcnt] = TCPOPT_MAXSEG;
|
|
if (p[1] == '*')
|
|
{
|
|
sig[sigcnt].mss = 1;
|
|
sig[sigcnt].mss_mod = MOD_CONST;
|
|
}
|
|
else if (p[1] == '%')
|
|
{
|
|
if (!(sig[sigcnt].mss = atoi(p+2)))
|
|
Error("OS fingerprinting: Null modulo for MSS in config line",(uint32)ln);
|
|
sig[sigcnt].mss_mod = MOD_CONST;
|
|
}
|
|
else if (!isdigit(*(p+1)))
|
|
Error("OS fingerprinting: Incorrect M value in line",(uint32)ln);
|
|
else sig[sigcnt].mss = atoi(p+1);
|
|
break;
|
|
|
|
/* Yuck! */
|
|
case '?': if (!isdigit(*(p+1)))
|
|
Error("OS fingerprinting: Bogus ?nn value in line",(uint32)ln);
|
|
else sig[sigcnt].opt[optcnt] = atoi(p+1);
|
|
break;
|
|
|
|
default: Error("OS fingerprinting: Unknown TCP option in config line",(uint32)ln);
|
|
}
|
|
|
|
if (++sig[sigcnt].optcnt >= MAXOPT)
|
|
Error("OS fingerprinting: Too many TCP options specified in config line",(uint32)ln);
|
|
|
|
/* Skip separators */
|
|
do { p++; } while (*p && !isalpha(*p) && *p != '?');
|
|
|
|
}
|
|
|
|
sig[sigcnt].line = ln;
|
|
|
|
p = quirks;
|
|
|
|
while (*p)
|
|
switch (toupper(*(p++)))
|
|
{
|
|
case 'E':
|
|
Error("OS fingerprinting: Quirk 'E' is obsolete. Remove it, append E to the options. Line",(uint32)ln);
|
|
|
|
case 'K':
|
|
if ( mode != RST_FINGERPRINT_MODE )
|
|
Error("OS fingerprinting: Quirk 'K' is valid only in RST+ (-R) mode (wrong config file?). Line",(uint32)ln);
|
|
sig[sigcnt].quirks |= QUIRK_RSTACK;
|
|
break;
|
|
|
|
case 'Q': sig[sigcnt].quirks |= QUIRK_SEQEQ; break;
|
|
case '0': sig[sigcnt].quirks |= QUIRK_SEQ0; break;
|
|
case 'P': sig[sigcnt].quirks |= QUIRK_PAST; break;
|
|
case 'Z': sig[sigcnt].quirks |= QUIRK_ZEROID; break;
|
|
case 'I': sig[sigcnt].quirks |= QUIRK_IPOPT; break;
|
|
case 'U': sig[sigcnt].quirks |= QUIRK_URG; break;
|
|
case 'X': sig[sigcnt].quirks |= QUIRK_X2; break;
|
|
case 'A': sig[sigcnt].quirks |= QUIRK_ACK; break;
|
|
case 'T': sig[sigcnt].quirks |= QUIRK_T2; break;
|
|
case 'F': sig[sigcnt].quirks |= QUIRK_FLAGS; break;
|
|
case 'D': sig[sigcnt].quirks |= QUIRK_DATA; break;
|
|
case '!': sig[sigcnt].quirks |= QUIRK_BROKEN; break;
|
|
case '.': break;
|
|
default: Error("OS fingerprinting: Bad quirk in line",(uint32)ln);
|
|
}
|
|
|
|
e = bh[SIGHASH(s,sig[sigcnt].optcnt,sig[sigcnt].quirks,d)];
|
|
|
|
if (!e)
|
|
{
|
|
bh[SIGHASH(s,sig[sigcnt].optcnt,sig[sigcnt].quirks,d)] = &sig[sigcnt];
|
|
}
|
|
else
|
|
{
|
|
while (e->next) e = e->next;
|
|
e->next = &sig[sigcnt];
|
|
}
|
|
|
|
collide(sigcnt);
|
|
if (++sigcnt >= MAXSIGS)
|
|
Error("OS fingerprinting: Maximum signature count exceeded.\n");
|
|
|
|
}
|
|
|
|
fclose(c);
|
|
|
|
if (!sigcnt)
|
|
Error("OS fingerprinting: no signatures loaded from config file.");
|
|
|
|
}
|
|
|
|
// Does the actual match between the packet and the signature database.
|
|
// Modifies retval and contains OS Type and other useful information.
|
|
// Returns config-file line of the matching signature as id.
|
|
int OSFingerprint::FindMatch(struct os_type* retval, uint16 tot,uint8 df,
|
|
uint8 ttl,uint16 wss,uint8 ocnt,uint8* op,
|
|
uint16 mss,uint8 wsc,uint32 tstamp,
|
|
uint32 quirks,uint8 ecn) const
|
|
{
|
|
uint32 j; //used for counter in loops
|
|
struct fp_entry* p;
|
|
uint8 orig_df = df;
|
|
|
|
struct fp_entry* fuzzy = 0;
|
|
uint8 fuzzy_now = 0;
|
|
int id = 0; //return value: 0 indicates no match.
|
|
|
|
retval->os="UNKNOWN";
|
|
retval->desc=NULL;
|
|
retval->gadgets=0;
|
|
retval->match=0;
|
|
retval->uptime=0;
|
|
|
|
re_lookup:
|
|
|
|
p = bh[SIGHASH(tot,ocnt,quirks,df)];
|
|
|
|
while (p)
|
|
{
|
|
/* Cheap and specific checks first... */
|
|
/* psize set to zero means >= PACKET_BIG */
|
|
if (p->size) { if (tot ^ p->size) { p = p->next; continue; } }
|
|
else if (tot < PACKET_BIG) { p = p->next; continue; }
|
|
|
|
if (ocnt ^ p->optcnt) { p = p->next; continue; }
|
|
|
|
if (p->zero_stamp ^ (!tstamp)) { p = p->next; continue; }
|
|
if (p->df ^ df) { p = p->next; continue; }
|
|
if (p->quirks ^ quirks) { p = p->next; continue; }
|
|
|
|
/* Check MSS and WSCALE... */
|
|
if (!p->mss_mod) {
|
|
if (mss ^ p->mss) { p = p->next; continue; }
|
|
} else if (mss % p->mss) { p = p->next; continue; }
|
|
|
|
if (!p->wsc_mod) {
|
|
if (wsc ^ p->wsc) { p = p->next; continue; }
|
|
} else if (wsc % p->wsc) { p = p->next; continue; }
|
|
|
|
/* Then proceed with the most complex WSS check... */
|
|
switch (p->wsize_mod)
|
|
{
|
|
case 0:
|
|
if (wss ^ p->wsize) { p = p->next; continue; }
|
|
break;
|
|
case MOD_CONST:
|
|
if (wss % p->wsize) { p = p->next; continue; }
|
|
break;
|
|
case MOD_MSS:
|
|
if (mss && !(wss % mss))
|
|
{
|
|
if ((wss / mss) ^ p->wsize) { p = p->next; continue; }
|
|
}
|
|
else if (!(wss % 1460))
|
|
{
|
|
if ((wss / 1460) ^ p->wsize) { p = p->next; continue; }
|
|
}
|
|
else { p = p->next; continue; }
|
|
break;
|
|
case MOD_MTU:
|
|
if (mss && !(wss % (mss+40)))
|
|
{
|
|
if ((wss / (mss+40)) ^ p->wsize) { p = p->next; continue; }
|
|
}
|
|
else if (!(wss % 1500))
|
|
{
|
|
if ((wss / 1500) ^ p->wsize) { p = p->next; continue; }
|
|
}
|
|
else { p = p->next; continue; }
|
|
break;
|
|
}
|
|
|
|
/* Numbers agree. Let's check options */
|
|
for (j=0;j<ocnt;j++)
|
|
if (p->opt[j] ^ op[j]) goto continue_search;
|
|
|
|
/* Check TTLs last because we might want to go fuzzy. */
|
|
if (p->ttl < ttl)
|
|
{
|
|
if ( mode != RST_FINGERPRINT_MODE )fuzzy = p;
|
|
p = p->next;
|
|
continue;
|
|
}
|
|
|
|
/* Naah... can't happen ;-) */
|
|
if (!p->no_detail)
|
|
if (p->ttl - ttl > MAXDIST)
|
|
{
|
|
if (mode != RST_FINGERPRINT_MODE ) fuzzy = p;
|
|
p = p->next;
|
|
continue;
|
|
}
|
|
|
|
continue_fuzzy:
|
|
|
|
/* Match! */
|
|
id = p->line;
|
|
if (mss & wss)
|
|
{
|
|
if (p->wsize_mod == MOD_MSS)
|
|
{
|
|
if ((wss % mss) && !(wss % 1460)) retval->gadgets|=GADGETNAT;
|
|
}
|
|
else if (p->wsize_mod == MOD_MTU)
|
|
{
|
|
if ((wss % (mss+40)) && !(wss % 1500)) retval->gadgets|=GADGETNAT2;
|
|
}
|
|
}
|
|
|
|
retval->os=p->os;
|
|
retval->desc=p->desc;
|
|
retval->dist=p->ttl-ttl;
|
|
|
|
if (ecn) retval->gadgets|=GADGETECN;
|
|
if (orig_df ^ df) retval->gadgets|=GADGETFIREWALL;
|
|
|
|
if (p->generic) retval->match=MATCHGENERIC;
|
|
if (fuzzy_now) retval->match=MATCHFUZZY;
|
|
|
|
if (!p->no_detail && tstamp)
|
|
{
|
|
retval->uptime=tstamp/360000;
|
|
retval->gadgets|=GADGETUPTIME;
|
|
}
|
|
|
|
return id;
|
|
|
|
continue_search:
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
if (!df) { df = 1; goto re_lookup; } //not found with df=0 do df=1
|
|
|
|
if (fuzzy)
|
|
{
|
|
df = orig_df;
|
|
fuzzy_now = 1;
|
|
p = fuzzy;
|
|
fuzzy = 0;
|
|
goto continue_fuzzy;
|
|
}
|
|
|
|
if (mss & wss)
|
|
{
|
|
if ((wss % mss) && !(wss % 1460)) retval->gadgets|=GADGETNAT;
|
|
else if ((wss % (mss+40)) && !(wss % 1500)) retval->gadgets|=GADGETNAT2;
|
|
}
|
|
|
|
if (ecn) retval->gadgets|=GADGETECN;
|
|
|
|
if (tstamp)
|
|
{
|
|
retval->uptime=tstamp/360000;
|
|
retval->gadgets|=GADGETUPTIME;
|
|
}
|
|
|
|
return id;
|
|
}
|