/*
* Copyright (c) 2002, 2003, 2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* 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 of the License, 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
* 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 of the License, 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 <sys/param.h>
#include <sys/types.h>
#include "config.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/stat.h>
#include <sys/tree.h>
#include <sys/queue.h>
#include <sys/wait.h>
#include <math.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <dnet.h>
#include <ctype.h>
#undef timeout_pending
#undef timeout_initialized
#include <event.h>
#include "honeyd.h"
#include "personality.h"
#include "xprobe_assoc.h"
#include "template.h"
#include "debug.h"
//#define DEBUG_XPROBE_STRUCT
/* ET - Moved SPLAY_HEAD to personality.h so xprobe_assoc.c could use it. */
int npersons;
/* ET - global from honeyd.c */
struct personate person_drop = {};
static struct event personality_time_ev;
static struct timeval tv_periodic;
SPLAY_GENERATE(perstree, personality, node, perscompare);
/* ET - For the Xprobe fingerprint tree */
SPLAY_GENERATE(xp_fprint_tree, xp_fingerprint, node, xp_fprint_compare);
static void
personality_time_evcb(int fd, short what, void *arg)
{
struct event *ev = arg;
struct timeval tv;
gettimeofday(&tv_periodic, NULL);
timerclear(&tv);
tv.tv_usec = 100000; /* every 100 ms */
evtimer_add(ev, &tv);
}
void
xprobe_personality_init(void)
{
SPLAY_INIT(&xp_fprints);
}
void
personality_init(void)
{
npersons = 0;
SPLAY_INIT(&personalities);
/* Start a timer that keeps track of the current system time */
evtimer_set(&personality_time_ev,
personality_time_evcb, &personality_time_ev);
personality_time_evcb(-1, EV_TIMEOUT, &personality_time_ev);
}
struct personality *
personality_new(const char *name)
{
struct personality *pers, tmp;
tmp.name = (char *)name;
if (SPLAY_FIND(perstree, &personalities, &tmp))
return (NULL);
if ((pers = calloc(1, sizeof(struct personality))) == NULL)
err(1, "%s: calloc", __FUNCTION__);
if ((pers->name = strdup(name)) == NULL)
err(1, "%s: stdup", __FUNCTION__);
/* Initialize defaults */
pers->tstamphz = -1;
npersons++;
SPLAY_INSERT(perstree, &personalities, pers);
/* Find and add the Xprobe fingerprint, if it exists */
correlate_nmap_with_xprobe(pers);
return (pers);
}
struct personality *
personality_clone(const struct personality *person)
{
struct personality *newperson;
if ((newperson = malloc(sizeof(struct personality))) == NULL)
err(1, "%s: malloc", __FUNCTION__);
memcpy(newperson, person, sizeof(struct personality));
return (newperson);
}
/*
* Frees the reference that a template has to a personality.
*/
void
personality_declone(struct personality *pers)
{
free(pers);
}
void
personality_free(struct personality *pers)
{
SPLAY_REMOVE(perstree, &personalities, pers);
free(pers->name);
free(pers);
}
struct personality *
personality_random(void)
{
extern rand_t *honeyd_rand;
struct personality *pers;
int i;
if (!npersons)
return (NULL);
i = rand_uint32(honeyd_rand) % npersons;
pers = SPLAY_MIN(perstree, &personalities);
while (i--) {
pers = SPLAY_NEXT(perstree, &personalities, pers);
}
return (pers);
}
struct personality *
personality_find(const char *name)
{
struct personality tmp;
tmp.name = (char *)name;
return (SPLAY_FIND(perstree, &personalities, &tmp));
}
/* Not much here, set up ip id accordingly */
void
ip_personality(struct template *tmpl, uint16_t *pid)
{
extern rand_t *honeyd_rand;
struct personality *person;
if (tmpl == NULL)
return;
if ((person = tmpl->person) == NULL)
return;
while (!tmpl->id)
tmpl->id = rand_uint16(honeyd_rand);
if (person->idt == ID_SEQUENTIAL)
*pid = tmpl->id++;
else if (person->idt == ID_SEQUENTIAL_BROKEN)
*pid = htons(tmpl->id++);
else if (person->idt == ID_ZERO)
*pid = 0;
else if (person->idt == ID_RPI) {
/* Apparently needs to be at least 1000 */
tmpl->id += 1000 + (rand_uint16(honeyd_rand) % 1024);
*pid = tmpl->id;
} else if (person->idt == ID_CONSTANT)
*pid = tmpl->id;
}
struct personate *
tcp_personality_test(const struct tcp_con *con, struct personality *person,
uint8_t sndflags)
{
uint8_t flags;
/* ET - The T1 test of NMAP has the SYN and ECE TCP flags set. The T5
* test of NMAP has only the SYN TCP flag set. The sequence number
* test of NMAP has only the SYN TCP flag set.
* Niels: We reuse the T1 even if ECE is not set if the response seems
* sane. This allows us to get TCP options right, too.
*/
flags = con->rcv_flags & (TH_FIN|TH_RST|TH_PUSH|TH_ACK|TH_URG|TH_SYN);
if (flags == TH_SYN) {
int hasece = con->rcv_flags & TH_ECE;
switch (con->state) {
case TCP_STATE_LISTEN:
case TCP_STATE_SYN_RECEIVED: {
struct personate *test = &person->tests[0];
if (sndflags & TH_RST)
return (NULL);
/* Check if we can use the ECE response for normal
* SYN, too.
*/
if (hasece || (test->flags == (TH_SYN|TH_ACK) &&
test->forceack == ACK_KEEP))
return (test);
return (NULL);
}
case TCP_STATE_CLOSED:
if (hasece)
return (NULL);
return (&person->tests[4]);
default:
return (NULL);
}
} else if (flags == 0) {
switch (con->state) {
case TCP_STATE_LISTEN:
return (&person->tests[1]);
default:
return (NULL);
}
} else if (flags == (TH_SYN|TH_PUSH|TH_FIN|TH_URG)) {
switch (con->state) {
case TCP_STATE_LISTEN:
case TCP_STATE_SYN_RECEIVED:
return (&person->tests[2]);
default:
return (NULL);
}
} else if (flags == TH_ACK) {
switch (con->state) {
case TCP_STATE_LISTEN:
case TCP_STATE_SYN_RECEIVED:
return (&person->tests[3]);
case TCP_STATE_CLOSED:
return (&person->tests[5]);
default:
return (NULL);
}
} else if (flags == (TH_FIN|TH_PUSH|TH_URG)) {
switch (con->state) {
case TCP_STATE_CLOSED:
return (&person->tests[6]);
default:
return (NULL);
}
} else if ((flags & TH_FIN) && (flags & (TH_SYN|TH_ACK)) == 0) {
/*
* If we get a FIN flag and do not allow fin scanning, then
* we just let the regular state engine run its course.
* Otherwise, we silently drop the packet, which indicates
* that FIN scanning is allowed for TCP_STATE_LISTEN.
*/
if (person->disallow_finscan || con->state != TCP_STATE_LISTEN)
return (NULL);
return (&person_drop);
}
return (NULL);
}
void
tcp_personality_seqinit(struct personality *person)
{
uint32_t simin = person->seqindex_min;
uint32_t simax = person->seqindex_max;
person->seqindex_amin = sqrt(((double)simin * simin - simin) / 2);
person->seqindex_amax = sqrt(((double)simax * simax - simax) / 2);
person->seqindex_aconst = sqrt((simax * simax - simax));
}
/* This function computes ISNs for TD and RI.
*
* NMAP obtains six initial sequence number (ISN) samples when performing
* an OS scan. Most of the calculations for sequence number prediction
* is based upon the differences of consecutive ISNs. Thus there are
* five sample differences. If the differences are determined not be
* be CONSTANT, RANDOM, Multiple of 64000, or Multiple of 800, statistical
* calculations are performed on the differences. The remaining cases,
* TRIVIALTIME (or time dependent) and RI (random increment) are dealt
* with in this function.
*
* NMAP computes the greatest common denominator (GCD) and a slightly
* modified standard deviation (modified so-as to prevent floating-point
* calculations to be rounded down to zero, which is not as useful) of the
* differences in the ISNs. This function is the reverse, computing
* ISNs which will produce a valid GCD and standard deviation for NMAP
* to match to a fingerprint.
*
* First, if the standard deviation is zero, then the difference in ISNs
* is constant. The set of five differences whose GCD is X, and whose
* standard deviation is not zero is { X, X, X, X, 2X }. The NMAP
* fingerprints file gives a valid range for the standard deviation. If
* no X can produce a standard deviation in the given range, then it is
* assumed that the standard deviation must be zero. Mathematically, this
* is the case if X > (2.5 + sqrt(simax^2 - simax)). Unfortunately, this
* series does not allow the ability to make sure the series is within
* certain standard deviation range.
*
* Another less noticable, but less reliable way to generate a set of
* five differences is to use a set
* { (A-1)X, (2A-1)X, (3A-1)X, (4A-1)X, (5A-1)X }
* whose GCD is X, and the value of the integer A can be found to make
* sure the standard deviation of the five numbers is within a given
* range. The range is Amin < A < Amax, where
* Amin = sqrt(simin * simin - simin) / 2) / X
* Amax = sqrt(simax * simax - simax) / 2) / X
* One selects an integer in this range for the value of A, plugs the
* number into the set of differences, and then adds the differences, in
* order, to the previous ISN, looping around when all five differences
* have been used. Because the set of numbers is not optimal there are
* cases when Amax - Amin < 1, in which case an integer in between
* Amin and Amax may not be able to be found. In this case, the only
* way to generate the ISN is to use constant differences, whose standard
* deviation is zero.
*
* This discussion is a very short version of how this function generates
* ISNs.
*/
static uint32_t
get_next_isn(struct template *tmpl, const struct personality *person)
{
uint32_t prev_isn = tmpl->seq;
uint32_t num_isns = tmpl->seqcalls;
uint32_t gcd = person->gcd;
double amin = person->seqindex_amin;
double amax = person->seqindex_amax;
double a;
if ((amax - amin) >= 2.0) {
/* Select an 'a' halfway between acceptable limits. The
* value of 'a' should always be the same, not random.
*/
a = ((amax - amin) / 2.0) + amin;
} else {
/* To get a valid number in a small range */
a = (uint32_t)ceil(((amax - amin) / 2.0) + amin);
}
/* Constant ISN difference */
if ((amax - amin) < 1 || gcd > (5 / 2 * person->seqindex_aconst))
return (prev_isn + gcd);
return (prev_isn + ((((num_isns - 1) % 5) + 1) * (uint32_t)a - 1)*gcd);
}
#define TIME_CORRECT(x,y) do { \
(y)->tv_sec = 0; \
(y)->tv_usec = (y)->tv_usec % (x); \
timersub(&tmpl->tv, y, &tmpl->tv); \
} while (0)
/* Get the correct time for this personality */
void
personality_time(struct template *tmpl, struct timeval *diff)
{
uint32_t ms, old_ms;
timersub(&tv_periodic, &tmpl->tv_real, diff);
tmpl->tv_real = tv_periodic;
old_ms = ms = diff->tv_sec * 10000 + (diff->tv_usec / 100);
ms *= tmpl->drift;
diff->tv_sec = ms / 10000;
diff->tv_usec = (ms % 10000) * 100;
timeradd(&tmpl->tv, diff, &tmpl->tv);
}
int
tcp_personality_time(struct template *tmpl, struct timeval *diff)
{
extern rand_t *honeyd_rand;
struct personality *person = tmpl->person;
int slowhz;
if (person == NULL)
return (-1);
personality_time(tmpl, diff);
if (person->tstamphz) {
int tstamphz = person->tstamphz;
int ticks;
if (tstamphz == -1)
tstamphz = 2;
/*
* Adjust so that the remaining subsecond ticks get
* counted next time.
*/
ticks = 1000000L / tstamphz;
slowhz = diff->tv_sec * tstamphz + diff->tv_usec / ticks;
TIME_CORRECT(ticks, diff);
tmpl->timestamp += slowhz;
} else {
/*
* This is not the default. Some stacks don't have
* any notion of time.
*/
slowhz = 0;
tmpl->timestamp = 0;
}
/*
* This is where new ISNs are generated. The latest ISN is
* stored in tmpl->seq. tmpl->seqcalls is the number of ISNs
* generated so far.
*/
switch(person->seqt) {
case SEQ_TRIVIALTIME:
case SEQ_RI:
/* No time component. May be required for high latency */
if (diff->tv_sec > 2) {
uint32_t adjust;
adjust = rand_uint32(honeyd_rand) % 2048;
adjust *= (slowhz - 2) * person->gcd;
tmpl->seq += adjust;
}
break;
case SEQ_CLASS64K:
tmpl->seq += slowhz * 64000;
break;
case SEQ_I800:
tmpl->seq += slowhz * 800;
break;
default:
break;
}
return (slowhz);
}
uint32_t
tcp_personality_seq(struct template *tmpl, struct personality *person)
{
struct timeval tmp;
extern rand_t *honeyd_rand;
int slowhz;
tmpl->seqcalls++;
if (!timerisset(&tmpl->tv)) {
gettimeofday(&tv_periodic, NULL);
tmpl->tv_real = tmpl->tv = tv_periodic;
if (tmpl->timestamp == 0)
tmpl->timestamp = rand_uint32(honeyd_rand) % 1728000;
if (tmpl->seq == 0) {
if (person->seqt == SEQ_CONSTANT && person->valset)
tmpl->seq = person->val;
else
tmpl->seq = rand_uint32(honeyd_rand);
}
return (tmpl->seq);
}
slowhz = tcp_personality_time(tmpl, &tmp);
/*
* This is where new ISNs are generated. The latest ISN is
* stored in tmpl->seq. tmpl->seqcalls is the number of ISNs
* generated so far.
*/
switch(person->seqt) {
case SEQ_CONSTANT:
return (tmpl->seq);
case SEQ_TRIVIALTIME:
case SEQ_RI:
tmpl->seq = get_next_isn(tmpl, person);
return (tmpl->seq);
case SEQ_CLASS64K:
tmpl->seq += 64000;
return (tmpl->seq);
case SEQ_I800:
tmpl->seq += 800;
return (tmpl->seq);
case SEQ_RANDOM:
default:
return (rand_uint32(honeyd_rand));
}
}
/* Default TCP options is timestamp, noop, noop */
static char *default_opts = "tnn";
int
tcp_personality_match(struct tcp_con *con, int flags)
{
struct template *tmpl = con->tmpl;
struct personality *person;
/* Find template and corresponding personality */
if (tmpl == NULL)
return (0);
person = tmpl->person;
if (person == NULL)
return (0);
return (tcp_personality_test(con, person, flags) != NULL);
}
int
tcp_personality(struct tcp_con *con, uint8_t *pflags, int *pwindow, int *pdf,
uint16_t *pid, char **poptions)
{
struct template *tmpl = con->tmpl;
struct personality *person;
struct personate *pers;
uint8_t flags = *pflags;
if (poptions != NULL)
*poptions = NULL;
/* XXX - We need to find some template to use here */
/* Find template and corresponding personality */
if (tmpl == NULL)
return (-1);
person = tmpl->person;
if (person == NULL)
return (-1);
if ((pers = tcp_personality_test(con, person, flags)) == NULL) {
/* Not a test case - but we still want to pretend */
ip_personality(tmpl, pid);
/* Set the sequence number only on SYN segments */
if (con->snd_una == 0 && (flags & TH_SYN))
con->snd_una = tcp_personality_seq(tmpl, person);
/* If we support timestamps, always set them */
if (person->tstamphz >= 0 && poptions != NULL)
*poptions = default_opts;
return (-1);
}
*pwindow = pers->window;
*pflags = pers->flags;
*pdf = pers->df;
if (poptions != NULL)
*poptions = pers->options;
switch (pers->forceack) {
case ACK_ZERO:
con->rcv_next = 0;
break;
case ACK_DECREMENT:
con->rcv_next--;
break;
case ACK_KEEP:
break;
}
ip_personality(tmpl, pid);
if (con->snd_una == 0)
con->snd_una = tcp_personality_seq(tmpl, person);
return (0);
}
#define SET(y, x, w, l) do { \
(x)->opt_type = w; \
(x)->opt_len = l; \
memcpy(y, x, l); \
} while (0)
/*
* Given a character string that describe TCP options, create the
* corresponding packet data.
*/
void
tcp_personality_options(struct tcp_con *con, struct tcp_hdr *tcp,
char *options)
{
extern rand_t *honeyd_rand;
u_char *p = (u_char *)tcp + TCP_HDR_LEN;
struct template *tmpl = con->tmpl;
struct tcp_opt opt;
int optlen = 0, simple = 0;
uint32_t timestamp;
short mss = 1460;
char *o;
for (o = options; *o; o++) {
opt.opt_len = 0;
switch(*o) {
case 'm':
if (o[1] == 'e') {
o++;
if (con->mss)
mss = con->mss;
}
if (con->flags & TCP_TARPIT)
mss = 64;
opt.opt_data.mss = htons(mss);
SET(p, &opt, TCP_OPT_MSS, 4);
break;
case 'w':
opt.opt_data.wscale = 0;
SET(p, &opt, TCP_OPT_WSCALE, 3);
break;
case 't':
if (tmpl != NULL) {
struct timeval tv;
tcp_personality_time(tmpl, &tv);
timestamp = htonl(tmpl->timestamp);
} else {
timestamp = rand_uint32(honeyd_rand);
}
opt.opt_data.timestamp[0] = timestamp;
opt.opt_data.timestamp[1] = 0;
if (con->sawtimestamp)
opt.opt_data.timestamp[1] = con->echotimestamp;
SET(p, &opt, TCP_OPT_TIMESTAMP, 2 + 4 + 4);
break;
case 'n':
simple++;
SET(p, &opt, TCP_OPT_NOP, 1);
break;
case 'l':
SET(p, &opt, TCP_OPT_EOL, 2);
break;
default:
opt.opt_len = 0;
break;
}
optlen += opt.opt_len;
p += opt.opt_len;
}
/* Check if we have only unreasonable options */
if (simple == optlen)
optlen = 0;
if (optlen)
tcp->th_off += (optlen + 3) / 4;
}
/* JVR - added '+1' in default case below for situations where IP checksum does not
change after byte order conversion, e.g. IP checksum of 0x6565.
Also added missing 'break' statement in RVAL_ZERO case */
#define RVAL_DO(x, w) do { \
switch (w) { \
case RVAL_OKAY: break; \
case RVAL_ZERO: (x) = 0; break; \
default: (x) = ntohs(x+1); \
} \
} while (0)
int
icmp_error_personality(struct template *tmpl,
struct addr *dst, struct ip_hdr *ip, uint8_t *pdf,
uint8_t *ptos, int *pquotelen, uint8_t *ttl)
{
struct persudp *test;
int iphdr_changed = 0;
struct xp_fingerprint *xp_print;
if (tmpl == NULL || tmpl->person == NULL)
return (1);
/* JVR - set TTL using XP fingerprint, use nmap for rest of header settings */
xp_print = tmpl->person->xp_fprint;
if (xp_print != NULL)
*ttl = xp_print->ttl_vals.icmp_unreach_reply_ttl.ttl_val;
/* JVR */
test = &tmpl->person->udptest;
if (!test->response)
return (0);
*pdf = test->df;
*ptos = test->tos;
*pquotelen = test->quotelen;
if (test->riplen) {
u_int len = ntohs(ip->ip_len);
ip->ip_len = htons(len + test->riplen);
iphdr_changed = 1;
}
RVAL_DO(ip->ip_id, test->rid);
if (test->rid != RVAL_OKAY)
iphdr_changed = 1;
/* We need to recompute the ip header checksum in some cases */
if (test->ripck == RVAL_OKAY) {
if (iphdr_changed)
ip_checksum(ip, ip->ip_hl << 2);
} else
RVAL_DO(ip->ip_sum, test->ripck);
if (ip->ip_p == IP_PROTO_UDP) {
struct udp_hdr *udp = (struct udp_hdr *)((u_char *)ip + (ip->ip_hl << 2));
u_char *p = (u_char *)(udp + 1);
RVAL_DO(udp->uh_sum, test->uck);
if (test->dat == RVAL_BAD)
*p = 0;
}
return (1);
}
/* Parse nmap tests */
/*
* Improve TCP ISN calculation by reading all possible
* gcd values and selecting the smallest one.
*/
int
parse_tseq_gcd(char *s, char *end)
{
char *next, *endptr, *quantifier;
unsigned int val, minval;
int orexp;
orexp = val = 0;
endptr = quantifier = NULL;
minval = UINT_MAX;
/* Determine if the values are or'd or and'd */
if (*s && strpbrk(s, "|") != NULL)
orexp = 1;
/* Go through all &'d and |'d values */
while (s < end && s != NULL && *s) {
/* Get the next and'd or or'd number */
if ((next = strpbrk(s, "|&")) != NULL)
*next++ = '\0';
/* Determine if field is non-zero */
if (*s == '+') {
minval = 1;
s = next;
continue;
}
/* Determine value quantifier */
if ((quantifier = strpbrk(s, "<>")) != NULL)
s++;
val = strtol(s, &endptr, 16);
s = next;
if (quantifier != NULL) {
if (*quantifier != '>')
continue;
/* val is minval if | and val < minval or & */
if (val < minval || !orexp)
minval = val + 1;
continue;
}
if (!orexp) {
/* &, minval is val */
minval = val;
} else if (orexp && val < minval) {
/* |, val is minval if val < minval */
minval = val;
}
}
if (minval == 0 || minval == UINT_MAX)
minval = 1;
return (minval);
}
/* Determine minimal and maximal SI value from fingerprint */
int
parse_tseq_si(struct personality *pers, char *s, char *end)
{
char *next, *endptr, *quantifier;
int orexp, setsimin, setsimax;
unsigned int val, minval, maxval;
orexp = val = setsimin = setsimax = 0;
endptr = quantifier = NULL;
maxval = 1;
minval = UINT_MAX;
/* Determine if the values are or'd or and'd */
if (*s && strpbrk(s, "|") != NULL)
orexp = 1;
/* Go through all &'d and |'d values */
while (s < end && s != NULL && *s) {
/* Get the next and'd or or'd number */
if ((next = strpbrk(s, "|&")))
*next++ = '\0';
/* Determine if field is non-zero */
if (*s == '+') {
minval = 1;
setsimin = 1;
s = next;
continue;
}
/* Determine value quantifier */
if ((quantifier = strpbrk(s, "<>")))
s++;
val = strtol(s, &endptr, 16);
s = next;
if (quantifier != NULL) {
switch (*quantifier) {
case '>':
if (val < minval || !orexp) {
minval = val + 1;
setsimin = 1;
}
break;
case '<':
if (val > maxval || !orexp) {
maxval = val - 1;
setsimax = 1;
}
break;
}
continue;
}
if (!orexp) {
minval = maxval = val;
setsimin = 1;
setsimax = 1;
} else {
if (val < minval) {
minval = val;
setsimin = 1;
} else if (val > maxval) {
maxval = val;
setsimax = 1;
}
}
}
if (!setsimin)
minval = 0;
/* seqindex_max cannot be bigger than SEQ_RI_MAX,
* or nmap says the difference in ISN is "truly random"
*/
if (!setsimax || maxval > SEQ_RI_MAX)
maxval = SEQ_RI_MAX;
/* For TD, seqindex_max cannot be larger than 75 */
if (pers->seqt == SEQ_TRIVIALTIME && maxval > SEQ_TRIVIALTIME_MAX)
maxval = SEQ_TRIVIALTIME_MAX;
/* For RI, seqindex_min must be larger than 75 */
if (pers->seqt == SEQ_RI && minval <= SEQ_TRIVIALTIME_MAX)
minval = SEQ_TRIVIALTIME_MAX + 1;
/*
* With the mods above, this is now possible, even though it
* is not supposed to happen. This means the fingerprints
* file is wrong.
*/
if (minval > maxval) {
fprintf(stderr, "Warning: "
"Impossible SI range in Class fingerprint \"%s\"\n",
pers->name);
}
pers->seqindex_min = minval;
pers->seqindex_max = maxval;
return (setsimin || setsimax);
}
int
parse_tseq(struct personality *pers, int off, char *line)
{
char *p = line, *p2, *end;
int setsi = 0;
while (p != NULL && strlen(p)) {
p2 = strsep(&p, "%");
end = p2;
if (strncasecmp(p2, "Class=", 6) == 0) {
/* TCP ISN calculation */
enum seqtype st = 0;
p2 = strsep(&end, "|");
p2 = line + 6;
if (strcasecmp(p2, "TD") == 0)
st = SEQ_TRIVIALTIME;
else if (strcasecmp(p2, "TR") == 0)
st = SEQ_RANDOM;
else if (strcasecmp(p2, "RI") == 0)
st = SEQ_RI;
else if (strcasecmp(p2, "C") == 0) {
st = SEQ_CONSTANT;
pers->val = 0;
} else if (strcasecmp(p2, "64K") == 0)
st = SEQ_CLASS64K;
else if (strcasecmp(p2, "i800") == 0)
st = SEQ_I800;
else
return (-1);
pers->seqt = st;
} else if (strncasecmp(p2, "SI=", 3) == 0) {
p2 += 3;
if (p == NULL)
p = p2 + strlen(p2);
setsi = parse_tseq_si(pers, p2, p);
} else if (strncasecmp(p2, "gcd=", 4) == 0) {
p2 += 4;
if (p == NULL)
p = p2 + strlen(p2);
pers->gcd = parse_tseq_gcd(p2, p);
} else if (strncasecmp(p2, "Val=", 4) == 0) {
/* Some SEQ_CONSTANT's have a specific value */
char *e;
p2 = strsep(&end, "|");
p2 += 4;
pers->val = strtol(p2, &e, 16);
pers->valset = 1;
}
/* Improve IPID sequencing capability */
else if (strncasecmp(p2, "IPID=", 5) == 0) {
int done = 0;
end += 5;
while (!done) {
done = 1;
p2 = strsep(&end, "|");
if (strcasecmp(p2, "I") == 0) {
pers->idt = ID_SEQUENTIAL;
} else if (strcasecmp(p2, "BI") == 0) {
pers->idt = ID_SEQUENTIAL_BROKEN;
} else if (strcasecmp(p2, "Z") == 0) {
if (end != NULL) {
done = 0;
continue;
}
pers->idt = ID_ZERO;
} else if (strcasecmp(p2, "RD") == 0) {
pers->idt = ID_RANDOM;
} else if (strcasecmp(p2, "C") == 0) {
pers->idt = ID_CONSTANT;
} else if (strcasecmp(p2, "RPI") == 0) {
pers->idt = ID_RPI;
} else
return -1;
}
} else if (strncasecmp(p2, "TS=", 3) == 0) {
/* TCP timestamp sequencing capability */
p2 = strsep(&end, "|");
p2 += 3;
if (strcasecmp(p2, "2HZ") == 0)
pers->tstamphz = 2;
else if (strcasecmp(p2, "100HZ") == 0)
pers->tstamphz = 100;
else if (strcasecmp(p2, "1000HZ") == 0)
pers->tstamphz = 1000;
else if (strcasecmp(p2, "0") == 0)
pers->tstamphz = 0;
else if (strcasecmp(p2, "U") == 0)
pers->tstamphz = -1;
else
return (-1);
}
}
if (pers->gcd == 0)
pers->gcd = 1;
if (!setsi) {
if (pers->seqt == SEQ_TRIVIALTIME)
pers->seqindex_max = SEQ_TRIVIALTIME_MAX;
else if (pers->seqt == SEQ_RI) {
pers->seqindex_min = SEQ_TRIVIALTIME_MAX + 1;
pers->seqindex_max = SEQ_RI_MAX;
}
}
/* Calculate variables for sequence number generation */
tcp_personality_seqinit(pers);
return (0);
}
int
parse_tl(struct personality *pers, int off, char *line)
{
struct personate *test = &pers->tests[off];
char *p = line, *p2, *end;
if (strncasecmp(line, "Resp=N", 6) == 0) {
test->flags = 0;
return (0);
}
/* Permits Y|N, too */
if (strncasecmp(line, "Resp=Y", 6) == 0) {
p = strchr(p, '%');
if (p == NULL)
return (-1);
p++;
}
while (p != NULL && strlen(p)) {
p2 = strsep(&p, "%");
/* We ignore all other values, only take the first */
end = p2;
p2 = strsep(&end, "|");
if (strcasecmp(p2, "DF=Y") == 0) {
test->df = 1;
} else if (strcasecmp(p2, "DF=N") == 0) {
test->df = 0;
} else if (strncasecmp(p2, "W=", 2) == 0) {
int smaller = 0;
p2 += 2;
/* Special cases */
if (strcasecmp(p2, "O") == 0)
continue;
if (*p2 == '<') {
p2++;
smaller = 1;
}
test->window = strtoul(p2, &end, 16);
if (end == NULL || *end != '\0')
return (-1);
if (smaller)
test->window--;
} else if (strncasecmp(p2, "ACK=", 4) == 0) {
p2 += 4;
/* Try to use S++ if that is an option */
do {
if (strcasecmp(p2, "O") == 0)
test->forceack = ACK_ZERO;
else if (strcasecmp(p2, "S") == 0)
test->forceack = ACK_DECREMENT;
else if (strcasecmp(p2, "S++") == 0)
test->forceack = ACK_KEEP;
else
return (-1);
} while (test->forceack != ACK_KEEP &&
(p2 = strsep(&end, "|")) != NULL);
} else if (strncasecmp(p2, "Flags=", 6) == 0) {
p2 += 6;
/* Special case. A|AS should result in AS */
if (end != NULL && *end != '\0')
p2 = strsep(&end, "|");
test->flags = 0;
for (; *p2; p2++) {
*p2 = tolower(*p2);
switch (*p2) {
case 'a':
test->flags |= TH_ACK;
break;
case 's':
test->flags |= TH_SYN;
break;
case 'f':
test->flags |= TH_FIN;
break;
case 'r':
test->flags |= TH_RST;
break;
case 'p':
test->flags |= TH_PUSH;
break;
case 'u':
test->flags |= TH_URG;
break;
case 'b':
test->flags |= TH_ECE;
break;
default:
return (-1);
}
}
} else if (strncasecmp(p2, "Ops=", 4) == 0) {
char *p3;
p2 += 4;
if (strlen(p2)) {
for (p3 = p2; *p3; p3++)
*p3 = tolower(*p3);
if ((test->options = strdup(p2)) == NULL)
err(1, "%s: strdup", __FUNCTION__);
}
} else
return (-1);
}
return (0);
}
#define RVAL_TRANS(x, p) do { \
if (strcasecmp(p, "0") == 0) \
(x) = RVAL_ZERO; \
else if (strcasecmp(p, "E") == 0) \
(x) = RVAL_OKAY; \
else if (strcasecmp(p, "F") == 0) \
(x) = RVAL_BAD; \
else if (*p == '\0') \
(x) = RVAL_OKAY; /* Fill in default */ \
else \
return (-1); \
} while (0)
int
parse_pu(struct personality *pers, int off, char *line)
{
struct persudp *test = &pers->udptest;
char *p = line, *p2, *end;
if (strncasecmp(line, "Resp=N", 6) == 0) {
test->response = 0;
return (0);
}
test->response = 1;
if (strncasecmp(line, "Resp=Y%", 7) == 0)
p += 7;
while (p != NULL && strlen(p)) {
p2 = strsep(&p, "%");
/* We ignore all other values, only take the first */
end = p2;
p2 = strsep(&end, "|");
if (strcasecmp(p2, "DF=Y") == 0) {
test->df = 1;
} else if (strcasecmp(p2, "DF=N") == 0) {
test->df = 0;
} else if (strcasecmp(p2, "DF=") == 0) {
/* Fill in default */
test->df = 0;
} else if (strncasecmp(p2, "ULEN=", 5) == 0) {
continue;
} else if (strncasecmp(p2, "TOS=", 4) == 0) {
p2 += 4;
test->tos = strtoul(p2, &end, 16);
if (end == NULL || *end != '\0')
return (-1);
} else if (strncasecmp(p2, "IPLEN=", 6) == 0) {
p2 += 6;
test->quotelen = strtoul(p2, &end, 16);
if (end == NULL || *end != '\0')
return (-1);
test->quotelen -= IP_HDR_LEN + ICMP_HDR_LEN + 4;
} else if (strncasecmp(p2, "RIPTL=", 6) == 0) {
p2 += 6;
test->riplen = strtoul(p2, &end, 16) - 328;
if (end == NULL || *end != '\0')
return (-1);
} else if (strncasecmp(p2, "RID=", 4) == 0) {
p2 += 4;
RVAL_TRANS(test->rid, p2);
} else if (strncasecmp(p2, "RIPCK=", 6) == 0) {
p2 += 6;
RVAL_TRANS(test->ripck, p2);
} else if (strncasecmp(p2, "UCK=", 4) == 0) {
p2 += 4;
RVAL_TRANS(test->uck, p2);
} else if (strncasecmp(p2, "DAT=", 4) == 0) {
p2 += 4;
RVAL_TRANS(test->dat, p2);
} else
return (-1);
}
return (0);
}
struct parse_test {
char *start;
int offset;
int (*parse_test)(struct personality *, int, char *);
} parse_tests[] = {
{"TSeq", 0, parse_tseq},
{"T1", 0, parse_tl},
{"T2", 1, parse_tl},
{"T3", 2, parse_tl},
{"T4", 3, parse_tl},
{"T5", 4, parse_tl},
{"T6", 5, parse_tl},
{"T7", 6, parse_tl},
{"PU", 0, parse_pu},
{NULL, 0, NULL}
};
int
personality_line(struct personality *pers, char *line)
{
struct parse_test *pt = parse_tests;
char *p, *p2;
/* Ignore additional nmap output */
if (strncasecmp(line, "SInfo", 5) == 0 ||
strncasecmp(line, "Class", 5) == 0)
return (0);
p2 = line;
p = strsep(&p2, "(");
if (p2 == NULL)
return (-1);
p = strsep(&p2, ")");
if (p2 == NULL)
return (-1);
for (; pt->start; pt++) {
if (strncasecmp(line, pt->start, strlen(pt->start)) == 0)
return (pt->parse_test(pers, pt->offset, p));
}
return (-1);
}
/* Creates a new personality and details on how to deal with duplicates */
struct personality *
personality_config_new(const char *name, int lineno)
{
struct personality *pers;
if ((pers = personality_new(name)) != NULL)
return (pers);
DFPRINTF(2, (stderr, "%d: Overwriting old fingerprint \"%s\"\n",
lineno, name));
/* Find the old personality, and remove it */
pers = personality_find(name);
personality_free(pers);
return (personality_new(name));
}
/* Parsing Functions */
int
personality_parse(FILE *fin)
{
char bl[1024], line[1024], *p, *p2;
int errors = 0, lineno = 0, ignore = 0;
struct personality *pers = NULL;
while ((p = fgets(line, sizeof(line), fin)) != NULL) {
strlcpy(bl, line, sizeof(bl));
lineno++;
p += strspn(p, WHITESPACE);
if (*p == '\0' || *p == '#') {
pers = NULL;
continue;
}
/* Remove trailing comments */
p2 = p;
strsep(&p2, "#\r\n");
if (CMP(p, FINGERPRINT) == 0) {
p += sizeof(FINGERPRINT) - 1;
p += strspn(p, ": \t");
if (!isalnum(*p)) {
fprintf(stderr, "%d: bad name \"%s\"\n",
lineno, p);
return (-1);
}
for (p2 = p + strlen(p) - 1; isblank(*p2); p2--)
*p2 = '\0';
if ((pers = personality_config_new(p, lineno)) == NULL)
ignore = 1;
else
ignore = 0;
continue;
}
if (pers == NULL) {
if (ignore)
continue;
fprintf(stderr, "%d: No personality for \"%s\"\n",
lineno, p);
return (-1);
}
if (personality_line(pers, p) == -1) {
fprintf(stderr, "%d: parse error: %s", lineno, bl);
errors++;
}
}
return (errors ? -1 : 0);
}
//-------------------------------------------------------------------
/* ET - Moved student xprobe parse functions from xprobe_parse.c */
/* CK - These are pretty much self-explanitory. Some are not very good.*/
/* ET - Actually none are very good since they don't return an error if
* an unexpected value was found. In all cases, they look for
* all but one of the possible values, and if none of those values
* are found, the one value not searched for is returned. Uggghhhh!
* This implementation should change. At least there should be some
* enums or macros for the values instead of magic numbers. */
static int
get_zero_notzero(char *input)
{ /* returns 1 if zero, 0 otherwise */
return (strncmp (input, "!0", 2) == 0);
}
/* ET - Added this one since the df bit is 0 or 1 */
static int
get_zero_one(char *input)
{ /* returns 1 if zero, 0 otherwise */
return (input[0] == '1');
}
static int
get_yes_no(char *input)
{ /*returns 0 if y, 0 otherwise */
return ((strncasecmp (input, "Y", 1) == 0));
}
static int
get_zero_ok_bad(char *input)
{
if (input[0] == '0')
return (1);
else if (strncmp (input, "OK", 2) == 0)
return (2);
else if (strncmp (input, "BAD", 3) == 0)
return (4);
return (0);
}
static int
get_ok_flipped(char *input)
{
if (strncmp (input, "OK", 2) == 0)
return (1);
else if (strncmp (input, "FLIPPED", 7) == 0)
return (2);
return 0;
}
static int
get_echoed_total_len(char *input)
{
if (strcmp (input, ">20") == 0)
return (1);
else if (strcmp (input, "OK") == 0)
return (2);
else if (strcmp (input, "<20") == 0)
return (4);
return 0;
}
static int
get_echoed_dtsize (char *input)
{
if (input[0] == '8')
return (1);
else if (strncmp (input, "64", 2) == 0)
return (2);
else if (strncmp (input, ">64", 3) == 0)
return (4);
return (0);
}
static struct ttl_pair
parse_and_load_ttl_pair(char *p)
{
/* CK- Parse TTL pairs*/
struct ttl_pair to_load;
char *tmp_p = p;
to_load.gt_lt = 0;
if (p[0] == '>') {
to_load.gt_lt = 1;
strsep (&tmp_p, ">");
} else if (p[0] == '<') {
to_load.gt_lt = 2;
strsep (&tmp_p, "<");
}
to_load.ttl_val = atoi (tmp_p);
return (to_load);
}
/* ET - Called by get_fprint() only */
static int
set_xp_struct(struct xp_fingerprint *pers, char *line)
{
/* CK- Parse xprobe lines. */
char *p, *p2;
int osname_len;
int foo;
p = p2 = line;
if ((foo = strncasecmp (p2, "OS_ID", 5)) == 0) {
/* Copy OS Name into structure:
* Gets the OS name into p2, assuming line has
* 'OS_ID = "OS"'
*/
strsep (&p2, "=");
strsep (&p2, " ");
strsep (&p2, "\"");
p = strsep (&p2, "\"");
if (p == NULL || p2 == NULL)
return (0);
osname_len = strcspn (p, "\0");
if (osname_len <= 0)
return (0);
pers->os_id = strdup(p);
} else {
/* Copy other icmp values into structure:
* Assumes the the format is: 'icmp_... = val'
*/
if (strsep (&p2, "=") == NULL || p2 == NULL)
return (0);
p2 += strspn(p2, WHITESPACE);
if (strncmp (p, "icmp_echo_code", 14) == 0) {
pers->flags.icmp_echo_code = get_zero_notzero (p2);
} else if (strncmp (p, "icmp_echo_ip_id", 15) == 0) {
pers->flags.icmp_echo_ip_id = get_zero_notzero (p2);
} else if (strncmp (p, "icmp_echo_tos_bits", 18) == 0) {
pers->flags.icmp_echo_tos_bits = get_zero_notzero (p2);
} else if (strncmp (p, "icmp_echo_df_bit", 16) == 0) {
pers->flags.icmp_echo_df_bit = get_zero_one (p2);
} else if (strncmp (p, "icmp_echo_reply_ttl", 19) == 0) {
pers->ttl_vals.icmp_echo_reply_ttl = parse_and_load_ttl_pair (p2);
} else if (strncmp (p, "icmp_timestamp_reply_ttl", 24) == 0) {
pers->ttl_vals.icmp_timestamp_reply_ttl =
parse_and_load_ttl_pair (p2);
} else if (strncmp (p, "icmp_timestamp_reply", 20) == 0) {
pers->flags.icmp_timestamp_reply = get_yes_no (p2);
} else if (strncmp (p, "icmp_addrmask_reply_ttl", 23) == 0) {
pers->ttl_vals.icmp_addrmask_reply_ttl =
parse_and_load_ttl_pair (p2);
} else if (strncmp (p, "icmp_addrmask_reply", 19) == 0) {
pers->flags.icmp_addrmask_reply = get_yes_no (p2);
} else if (strncmp (p, "icmp_info_reply_ttl", 19) == 0) {
pers->ttl_vals.icmp_info_reply_ttl = parse_and_load_ttl_pair (p2);
} else if (strncmp (p, "icmp_info_reply", 15) == 0) {
pers->flags.icmp_info_reply = get_yes_no (p2);
} else if (strncmp (p, "icmp_unreach_echoed_dtsize", 26) == 0){
pers->flags.icmp_unreach_echoed_dtsize = get_echoed_dtsize (p2);
} else if (strncmp (p, "icmp_unreach_reply_ttl", 22) == 0) {
pers->ttl_vals.icmp_unreach_reply_ttl =
parse_and_load_ttl_pair (p2);
} else if (strncmp (p, "icmp_unreach_precedence_bits", 28) == 0) {
pers->flags.icmp_unreach_precedence_bits = strtol (p2, NULL, 16);
} else if (strncmp (p, "icmp_unreach_df_bit", 19) == 0) {
pers->flags.icmp_unreach_df_bit = get_zero_one (p2);
} else if (strncmp (p, "icmp_unreach_echoed_udp_cksum", 29) == 0) {
pers->flags.icmp_unreach_echoed_udp_cksum = get_zero_ok_bad (p2);
} else if (strncmp (p, "icmp_unreach_echoed_ip_cksum", 28) == 0) {
pers->flags.icmp_unreach_echoed_ip_cksum = get_zero_ok_bad (p2);
} else if (strncmp (p, "icmp_unreach_echoed_ip_id", 25) == 0) {
pers->flags.icmp_unreach_echoed_ip_id = get_ok_flipped (p2);
} else if (strncmp (p, "icmp_unreach_echoed_total_len", 29) == 0) {
pers->flags.icmp_unreach_echoed_total_len =
get_echoed_total_len (p2);
} else if (strncmp (p, "icmp_unreach_echoed_3bit_flags", 30) == 0) {
pers->flags.icmp_unreach_echoed_3bit_flags = get_ok_flipped (p2);
} else {
/* Hmmm, got an unrecognized line. You FAIL */
return (0);
}
}
return (1);
}
/* ET - Called by xprobe_personality_parse() only to parse a single FP */
/* CK - Parses one finger print at a time, returns a pointer */
static struct xp_fingerprint *
get_fprint(FILE * fp_in)
{
char line[1024], *p, *p2;
struct xp_fingerprint *pers = NULL;
int generic = 0;
pers = (struct xp_fingerprint *) calloc (1,sizeof (struct xp_fingerprint));
if (pers == NULL) {
warn("%s: Could not allocate\n", __func__);
return (NULL);
}
/* Get a single line */
while ((p = fgets (line, sizeof (line), fp_in)) != NULL) {
/* Skip leading whitespace */
p += strspn (p, WHITESPACE);
/* Eliminate blank lines and comments */
if (*p == '\0' || *p == '#')
continue;
/* Remove trailing comments */
p2 = p;
strsep (&p2, "#\r\n");
/* Remove trailing whitespace */
for (p2 -= 2; (p2 >= p) && isspace (*p2); p2--)
*p2 = '\0';
/* Ignore the "fingperint {" line */
if (CMP (p, XPRINT) == 0)
continue;
/* xprobe2.conf has a generic section.
* This skips that section */
if (CMP (p, "generic") == 0)
generic = 1;
if (*p == '}') {
if (!generic) {
/* This closing brace signifies the end of
* a fingerprint. */
return pers;
}
/* Unskip the generic section */
generic = 0;
continue;
}
/* Skip the generic section */
if (generic)
continue;
/* This line is not a comment, blank line, or start of a fingerprint.
* So it must be a icmp_* or OS_ID line, right? :) */
if (!set_xp_struct (pers, p)) {
fprintf (stderr, "Errors setting struct\n");
free(pers);
return (NULL);
}
}
/* If we return here, then we didn't get a complete fingerprint.
* Therefore we must free the alloced fingerprint and return NULL. */
free(pers);
return (NULL);
}
/* !!!DEBUG FUNCTION!!! */
#ifdef DEBUG_XPROBE_STRUCT
static void
print_xprobe_struct(struct xp_fingerprint *pers)
{
printf ("OS_ID: %s\n", pers->os_id);
printf ("-- Module A --\n");
printf ("icmp_echo_code: %d\n",
pers->flags.icmp_echo_code);
printf ("icmp_echo_ip_id: %d\n",
pers->flags.icmp_echo_ip_id);
printf ("icmp_echo_tos_bits: %d\n",
pers->flags.icmp_echo_tos_bits);
printf ("icmp_echo_df_bit: %d\n",
pers->flags.icmp_echo_df_bit);
printf ("icmp_reply_ttl.gt_lt: %d\n",
pers->ttl_vals.icmp_echo_reply_ttl.gt_lt);
printf ("icmp_reply_ttl.ttl_vals: %d\n",
pers->ttl_vals.icmp_echo_reply_ttl.ttl_val);
printf ("-- Module B --\n");
printf ("icmp_timestamp_reply: %d\n",
pers->flags.icmp_timestamp_reply);
printf ("icmp_timestamp_reply_ttl.gt_lt: %d\n",
pers->ttl_vals.icmp_timestamp_reply_ttl.gt_lt);
printf ("icmp_timestamp_reply_ttl.ttl_vals: %d\n",
pers->ttl_vals.icmp_timestamp_reply_ttl.ttl_val);
printf ("-- Module C --\n");
printf ("icmp_addrmask_reply: %d\n",
pers->flags.icmp_addrmask_reply);
printf ("icmp_addrmask_reply_ttl.gt_lt: %d\n",
pers->ttl_vals.icmp_addrmask_reply_ttl.gt_lt);
printf ("icmp_addrmask_reply_ttl.ttl_vals: %d\n",
pers->ttl_vals.icmp_addrmask_reply_ttl.ttl_val);
printf ("-- Module D --\n");
printf ("icmp_info_reply: %d\n",
pers->flags.icmp_info_reply);
printf ("icmp_info_reply_ttl.gt_lt: %d\n",
pers->ttl_vals.icmp_info_reply_ttl.gt_lt);
printf ("icmp_info_reply_ttl.ttl_vals: %d\n",
pers->ttl_vals.icmp_info_reply_ttl.ttl_val);
printf ("-- Module E --\n");
printf ("icmp_unreach_echoed_dtsize: %d\n",
pers->flags.icmp_unreach_echoed_dtsize);
printf ("icmp_u_reply_ttl.gt_lt: %d\n",
pers->ttl_vals.icmp_unreach_reply_ttl.gt_lt);
printf ("icmp_u_reply_ttl.ttl_vals: %d\n",
pers->ttl_vals.icmp_unreach_reply_ttl.ttl_val);
printf ("icmp_unreach_precedence_bits: 0x%x\n",
pers->flags.icmp_unreach_precedence_bits);
printf ("icmp_unreach_df_bit: %d\n",
pers->flags.icmp_unreach_df_bit);
printf ("icmp_unreach_echoed_udp_cksum: %d\n",
pers->flags.icmp_unreach_echoed_udp_cksum);
printf ("icmp_unreach_echoed_ip_cksum: %d\n",
pers->flags.icmp_unreach_echoed_ip_cksum);
printf ("icmp_unreach_echoed_ip_id: %d\n",
pers->flags.icmp_unreach_echoed_ip_id);
printf ("icmp_unreach_echoed_total_len: %d\n",
pers->flags.icmp_unreach_echoed_total_len);
printf ("icmp_unreach_echoed_3bit_flags: %d\n",
pers->flags.icmp_unreach_echoed_3bit_flags);
}
#endif /* DEBUG_XPROBE_STRUCT */
void
print_perstree(void)
{
struct personality * pers;
int i = 0;
SPLAY_FOREACH(pers, perstree, &personalities) {
if (pers->xp_fprint != NULL)
printf("\tXP %d: %s\n", ++i, pers->xp_fprint->os_id);
}
}
/**
* Loads the xprobe fingerprint file and stores the fingerprints in the
* correct personality based upon the association table
*
* @param fp The pre-fopen'd file to load
* @return -1 on failure, 0 on success
*/
int
xprobe_personality_parse(FILE *fp)
{
struct xp_fingerprint *new_print = NULL;
if (fp == NULL) {
fprintf (stderr, "Could not parse fingerprint file!");
return (-1);
}
while (!feof (fp)) {
/* Get a single fingerprint */
new_print = get_fprint (fp);
if (new_print != NULL) {
#ifdef DEBUG_XPROBE_STRUCT
print_xprobe_struct (new_print);
#endif /* DEBUG_XPROBE_STRUCT */
SPLAY_INSERT(xp_fprint_tree, &xp_fprints, new_print);
new_print = NULL;
}
}
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1