/*
* TCPVIEW
*
* Author: Martin Hunt
* Networks and Distributed Computing
* Computing & Communications
* University of Washington
* Administration Building, AG-44
* Seattle, WA 98195
* Internet: martinh@cac.washington.edu
*
*
* Copyright 1992 by the University of Washington
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appears in all copies and that both the
* above copyright notice and this permission notice appear in supporting
* documentation, and that the name of the University of Washington not be
* used in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. This software is made
* available "as is", and
* THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
* WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
* NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
* (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef lint
static char rcsid[] =
"@(#) $Header: /usr/staff/martinh/tcpview/RCS/sniffer_file.c,v 1.3 1993/04/22 20:35:19 martinh Exp $ (UW)";
#endif
/*
* routines to write and playback a Network General "Sniffer" compatible file
* through tcpdump. Most of the information about Sniffer files came from the
* Sniffer documentation. Some was gained by squinting at hex dumps.
*
* This code is brought to you by Corey Satten, corey@cac.wasnington.edu
* and Martin Hunt, martinh@cac.washington.edu. The ifdefs are optional
* and for speed, of course.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <math.h>
#include <string.h>
#include <net/bpf.h>
#include "version.h"
#include "savefile.h"
#define MAXPKT 1600 /* >= any ether pkt */
static short byte_order = 0x100;
static int low_offset; /* where is low byte in short */
static int high_offset; /* where is hi byte in short */
/*
* The following moves byte-order determination from runtime to compile-time
* if known. Don't worry, if you don't know, it will still work.
*/
#if defined(vax)||defined(mips)||defined(i386)
# define LOW_OFFSET 0
# define HIGH_OFFSET 1
#endif
#if defined(mc68000)||defined(sparc)
# define LOW_OFFSET 1
# define HIGH_OFFSET 0
#endif
#if !defined(HIGH_OFFSET)||!defined(LOW_OFFSET)
# define LOW_OFFSET low_offset
# define HIGH_OFFSET high_offset
#endif
/*
* x should be of static storage class. If HIGH/LOW_OFFSET is constant,
* these should result in compile-time access to the proper byte.
*/
#define low(x) (*(((char *)&(x))+LOW_OFFSET))
#define high(x) (*(((char *)&(x))+HIGH_OFFSET))
#define putbs(x,f) putc(low(x),f); putc(high(x),f)
#define getbs(x,f) low(x)=getc(f); high(x)=getc(f)
static void hdr_load(), hdr_print(), dh_print(), ver_print(), dh_load();
static FILE *sfd; /* sniffer output file descriptor */
static FILE *rfd; /* sniffer input file descriptor */
static int TimeZoneOffset; /* number of seconds from GMT */
static int frame_count = 0; /* number of records written to sniffer file */
static long last_offset = 0; /* position of start of last complete record */
static char s_magic[] = { /* magic header */
'T', 'R', 'S', 'N', 'I', 'F', 'F', ' ', 'd', 'a', 't', 'a', ' ', ' ', ' ', ' ',
0x1a };
/*
* putting the two structures all in one big structure allows us to
* fwrite the entire structure at once for a substantial speedup on
* little-endian machines (vax, etc)
*/
static struct s {
struct s_hdr { /* data record header */
short type;
short len;
short rsvd;
} s_hdr;
struct s_dh { /* record data header */
ushort time_low;
ushort time_med;
ushort time_high;
short size;
char fs;
char flags;
short truesize;
short rsvd;
} s_dh;
} s = { {4, 0, 0}, {0, 0, 0, 0, 0, 0x1c, 0, 0} };
static struct v {
short maj_vers; /* major version number */
short min_vers; /* minor version number */
short time;
short date;
char type;
char network; /* network type */
char format;
char timeunit; /* timestamp units */
short rsvd[3]; /* reserved */
} V;
/* values for V.timeunit */
double Usec[] = { 15.0, 0.838096, 15.0, 0.5, 2.0 };
double Timeunit;
/* initializes reading.
** returns SFERR_BADF if incorrect file type, -1 on open failure, 0 otherwise
*/
int
sniff_rinit(name, linktypep, thiszonep, snaplenp, precisionp)
char *name;
int *linktypep, *thiszonep, *snaplenp, *precisionp;
{
char buf[128];
void print2hexwindow();
/* Sniffer saves times in local format */
*thiszonep = 0;
low_offset = *(((char *)&(byte_order))+0);
high_offset = *(((char *)&(byte_order))+1);
/* the Sniffer file format doesn't give the max frame size */
/* Each frame has a true size and a stored size, so we */
/* have to set 'snaplen' to the max possible size */
*snaplenp = MAXPKT;
if (name[0]=='-' && name[1]=='\0')
rfd = stdin;
else if ( !(rfd=fopen(name, "r")) ) {
fprintf(stderr,"tcpdump: fopen: ");
perror(name);
return(1);
}
/* read in magic header */
fread(buf,sizeof(s_magic),1,rfd);
if(strncmp(buf,s_magic,sizeof(s_magic)))
return SFERR_BADF; /* no magic header found */
hdr_load();
if (s.s_hdr.type != 1 ) /* version record should be next */
return SFERR_BADF;
getbs(V.maj_vers, rfd);
getbs(V.min_vers, rfd);
getbs(V.time, rfd);
getbs(V.date, rfd);
V.type = getc(rfd);
V.network = getc(rfd);
V.format = getc(rfd);
V.timeunit = getc(rfd);
getbs(V.rsvd[0], rfd);
getbs(V.rsvd[1], rfd);
getbs(V.rsvd[2], rfd);
sprintf(buf,"Sniffer save file\nCreated by Version %d.%d on %d-%d-%d at %d:%d:%d",
V.maj_vers,V.min_vers,
((V.date&0x1e0)>>5),(V.date&0x1f),
((V.date&0xfe00)>>9)+1980,
(V.time&0xfc00)>>11,
(V.time&0x7e0)>>5,
(V.time&0x1f)<<1);
#ifdef TCPVIEW
print2hexwindow(buf);
#else
fputs(buf,stderr);
#endif
if(V.timeunit==2)
*precisionp = 5;
else
*precisionp = 6;
Timeunit = Usec[V.timeunit];
if (linktypep) {
switch (V.network) {
case 1:
*linktypep = DLT_EN10MB;
break;
case 2:
*linktypep = DLT_ARCNET;
break;
case 7:
*linktypep = DLT_PPP;
break;
case 8:
*linktypep = DLT_SLIP;
break;
default:
*linktypep = 0;
}
}
return 0;
}
int sniff_winit(name, linktype, thiszone, snaplen, precision)
char *name;
int linktype, thiszone, snaplen, precision;
{
time_t tt;
struct tm *t;
TimeZoneOffset = thiszone;
low_offset = *(((char *)&(byte_order))+0);
high_offset = *(((char *)&(byte_order))+1);
frame_count = 0;
if (name[0]=='-' && name[1]=='\0')
sfd = stdout;
else if ( !(sfd=fopen(name ? name : "snif.enc", "w")) ) {
fprintf(stderr,"tcpdump: fopen: ");
perror(name);
#ifdef TCPVIEW
return(1);
#else
exit(1);
#endif
}
/* write the magic header */
fwrite(s_magic, sizeof(s_magic), 1, sfd);
/* write a version header */
s.s_hdr.type = 1;
s.s_hdr.len = 18;
hdr_print();
/* write the version record */
V.maj_vers = 3;
V.min_vers = 9;
(void)time( &tt );
t = localtime( &tt );
V.time = t->tm_sec>>1 | t->tm_min<<5 | t->tm_hour<<11;
V.date = t->tm_mday | (t->tm_mon+1)<<5 | (t->tm_year-80)<<9;
V.type = (char)4;
switch ( linktype ) {
case DLT_EN10MB:
V.network = (char)1;
break;
case DLT_ARCNET:
V.network = (char)2;
break;
case DLT_SLIP:
V.network = (char)7;
break;
case DLT_PPP:
V.network = (char)8;
break;
default:
V.network = (char)9;
break;
}
V.format = (char)1;
if( precision > 5)
V.timeunit = (char)4;
else
V.timeunit = (char)2;
Timeunit = Usec[V.timeunit];
ver_print();
/* write fake type 6 record */
/* fwrite(s_sixrec, sizeof(s_sixrec), 1, sfd);
last_offset = sizeof(s_magic) + sizeof(s_sixrec) + sizeof(struct v) + 6; */
last_offset = sizeof(s_magic) + sizeof(struct v) + 6;
s.s_hdr.type = 4;
return 0;
}
void sniff_end(mode)
char *mode;
{
if (*mode == 'w') { /* end writing sniffer file */
s.s_hdr.type = 3; /* EOF */
s.s_hdr.len = 0;
fseek(sfd, last_offset, 0); /* undo any partial records */
hdr_print();
fclose(sfd);
fprintf(stderr,"wrote %d sniffer frames\n", frame_count);
}
if (*mode == 'r') { /* end re-reading a sniffer file */
fclose(rfd);
}
}
void
sniff_write(sp, tvp, length, caplen)
u_char *sp;
struct timeval *tvp;
int length;
int caplen;
{
double t, x;
int days;
extern int Wflag;
tvp->tv_sec += TimeZoneOffset;
x = 4.0*(double)(1<<30);
t = (double)(tvp->tv_sec)*1.0e6 + (double)(tvp->tv_usec);
days = t/8.64e10;
t -= days*8.64e10;
t /= Timeunit;
s.s_dh.time_high = (u_short)(t/x);
t -= (double)(s.s_dh.time_high)*x;
s.s_dh.time_med = (u_short)(t/(double)(1<<16));
s.s_dh.time_low = (u_short)(t - (double)(s.s_dh.time_med)*(double)(1<<16));
s.s_hdr.len = (s.s_dh.size = caplen) + 14;
s.s_dh.truesize = s.s_dh.size < length ? length : 0;
if (HIGH_OFFSET)
fwrite(&s, sizeof(s), 1, sfd);
else
hdr_print(), dh_print();
fwrite(sp, s.s_dh.size, 1, sfd);
last_offset += sizeof(s.s_dh) + sizeof(s.s_hdr) + s.s_dh.size;
++frame_count;
#ifndef TCPVIEW
switch (Wflag) {
case 2: /* a dot to stderr */
write(2,".",1);
break;
case 3: /* the normal print too */
ether_if_print(sp, tvp, length, caplen);
break;
}
#endif
return;
}
#ifndef TCPVIEW
int sniff_read(filtp, cnt, snaplen, printit)
struct bpf_program *filtp;
int cnt, snaplen;
void (*printit)();
{
struct packet_header h;
u_char *buf;
struct bpf_insn *fcode = filtp->bf_insns;
buf = (u_char *)malloc(snaplen);
if (buf==NULL)
return 1;
while(sniff_next_packet(&h, buf) == 0) {
if (bpf_filter(fcode,buf,h.len,h.caplen)) {
if (cnt >= 0 && --cnt < 0 )
break;
(*printit)(buf, &h.ts, h.len, h.caplen);
}
}
free((char *)buf);
return 0;
}
#endif /* not TCPVIEW */
int
sniff_next_packet(hdr, buf, buflen)
struct packet_header *hdr;
u_char *buf;
int buflen;
{
double t,x;
hdr_load();
if (s.s_hdr.type == 3 || s.s_hdr.type == EOF )
return 1;
if (s.s_hdr.type == 6 ) { /* infamous type 6 header detected */
if(fread(buf,s.s_hdr.len,1,rfd)==0)
return 1;
return (sniff_next_packet(hdr,buf));
}
dh_load();
if(fread(buf, s.s_dh.size, 1, rfd)==0)
return 1;
x = 4.0*(double)(1<<30);
t = (double)s.s_dh.time_low+(double)(s.s_dh.time_med)*65536.0 +
(double)s.s_dh.time_high*x;
t = t/1000000.0*Timeunit; /* set t equal to number of secs */
hdr->ts.tv_sec = (long)t;
hdr->ts.tv_usec = (u_long)((t-(double)(hdr->ts.tv_sec))*1.0e6);
hdr->len = s.s_dh.truesize ? s.s_dh.truesize : s.s_dh.size;
hdr->caplen = s.s_dh.size;
return 0;
}
static void
hdr_print() {
register FILE *f = sfd;
putbs(s.s_hdr.type, f);
putbs(s.s_hdr.len, f);
putbs(s.s_hdr.rsvd, f);
}
static void
hdr_load() {
register FILE *f = rfd;
getbs(s.s_hdr.type, f);
getbs(s.s_hdr.len, f);
getbs(s.s_hdr.rsvd, f);
}
static void
dh_print() {
register FILE *f = sfd;
putbs(s.s_dh.time_low, f);
putbs(s.s_dh.time_med, f);
putbs(s.s_dh.time_high, f);
putbs(s.s_dh.size, f);
putc(s.s_dh.fs, f);
putc(s.s_dh.flags, f);
putbs(s.s_dh.truesize, f);
putbs(s.s_dh.rsvd, f);
}
static void
ver_print() {
register FILE *f = sfd;
putbs(V.maj_vers, f);
putbs(V.min_vers, f);
putbs(V.time, f);
putbs(V.date, f);
putc(V.type, f);
putc(V.network, f);
putc(V.format, f);
putc(V.timeunit, f);
putbs(V.rsvd[1], f);
putbs(V.rsvd[2], f);
putbs(V.rsvd[3], f);
}
static void
dh_load() {
register FILE *f = rfd;
getbs(s.s_dh.time_low, f);
getbs(s.s_dh.time_med, f);
getbs(s.s_dh.time_high, f);
getbs(s.s_dh.size, f);
s.s_dh.fs = getc(f);
s.s_dh.flags = getc(f);
getbs(s.s_dh.truesize, f);
getbs(s.s_dh.rsvd, f);
}
syntax highlighted by Code2HTML, v. 0.9.1