/* $Id: synos.c,v 1.4 2005/12/09 07:04:07 dm Exp $ */ /* * * Copyright (C) 2004 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 "util/synos.h" u_int32_t synos_mtu = 1500; void synos_clearfp (fpdat_t *fpp) { VECTOR_CLEAR (&fpp->fp_opts); VECTOR_INIT (&fpp->fp_opts); } void synos_clearos (osdat_t *odp) { free (odp->od_name); odp->od_name = NULL; VECTOR_CLEAR (&odp->od_opts); VECTOR_INIT (&odp->od_opts); } int synos_check (const fpdat_t *fpp, const osdat_t *odp) { int i; u_int32_t mss = 0; int mss_valid = 0; if (fpp->fp_ttl > odp->od_ttl || fpp->fp_ttl + 40 < odp->od_ttl) return 0; if (fpp->fp_df != odp->od_df) return 0; if (fpp->fp_size != odp->od_size) return 0; if (fpp->fp_opts.v_size != odp->od_opts.v_size) return 0; for (i = 0; i < fpp->fp_opts.v_size; i++) { switch (odp->od_opts.v_vec[i].o_type) { case OPT_M: if (fpp->fp_opts.v_vec[i].o_type != OPT_M) return 0; mss = fpp->fp_opts.v_vec[i].o_val; mss_valid = 1; if (mss != odp->od_opts.v_vec[i].o_val) return 0; break; case OPT_MMOD: if (fpp->fp_opts.v_vec[i].o_type != OPT_M) return 0; mss = fpp->fp_opts.v_vec[i].o_val; mss_valid = 1; if (mss % odp->od_opts.v_vec[i].o_val) return 0; break; case OPT_MSTAR: if (fpp->fp_opts.v_vec[i].o_type != OPT_M) return 0; mss = fpp->fp_opts.v_vec[i].o_val; mss_valid = 1; break; default: if (odp->od_opts.v_vec[i].o_type != fpp->fp_opts.v_vec[i].o_type) return 0; break; } } switch (odp->od_wintype) { case WIN_ANY: break; case WIN_EQ: if (fpp->fp_win != odp->od_win) return 0; break; case WIN_S: if ((!synos_mtu || fpp->fp_win != odp->od_win * (synos_mtu - 40)) && (!mss_valid || fpp->fp_win != odp->od_win * mss)) return 0; break; case WIN_T: if ((!synos_mtu || fpp->fp_win != odp->od_win * synos_mtu) && (!mss_valid || fpp->fp_win != odp->od_win * (mss + 40))) return 0; case WIN_MOD: if (fpp->fp_win % odp->od_win) return 0; break; } return 1; } int synos_parsefp (fpdat_t *fpp, const char *fps) { int n; const char *s = fps; VECTOR_CLEAR (&fpp->fp_opts); VECTOR_INIT (&fpp->fp_opts); if (sscanf (s, "%u:%u:%d:%d%n", &fpp->fp_win, &fpp->fp_ttl, &fpp->fp_df, &fpp->fp_size, &n) != 4) return 0; s += n; for (;;) { tcpopt_t *opp; if (!*s) return 1; if (*s != ':' && *s != ',') return 0; s++; opp = VECTOR_NEXT (&fpp->fp_opts); switch (*s++) { case 'N': opp->o_type = OPT_N; break; case 'W': if (sscanf (s, "%u%n", &opp->o_val, &n) != 1) return 0; opp->o_type = OPT_W; s += n; break; case 'M': if (sscanf (s, "%u%n", &opp->o_val, &n) != 1) return 0; opp->o_type = OPT_M; s += n; break; case 'S': opp->o_type = OPT_S; break; case 'T': if (*s == '0') { opp->o_type = OPT_T0; s++; } else opp->o_type = OPT_T; break; default: return 0; } } } int synos_parseos (osdat_t *odp, const char *line) { int n; const char *s = line; int begin = 1; if (!*s || *s == '#' || *s == '\n') return -1; VECTOR_CLEAR (&odp->od_opts); VECTOR_INIT (&odp->od_opts); free (odp->od_name); odp->od_name = NULL; if (*s == '*') { odp->od_wintype = WIN_ANY; s++; } else { if (*s == '%') odp->od_wintype = WIN_MOD; else if (*s == 'S') odp->od_wintype = WIN_S; else if (*s == 'T') odp->od_wintype = WIN_T; else { odp->od_wintype = WIN_EQ; s--; } s++; if (sscanf (s, "%u%n", &odp->od_win, &n) != 1) return 0; s += n; } if (sscanf (s, ":%u:%d:%d:%n", &odp->od_ttl, &odp->od_df, &odp->od_size, &n) != 3) return 0; s += n - 1; for (;;) { tcpopt_t *opp; if (!*s) return 0; if (*s == ':' && !begin) break; if (*s != ':' && *s != ',') return 0; s++; if (begin && *s == '.') { s++; break; } begin = 0; opp = VECTOR_NEXT (&odp->od_opts); switch (*s++) { case 'N': opp->o_type = OPT_N; break; case 'W': if (*s == '*') { s++; opp->o_type = OPT_WSTAR; break; } if (*s == '%') { opp->o_type = OPT_WMOD; s++; } else opp->o_type = OPT_W; if (sscanf (s, "%u%n", &opp->o_val, &n) != 1) return 0; s += n; break; case 'M': if (*s == '*') { s++; opp->o_type = OPT_MSTAR; break; } if (*s == '%') { opp->o_type = OPT_MMOD; s++; } else opp->o_type = OPT_M; if (sscanf (s, "%u%n", &opp->o_val, &n) != 1) return 0; s += n; break; case 'S': opp->o_type = OPT_S; break; case 'T': if (*s == '0') { opp->o_type = OPT_T0; s++; } else opp->o_type = OPT_T; break; default: return 0; } } if ((s = strrchr (s, ':'))) { odp->od_name = xstrdup (s + 1); n = strlen (odp->od_name); if (n > 0 && odp->od_name[n - 1] == '\n') odp->od_name[n - 1] = '\0'; return 1; } return 0; } #ifdef AVUTIL_MAIN #include "getopt_long.h" char *progname; const char *opt_fpdb = DATADIR "/pf.os"; int opt_verbose; static void usage (void) __attribute__ ((noreturn)); static void usage (void) { fprintf (stderr, "usage: %s [--mtu MTU] [--db FPDB] fingerprint\n", progname); exit (1); } static int lookup (const char *fps) { fpdat_t fp; osdat_t os; FILE *f; struct lnbuf buf; int line = 0; bzero (&fp, sizeof (fp)); bzero (&os, sizeof (os)); bzero (&buf, sizeof (buf)); if (!synos_parsefp (&fp, fps)) { fprintf (stderr, "bad fingerprint '%s'\n", fps); synos_clearfp (&fp); return 1; } if (!(f = fopen (opt_fpdb, "r"))) { perror (opt_fpdb); exit (1); } while (readln (&buf, f, (size_t) -1) == LNBUF_OK) { int r = synos_parseos (&os, buf.buf); line++; if (r < 0) continue; if (r == 0) { fprintf (stderr, "%s:%d: syntax error\n", opt_fpdb, line); continue; } if (synos_check (&fp, &os)) { if (opt_verbose) printf ("%40s %s\n", fps, os.od_name); else printf ("%s\n", os.od_name); synos_clearfp (&fp); synos_clearos (&os); fclose (f); return 0; } } if (opt_verbose) printf ("%40s unknown\n", fps); synos_clearfp (&fp); synos_clearos (&os); fclose (f); return 1; } int main (int argc, char **argv) { struct option o[] = { { "version", no_argument, NULL, 256 + 'v' }, { "mtu", required_argument, NULL, 'm' }, { "db", required_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; int c; int opt_fpdb_set = 0; progname = strrchr (argv[0], '/'); if (progname) progname++; else progname = argv[0]; while ((c = getopt_long (argc, argv, "+v", o, NULL)) != -1) switch (c) { case 'd': opt_fpdb = optarg; opt_fpdb_set = 1; break; case 'm': synos_mtu = atoi (optarg); if ((synos_mtu && synos_mtu < 40) || synos_mtu >= 0x10000) usage (); case 'v': opt_verbose = 1; break; case 256 + 'v': version (progname, 1); break; default: usage (); break; } if (!opt_verbose && optind + 1 != argc) usage (); if (!opt_fpdb_set && access (opt_fpdb, 0) && !access ("/etc/pf.os", 0)) opt_fpdb = "/etc/pf.os"; while (optind < argc) lookup (argv[optind++]); return 0; } #endif /* AVUTIL_MAIN */