/* $Id: osguess.C,v 1.5 2006/04/05 18:43:41 dm Exp $ */
/*
*
* Copyright (C) 2003 David Mazieres (dm@uun.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*/
#include "asmtpd.h"
#include "qhash.h"
#include "list.h"
#include "util/synos.h"
struct oscell : public osdat {
private:
oscell (const oscell &);
oscell &operator= (const oscell &);
public:
tailq_entry<oscell> link;
oscell () { bzero ((osdat_t *) this, sizeof (osdat_t)); }
~oscell () { synos_clearos (this); }
};
struct oslist {
private:
oslist (const oslist &);
oslist &operator= (const oslist &);
public:
tailq<oscell, &oscell::link> q;
oslist () {}
~oslist ();
oscell *lookup (const fpdat_t *fpp);
void insert (oscell *oc) { q.insert_tail (oc); }
};
static u_int64_t ostab_initno;
static qhash<u_int, ref<oslist> > ostab;
oslist::~oslist ()
{
while (oscell *oc = q.first) {
q.remove (oc);
delete oc;
}
}
oscell *
oslist::lookup (const fpdat_t *fpp)
{
for (oscell *oc = q.first; oc; oc = q.next (oc))
if (synos_check (fpp, oc))
return oc;
return NULL;
}
inline char
optletter (int type)
{
switch (type) {
case tcpopt::OPT_M:
case tcpopt::OPT_MMOD:
case tcpopt::OPT_MSTAR:
return 'M';
case tcpopt::OPT_N:
return 'N';
case tcpopt::OPT_T:
return 'T';
case tcpopt::OPT_T0:
return '0';
case tcpopt::OPT_S:
return 'S';
case tcpopt::OPT_W:
case tcpopt::OPT_WMOD:
case tcpopt::OPT_WSTAR:
return 'W';
default:
panic ("bad TCP option type %d\n", type);
return 0;
}
}
static u_int
hash_fixed (bool df, u_int16_t size, const tcpopt_t *opts, u_int nopts)
{
char c = df;
u_int seed = hash_bytes (&c, 1);
seed = hash_bytes (&size, 2, seed);
for (u_int i = 0; i < nopts; i++) {
c = optletter (opts[i].o_type);
seed = hash_bytes (&c, 1, seed);
}
return seed;
}
static bool
ostab_init ()
{
ostab.clear ();
ostab_initno = 0;
int fd = open (path_pfos, O_RDONLY);
if (fd < 0) {
warn ("%s: %m\n", path_pfos.cstr ());
ostab_initno = opt->configno;
return false;
}
suio buf;
u_int lineno = 0, nfp = 0;
while (buf.input (fd) > 0)
while (str line = suio_getline (&buf)) {
lineno++;
if (!line.len () || line[0] == '#')
continue;
oscell *oc = New oscell;
if (synos_parseos (oc, line)) {
u_int ln = hash_fixed (oc->od_df, oc->od_size,
oc->od_opts.v_vec, oc->od_opts.v_size);
ptr<oslist> ol = ostab[ln];
if (!ol) {
ol = New refcounted<oslist>;
ostab.insert (ln, ol);
}
ol->insert (oc);
nfp++;
}
else {
warn << path_pfos << ":" << lineno << " bad fingerprint template\n";
delete oc;
}
}
warn ("%s: %d fingerprints, %ld buckets\n", path_pfos.cstr (),
nfp, long (ostab.size ()));
ostab_initno = opt->configno;
return true;
}
const char *
synos_guess (str synfp)
{
if (!synfp)
return NULL;
fpdat_t fp;
bzero (&fp, sizeof (fp));
if (!synos_parsefp (&fp, synfp)) {
warn << "bad syn fingerprint `" << synfp << "'\n";
synos_clearfp (&fp);
return NULL;
}
if (ostab_initno != opt->configno) {
synos_mtu = opt->osguess_mtu;
ostab_init ();
}
const char *ret = NULL;
u_int ln = hash_fixed (fp.fp_df, fp.fp_size,
fp.fp_opts.v_vec, fp.fp_opts.v_size);
if (oslist *ol = ostab[ln])
if (oscell *oc = ol->lookup (&fp))
ret = oc->od_name;
synos_clearfp (&fp);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1