#ifndef lint
static const char rcsid[] _U_ =
"@(#) $Header: /cvs/root/libpcap/libpcap/savefile.c,v 1.1.1.3 2004/02/05 19:22:28 rbraun Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pcap-int.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
#endif
#define TCPDUMP_MAGIC 0xa1b2c3d4
#define PATCHED_TCPDUMP_MAGIC 0xa1b2cd34
#define SWAPLONG(y) \
((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
#define SWAPSHORT(y) \
( (((y)&0xff)<<8) | ((u_short)((y)&0xff00)>>8) )
#define SFERR_TRUNC 1
#define SFERR_BADVERSION 2
#define SFERR_BADF 3
#define SFERR_EOF 4
#define LINKTYPE_NULL DLT_NULL
#define LINKTYPE_ETHERNET DLT_EN10MB
#define LINKTYPE_EXP_ETHERNET DLT_EN3MB
#define LINKTYPE_AX25 DLT_AX25
#define LINKTYPE_PRONET DLT_PRONET
#define LINKTYPE_CHAOS DLT_CHAOS
#define LINKTYPE_TOKEN_RING DLT_IEEE802
#define LINKTYPE_ARCNET DLT_ARCNET
#define LINKTYPE_SLIP DLT_SLIP
#define LINKTYPE_PPP DLT_PPP
#define LINKTYPE_FDDI DLT_FDDI
#define LINKTYPE_PPP_HDLC 50
#define LINKTYPE_PPP_ETHER 51
#define LINKTYPE_ATM_RFC1483 100
#define LINKTYPE_RAW 101
#define LINKTYPE_SLIP_BSDOS 102
#define LINKTYPE_PPP_BSDOS 103
#define LINKTYPE_C_HDLC 104
#define LINKTYPE_IEEE802_11 105
#define LINKTYPE_ATM_CLIP 106
#define LINKTYPE_FRELAY 107
#define LINKTYPE_LOOP 108
#define LINKTYPE_ENC 109
#define LINKTYPE_LINUX_SLL 113
#define LINKTYPE_LTALK 114
#define LINKTYPE_ECONET 115
#define LINKTYPE_PFLOG 117
#define LINKTYPE_CISCO_IOS 118
#define LINKTYPE_PRISM_HEADER 119
#define LINKTYPE_AIRONET_HEADER 120
#define LINKTYPE_IP_OVER_FC 122
#define LINKTYPE_SUNATM 123
#define LINKTYPE_IEEE802_11_RADIO 127
#define LINKTYPE_TZSP 128
#define LINKTYPE_ARCNET_LINUX 129
#define LINKTYPE_JUNIPER_MLPPP 130
#define LINKTYPE_JUNIPER_MLFR 131
#define LINKTYPE_JUNIPER_ES 132
#define LINKTYPE_JUNIPER_GGSN 133
#define LINKTYPE_JUNIPER_MFR 134
#define LINKTYPE_JUNIPER_ATM2 135
#define LINKTYPE_JUNIPER_SERVICES 136
#define LINKTYPE_JUNIPER_ATM1 137
#define LINKTYPE_APPLE_IP_OVER_IEEE1394 138
#define LINKTYPE_RAWSS7 139
#define LINKTYPE_RAWSS7_MTP2 140
#define LINKTYPE_RAWSS7_MTP3 141
#define LINKTYPE_RAWSS7_SCCP 142
#define LINKTYPE_LINUX_IRDA 144
#define LINKTYPE_LANE8023 110
#define LINKTYPE_HIPPI 111
#define LINKTYPE_HDLC 112
#define LINKTYPE_IPFILTER 116
#define LINKTYPE_HHDLC 121
#define LINKTYPE_RIO 124
#define LINKTYPE_PCI_EXP 125
#define LINKTYPE_AURORA 126
#define LINKTYPE_DOCSIS 143
#define LINKTYPE_IBM_SP 145
#define LINKTYPE_IBM_SN 146
static struct linktype_map {
int dlt;
int linktype;
} map[] = {
{ DLT_NULL, LINKTYPE_NULL },
{ DLT_EN10MB, LINKTYPE_ETHERNET },
{ DLT_EN3MB, LINKTYPE_EXP_ETHERNET },
{ DLT_AX25, LINKTYPE_AX25 },
{ DLT_PRONET, LINKTYPE_PRONET },
{ DLT_CHAOS, LINKTYPE_CHAOS },
{ DLT_IEEE802, LINKTYPE_TOKEN_RING },
{ DLT_ARCNET, LINKTYPE_ARCNET },
{ DLT_SLIP, LINKTYPE_SLIP },
{ DLT_PPP, LINKTYPE_PPP },
{ DLT_FDDI, LINKTYPE_FDDI },
#ifdef DLT_FR
{ DLT_FR, LINKTYPE_FRELAY },
#endif
{ DLT_ATM_RFC1483, LINKTYPE_ATM_RFC1483 },
{ DLT_RAW, LINKTYPE_RAW },
{ DLT_SLIP_BSDOS, LINKTYPE_SLIP_BSDOS },
{ DLT_PPP_BSDOS, LINKTYPE_PPP_BSDOS },
{ DLT_C_HDLC, LINKTYPE_C_HDLC },
{ DLT_ATM_CLIP, LINKTYPE_ATM_CLIP },
{ DLT_PPP_SERIAL, LINKTYPE_PPP_HDLC },
{ DLT_PPP_ETHER, LINKTYPE_PPP_ETHER },
{ DLT_IEEE802_11, LINKTYPE_IEEE802_11 },
{ DLT_FRELAY, LINKTYPE_FRELAY },
{ DLT_LOOP, LINKTYPE_LOOP },
{ DLT_LINUX_SLL, LINKTYPE_LINUX_SLL },
{ DLT_LTALK, LINKTYPE_LTALK },
{ DLT_ECONET, LINKTYPE_ECONET },
{ DLT_PFLOG, LINKTYPE_PFLOG },
{ DLT_CISCO_IOS, LINKTYPE_CISCO_IOS },
{ DLT_PRISM_HEADER, LINKTYPE_PRISM_HEADER },
{ DLT_AIRONET_HEADER, LINKTYPE_AIRONET_HEADER },
{ DLT_HHDLC, LINKTYPE_HHDLC },
{ DLT_IP_OVER_FC, LINKTYPE_IP_OVER_FC },
{ DLT_SUNATM, LINKTYPE_SUNATM },
{ DLT_RIO, LINKTYPE_RIO },
{ DLT_PCI_EXP, LINKTYPE_PCI_EXP },
{ DLT_AURORA, LINKTYPE_AURORA },
{ DLT_IEEE802_11_RADIO, LINKTYPE_IEEE802_11_RADIO },
{ DLT_TZSP, LINKTYPE_TZSP },
{ DLT_ARCNET_LINUX, LINKTYPE_ARCNET_LINUX },
{ DLT_JUNIPER_MLPPP, LINKTYPE_JUNIPER_MLPPP },
{ DLT_JUNIPER_MLFR, LINKTYPE_JUNIPER_MLFR },
{ DLT_JUNIPER_ES, LINKTYPE_JUNIPER_ES },
{ DLT_JUNIPER_GGSN, LINKTYPE_JUNIPER_GGSN },
{ DLT_JUNIPER_MFR, LINKTYPE_JUNIPER_MFR },
{ DLT_JUNIPER_ATM2, LINKTYPE_JUNIPER_ATM2 },
{ DLT_JUNIPER_SERVICES, LINKTYPE_JUNIPER_SERVICES },
{ DLT_JUNIPER_ATM1, LINKTYPE_JUNIPER_ATM1 },
{ DLT_APPLE_IP_OVER_IEEE1394, LINKTYPE_APPLE_IP_OVER_IEEE1394 },
{ DLT_DOCSIS, LINKTYPE_DOCSIS },
{ DLT_LINUX_IRDA, LINKTYPE_LINUX_IRDA },
{ DLT_IBM_SP, LINKTYPE_IBM_SP },
{ DLT_IBM_SN, LINKTYPE_IBM_SN },
{ -1, -1 }
};
static int
dlt_to_linktype(int dlt)
{
int i;
for (i = 0; map[i].dlt != -1; i++) {
if (map[i].dlt == dlt)
return (map[i].linktype);
}
return (-1);
}
static int
linktype_to_dlt(int linktype)
{
int i;
for (i = 0; map[i].linktype != -1; i++) {
if (map[i].linktype == linktype)
return (map[i].dlt);
}
return linktype;
}
static int
sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
{
struct pcap_file_header hdr;
hdr.magic = TCPDUMP_MAGIC;
hdr.version_major = PCAP_VERSION_MAJOR;
hdr.version_minor = PCAP_VERSION_MINOR;
hdr.thiszone = thiszone;
hdr.snaplen = snaplen;
hdr.sigfigs = 0;
hdr.linktype = linktype;
if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
return (-1);
return (0);
}
static void
swap_hdr(struct pcap_file_header *hp)
{
hp->version_major = SWAPSHORT(hp->version_major);
hp->version_minor = SWAPSHORT(hp->version_minor);
hp->thiszone = SWAPLONG(hp->thiszone);
hp->sigfigs = SWAPLONG(hp->sigfigs);
hp->snaplen = SWAPLONG(hp->snaplen);
hp->linktype = SWAPLONG(hp->linktype);
}
static int
sf_getnonblock(pcap_t *p, char *errbuf)
{
return (0);
}
static int
sf_setnonblock(pcap_t *p, int nonblock, char *errbuf)
{
return (0);
}
static int
sf_stats(pcap_t *p, struct pcap_stat *ps)
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"Statistics aren't available from savefiles");
return (-1);
}
static void
sf_close(pcap_t *p)
{
if (p->sf.rfile != stdin)
(void)fclose(p->sf.rfile);
if (p->sf.base != NULL)
free(p->sf.base);
}
pcap_t *
pcap_open_offline(const char *fname, char *errbuf)
{
register pcap_t *p;
register FILE *fp;
struct pcap_file_header hdr;
bpf_u_int32 magic;
int linklen;
p = (pcap_t *)malloc(sizeof(*p));
if (p == NULL) {
strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
return (NULL);
}
memset((char *)p, 0, sizeof(*p));
if (fname[0] == '-' && fname[1] == '\0')
fp = stdin;
else {
#ifndef WIN32
fp = fopen(fname, "r");
#else
fp = fopen(fname, "rb");
#endif
if (fp == NULL) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname,
pcap_strerror(errno));
goto bad;
}
}
if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s",
pcap_strerror(errno));
goto bad;
}
magic = hdr.magic;
if (magic != TCPDUMP_MAGIC && magic != PATCHED_TCPDUMP_MAGIC) {
magic = SWAPLONG(magic);
if (magic != TCPDUMP_MAGIC && magic != PATCHED_TCPDUMP_MAGIC) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bad dump file format");
goto bad;
}
p->sf.swapped = 1;
swap_hdr(&hdr);
}
if (magic == PATCHED_TCPDUMP_MAGIC) {
p->sf.hdrsize = sizeof(struct pcap_sf_patched_pkthdr);
} else
p->sf.hdrsize = sizeof(struct pcap_sf_pkthdr);
if (hdr.version_major < PCAP_VERSION_MAJOR) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format");
goto bad;
}
p->tzoff = hdr.thiszone;
p->snapshot = hdr.snaplen;
p->linktype = linktype_to_dlt(hdr.linktype);
p->sf.rfile = fp;
#ifndef WIN32
p->bufsize = hdr.snaplen;
#else
p->bufsize = hdr.snaplen+sizeof(struct pcap_pkthdr);
#endif
switch (p->linktype) {
case DLT_EN10MB:
linklen = 14;
break;
case DLT_FDDI:
linklen = 13 + 8;
break;
case DLT_NULL:
default:
linklen = 0;
break;
}
if (p->bufsize < 0)
p->bufsize = BPF_MAXBUFSIZE;
p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT);
if (p->sf.base == NULL) {
strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
goto bad;
}
p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
p->sf.version_major = hdr.version_major;
p->sf.version_minor = hdr.version_minor;
#ifdef PCAP_FDDIPAD
pcap_fddipad = 0;
#endif
switch (hdr.version_major) {
case 2:
if (hdr.version_minor < 3)
p->sf.lengths_swapped = SWAPPED;
else if (hdr.version_minor == 3)
p->sf.lengths_swapped = MAYBE_SWAPPED;
else
p->sf.lengths_swapped = NOT_SWAPPED;
break;
case 543:
p->sf.lengths_swapped = SWAPPED;
break;
default:
p->sf.lengths_swapped = NOT_SWAPPED;
break;
}
#ifndef WIN32
p->selectable_fd = fileno(fp);
#endif
p->read_op = pcap_offline_read;
p->setfilter_op = install_bpf_program;
p->set_datalink_op = NULL;
p->getnonblock_op = sf_getnonblock;
p->setnonblock_op = sf_setnonblock;
p->stats_op = sf_stats;
p->close_op = sf_close;
return (p);
bad:
if(fp)
fclose(fp);
free(p);
return (NULL);
}
static int
sf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, u_int buflen)
{
struct pcap_sf_patched_pkthdr sf_hdr;
FILE *fp = p->sf.rfile;
size_t amt_read;
bpf_u_int32 t;
amt_read = fread(&sf_hdr, 1, p->sf.hdrsize, fp);
if (amt_read != p->sf.hdrsize) {
if (ferror(fp)) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"error reading dump file: %s",
pcap_strerror(errno));
return (-1);
} else {
if (amt_read != 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %d header bytes, only got %lu",
p->sf.hdrsize, (unsigned long)amt_read);
return (-1);
}
return (1);
}
}
if (p->sf.swapped) {
hdr->caplen = SWAPLONG(sf_hdr.caplen);
hdr->len = SWAPLONG(sf_hdr.len);
hdr->ts.tv_sec = SWAPLONG(sf_hdr.ts.tv_sec);
hdr->ts.tv_usec = SWAPLONG(sf_hdr.ts.tv_usec);
} else {
hdr->caplen = sf_hdr.caplen;
hdr->len = sf_hdr.len;
hdr->ts.tv_sec = sf_hdr.ts.tv_sec;
hdr->ts.tv_usec = sf_hdr.ts.tv_usec;
}
switch (p->sf.lengths_swapped) {
case NOT_SWAPPED:
break;
case MAYBE_SWAPPED:
if (hdr->caplen <= hdr->len) {
break;
}
case SWAPPED:
t = hdr->caplen;
hdr->caplen = hdr->len;
hdr->len = t;
break;
}
if (hdr->caplen > buflen) {
static u_char *tp = NULL;
static size_t tsize = 0;
if (hdr->caplen > 65535) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"bogus savefile header");
return (-1);
}
if (tsize < hdr->caplen) {
tsize = ((hdr->caplen + 1023) / 1024) * 1024;
if (tp != NULL)
free((u_char *)tp);
tp = (u_char *)malloc(tsize);
if (tp == NULL) {
tsize = 0;
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"BUFMOD hack malloc");
return (-1);
}
}
amt_read = fread((char *)tp, 1, hdr->caplen, fp);
if (amt_read != hdr->caplen) {
if (ferror(fp)) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"error reading dump file: %s",
pcap_strerror(errno));
} else {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %u captured bytes, only got %lu",
hdr->caplen, (unsigned long)amt_read);
}
return (-1);
}
hdr->caplen = buflen;
memcpy((char *)buf, (char *)tp, buflen);
} else {
amt_read = fread((char *)buf, 1, hdr->caplen, fp);
if (amt_read != hdr->caplen) {
if (ferror(fp)) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"error reading dump file: %s",
pcap_strerror(errno));
} else {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %u captured bytes, only got %lu",
hdr->caplen, (unsigned long)amt_read);
}
return (-1);
}
}
return (0);
}
int
pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
struct bpf_insn *fcode = p->fcode.bf_insns;
int status = 0;
int n = 0;
while (status == 0) {
struct pcap_pkthdr h;
if (p->break_loop) {
if (n == 0) {
p->break_loop = 0;
return (-2);
} else
return (n);
}
status = sf_next_packet(p, &h, p->buffer, p->bufsize);
if (status) {
if (status == 1)
return (0);
return (status);
}
if (fcode == NULL ||
bpf_filter(fcode, p->buffer, h.len, h.caplen)) {
(*callback)(user, &h, p->buffer);
if (++n >= cnt && cnt > 0)
break;
}
}
return (n);
}
void
pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
register FILE *f;
struct pcap_sf_pkthdr sf_hdr;
f = (FILE *)user;
sf_hdr.ts.tv_sec = h->ts.tv_sec;
sf_hdr.ts.tv_usec = h->ts.tv_usec;
sf_hdr.caplen = h->caplen;
sf_hdr.len = h->len;
(void)fwrite(&sf_hdr, sizeof(sf_hdr), 1, f);
(void)fwrite((char *)sp, h->caplen, 1, f);
}
pcap_dumper_t *
pcap_dump_open(pcap_t *p, const char *fname)
{
FILE *f;
int linktype;
linktype = dlt_to_linktype(p->linktype);
if (linktype == -1) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"%s: link-layer type %d isn't supported in savefiles",
fname, linktype);
return (NULL);
}
if (fname[0] == '-' && fname[1] == '\0') {
f = stdout;
#ifdef WIN32
_setmode(_fileno(f), _O_BINARY);
#endif
} else {
#ifndef WIN32
f = fopen(fname, "w");
#else
f = fopen(fname, "wb");
setbuf(f, NULL);
#endif
if (f == NULL) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
fname, pcap_strerror(errno));
return (NULL);
}
}
(void)sf_write_header(f, linktype, p->tzoff, p->snapshot);
return ((pcap_dumper_t *)f);
}
FILE *
pcap_dump_file(pcap_dumper_t *p)
{
return ((FILE *)p);
}
int
pcap_dump_flush(pcap_dumper_t *p)
{
if (fflush((FILE *)p) == EOF)
return (-1);
else
return (0);
}
void
pcap_dump_close(pcap_dumper_t *p)
{
#ifdef notyet
if (ferror((FILE *)p))
return-an-error;
#endif
(void)fclose((FILE *)p);
}