/* $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 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 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 > 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 ol = ostab[ln]; if (!ol) { ol = New refcounted; 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; }