savefile.c revision 26175
117683Spst/*
217683Spst * Copyright (c) 1993, 1994, 1995, 1996
317683Spst *	The Regents of the University of California.  All rights reserved.
417683Spst *
517683Spst * Redistribution and use in source and binary forms, with or without
617683Spst * modification, are permitted provided that: (1) source code distributions
717683Spst * retain the above copyright notice and this paragraph in its entirety, (2)
817683Spst * distributions including binary code include the above copyright notice and
917683Spst * this paragraph in its entirety in the documentation or other materials
1017683Spst * provided with the distribution, and (3) all advertising materials mentioning
1117683Spst * features or use of this software display the following acknowledgement:
1217683Spst * ``This product includes software developed by the University of California,
1317683Spst * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
1417683Spst * the University nor the names of its contributors may be used to endorse
1517683Spst * or promote products derived from this software without specific prior
1617683Spst * written permission.
1717683Spst * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
1817683Spst * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1917683Spst * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2026175Sfenner *
2117683Spst * savefile.c - supports offline use of tcpdump
2217683Spst *	Extraction/creation by Jeffrey Mogul, DECWRL
2317683Spst *	Modified by Steve McCanne, LBL.
2417683Spst *
2517683Spst * Used to save the received packet headers, after filtering, to
2617683Spst * a file, and then read them later.
2717683Spst * The first record in the file contains saved values for the machine
2817683Spst * dependent values so we can print the dump file on any architecture.
2917683Spst */
3017683Spst
3126175Sfenner#ifndef lint
3226175Sfennerstatic const char rcsid[] =
3326175Sfenner    "@(#) $Header: savefile.c,v 1.36 96/12/10 23:15:02 leres Exp $ (LBL)";
3426175Sfenner#endif
3526175Sfenner
3617683Spst#include <sys/types.h>
3717683Spst#include <sys/time.h>
3817683Spst
3917683Spst#include <errno.h>
4017683Spst#include <memory.h>
4117683Spst#include <stdio.h>
4217683Spst#include <stdlib.h>
4317683Spst#include <unistd.h>
4417683Spst
4517683Spst#include "pcap-int.h"
4617683Spst
4717683Spst#include "gnuc.h"
4817683Spst#ifdef HAVE_OS_PROTO_H
4917683Spst#include "os-proto.h"
5017683Spst#endif
5117683Spst
5217683Spst#define TCPDUMP_MAGIC 0xa1b2c3d4
5317683Spst
5417683Spst/*
5517683Spst * We use the "receiver-makes-right" approach to byte order,
5617683Spst * because time is at a premium when we are writing the file.
5717683Spst * In other words, the pcap_file_header and pcap_pkthdr,
5817683Spst * records are written in host byte order.
5917683Spst * Note that the packets are always written in network byte order.
6017683Spst *
6117683Spst * ntoh[ls] aren't sufficient because we might need to swap on a big-endian
6217683Spst * machine (if the file was written in little-end order).
6317683Spst */
6417683Spst#define	SWAPLONG(y) \
6517683Spst((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
6617683Spst#define	SWAPSHORT(y) \
6726175Sfenner	( (((y)&0xff)<<8) | ((u_short)((y)&0xff00)>>8) )
6817683Spst
6917683Spst#define SFERR_TRUNC		1
7017683Spst#define SFERR_BADVERSION	2
7117683Spst#define SFERR_BADF		3
7217683Spst#define SFERR_EOF		4 /* not really an error, just a status */
7317683Spst
7417683Spststatic int
7517683Spstsf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
7617683Spst{
7717683Spst	struct pcap_file_header hdr;
7817683Spst
7917683Spst	hdr.magic = TCPDUMP_MAGIC;
8017683Spst	hdr.version_major = PCAP_VERSION_MAJOR;
8117683Spst	hdr.version_minor = PCAP_VERSION_MINOR;
8217683Spst
8317683Spst	hdr.thiszone = thiszone;
8417683Spst	hdr.snaplen = snaplen;
8517683Spst	hdr.sigfigs = 0;
8617683Spst	hdr.linktype = linktype;
8717683Spst
8817683Spst	if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
8917683Spst		return (-1);
9017683Spst
9117683Spst	return (0);
9217683Spst}
9317683Spst
9417683Spststatic void
9517683Spstswap_hdr(struct pcap_file_header *hp)
9617683Spst{
9717683Spst	hp->version_major = SWAPSHORT(hp->version_major);
9817683Spst	hp->version_minor = SWAPSHORT(hp->version_minor);
9917683Spst	hp->thiszone = SWAPLONG(hp->thiszone);
10017683Spst	hp->sigfigs = SWAPLONG(hp->sigfigs);
10117683Spst	hp->snaplen = SWAPLONG(hp->snaplen);
10217683Spst	hp->linktype = SWAPLONG(hp->linktype);
10317683Spst}
10417683Spst
10517683Spstpcap_t *
10617683Spstpcap_open_offline(char *fname, char *errbuf)
10717683Spst{
10817683Spst	register pcap_t *p;
10917683Spst	register FILE *fp;
11017683Spst	struct pcap_file_header hdr;
11117683Spst	int linklen;
11217683Spst
11317683Spst	p = (pcap_t *)malloc(sizeof(*p));
11417683Spst	if (p == NULL) {
11517683Spst		strcpy(errbuf, "out of swap");
11617683Spst		return (NULL);
11717683Spst	}
11817683Spst
11917683Spst	memset((char *)p, 0, sizeof(*p));
12017683Spst	/*
12117683Spst	 * Set this field so we don't close stdin in pcap_close!
12217683Spst	 */
12317683Spst	p->fd = -1;
12417683Spst
12517683Spst	if (fname[0] == '-' && fname[1] == '\0')
12617683Spst		fp = stdin;
12717683Spst	else {
12817683Spst		fp = fopen(fname, "r");
12917683Spst		if (fp == NULL) {
13017683Spst			sprintf(errbuf, "%s: %s", fname, pcap_strerror(errno));
13117683Spst			goto bad;
13217683Spst		}
13317683Spst	}
13417683Spst	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
13517683Spst		sprintf(errbuf, "fread: %s", pcap_strerror(errno));
13617683Spst		goto bad;
13717683Spst	}
13817683Spst	if (hdr.magic != TCPDUMP_MAGIC) {
13917683Spst		if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) {
14017683Spst			sprintf(errbuf, "bad dump file format");
14117683Spst			goto bad;
14217683Spst		}
14317683Spst		p->sf.swapped = 1;
14417683Spst		swap_hdr(&hdr);
14517683Spst	}
14617683Spst	if (hdr.version_major < PCAP_VERSION_MAJOR) {
14717683Spst		sprintf(errbuf, "archaic file format");
14817683Spst		goto bad;
14917683Spst	}
15017683Spst	p->tzoff = hdr.thiszone;
15117683Spst	p->snapshot = hdr.snaplen;
15217683Spst	p->linktype = hdr.linktype;
15317683Spst	p->sf.rfile = fp;
15417683Spst	p->bufsize = hdr.snaplen;
15517683Spst
15617683Spst	/* Align link header as required for proper data alignment */
15717683Spst	/* XXX should handle all types */
15817683Spst	switch (p->linktype) {
15917683Spst
16017683Spst	case DLT_EN10MB:
16117683Spst		linklen = 14;
16217683Spst		break;
16317683Spst
16417683Spst	case DLT_FDDI:
16517683Spst		linklen = 13 + 8;	/* fddi_header + llc */
16617683Spst		break;
16717683Spst
16817683Spst	case DLT_NULL:
16917683Spst	default:
17017683Spst		linklen = 0;
17117683Spst		break;
17217683Spst	}
17317683Spst
17417683Spst	p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT);
17517683Spst	p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
17617683Spst	p->sf.version_major = hdr.version_major;
17717683Spst	p->sf.version_minor = hdr.version_minor;
17817683Spst#ifdef PCAP_FDDIPAD
17917683Spst	/* XXX padding only needed for kernel fcode */
18017683Spst	pcap_fddipad = 0;
18117683Spst#endif
18217683Spst
18317683Spst	return (p);
18417683Spst bad:
18517683Spst	free(p);
18617683Spst	return (NULL);
18717683Spst}
18817683Spst
18917683Spst/*
19017683Spst * Read sf_readfile and return the next packet.  Return the header in hdr
19117683Spst * and the contents in buf.  Return 0 on success, SFERR_EOF if there were
19217683Spst * no more packets, and SFERR_TRUNC if a partial packet was encountered.
19317683Spst */
19417683Spststatic int
19517683Spstsf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen)
19617683Spst{
19717683Spst	FILE *fp = p->sf.rfile;
19817683Spst
19917683Spst	/* read the stamp */
20017683Spst	if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) {
20117683Spst		/* probably an EOF, though could be a truncated packet */
20217683Spst		return (1);
20317683Spst	}
20417683Spst
20517683Spst	if (p->sf.swapped) {
20617683Spst		/* these were written in opposite byte order */
20717683Spst		hdr->caplen = SWAPLONG(hdr->caplen);
20817683Spst		hdr->len = SWAPLONG(hdr->len);
20917683Spst		hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
21017683Spst		hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
21117683Spst	}
21217683Spst	/*
21317683Spst	 * We interchanged the caplen and len fields at version 2.3,
21417683Spst	 * in order to match the bpf header layout.  But unfortunately
21517683Spst	 * some files were written with version 2.3 in their headers
21617683Spst	 * but without the interchanged fields.
21717683Spst	 */
21817683Spst	if (p->sf.version_minor < 3 ||
21917683Spst	    (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) {
22017683Spst		int t = hdr->caplen;
22117683Spst		hdr->caplen = hdr->len;
22217683Spst		hdr->len = t;
22317683Spst	}
22417683Spst
22517683Spst	if (hdr->caplen > buflen) {
22617683Spst		/*
22717683Spst		 * This can happen due to Solaris 2.3 systems tripping
22817683Spst		 * over the BUFMOD problem and not setting the snapshot
22917683Spst		 * correctly in the savefile header.  If the caplen isn't
23017683Spst		 * grossly wrong, try to salvage.
23117683Spst		 */
23217683Spst		static u_char *tp = NULL;
23317683Spst		static int tsize = 0;
23417683Spst
23526175Sfenner		if (hdr->caplen > 65535) {
23626175Sfenner			sprintf(p->errbuf, "bogus savefile header");
23726175Sfenner			return (-1);
23826175Sfenner		}
23917683Spst		if (tsize < hdr->caplen) {
24017683Spst			tsize = ((hdr->caplen + 1023) / 1024) * 1024;
24117683Spst			if (tp != NULL)
24217683Spst				free((u_char *)tp);
24317683Spst			tp = (u_char *)malloc(tsize);
24417683Spst			if (tp == NULL) {
24526175Sfenner				tsize = 0;
24617683Spst				sprintf(p->errbuf, "BUFMOD hack malloc");
24717683Spst				return (-1);
24817683Spst			}
24917683Spst		}
25017683Spst		if (fread((char *)tp, hdr->caplen, 1, fp) != 1) {
25117683Spst			sprintf(p->errbuf, "truncated dump file");
25217683Spst			return (-1);
25317683Spst		}
25426175Sfenner		/*
25526175Sfenner		 * We can only keep up to buflen bytes.  Since caplen > buflen
25626175Sfenner		 * is exactly how we got here, we know we can only keep the
25726175Sfenner		 * first buflen bytes and must drop the remainder.  Adjust
25826175Sfenner		 * caplen accordingly, so we don't get confused later as
25926175Sfenner		 * to how many bytes we have to play with.
26026175Sfenner		 */
26126175Sfenner		hdr->caplen = buflen;
26217683Spst		memcpy((char *)buf, (char *)tp, buflen);
26317683Spst
26417683Spst	} else {
26517683Spst		/* read the packet itself */
26617683Spst
26717683Spst		if (fread((char *)buf, hdr->caplen, 1, fp) != 1) {
26817683Spst			sprintf(p->errbuf, "truncated dump file");
26917683Spst			return (-1);
27017683Spst		}
27117683Spst	}
27217683Spst	return (0);
27317683Spst}
27417683Spst
27517683Spst/*
27617683Spst * Print out packets stored in the file initialized by sf_read_init().
27717683Spst * If cnt > 0, return after 'cnt' packets, otherwise continue until eof.
27817683Spst */
27917683Spstint
28017683Spstpcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
28117683Spst{
28217683Spst	struct bpf_insn *fcode = p->fcode.bf_insns;
28317683Spst	int status = 0;
28417683Spst	int n = 0;
28517683Spst
28617683Spst	while (status == 0) {
28717683Spst		struct pcap_pkthdr h;
28817683Spst
28917683Spst		status = sf_next_packet(p, &h, p->buffer, p->bufsize);
29017683Spst		if (status) {
29117683Spst			if (status == 1)
29217683Spst				return (0);
29317683Spst			return (status);
29417683Spst		}
29517683Spst
29617683Spst		if (fcode == NULL ||
29717683Spst		    bpf_filter(fcode, p->buffer, h.len, h.caplen)) {
29817683Spst			(*callback)(user, &h, p->buffer);
29917683Spst			if (++n >= cnt && cnt > 0)
30017683Spst				break;
30117683Spst		}
30217683Spst	}
30317683Spst	/*XXX this breaks semantics tcpslice expects */
30417683Spst	return (n);
30517683Spst}
30617683Spst
30717683Spst/*
30817683Spst * Output a packet to the initialized dump file.
30917683Spst */
31017683Spstvoid
31117683Spstpcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
31217683Spst{
31317683Spst	register FILE *f;
31417683Spst
31517683Spst	f = (FILE *)user;
31617683Spst	/* XXX we should check the return status */
31717683Spst	(void)fwrite((char *)h, sizeof(*h), 1, f);
31817683Spst	(void)fwrite((char *)sp, h->caplen, 1, f);
31917683Spst}
32017683Spst
32117683Spst/*
32217683Spst * Initialize so that sf_write() will output to the file named 'fname'.
32317683Spst */
32417683Spstpcap_dumper_t *
32517683Spstpcap_dump_open(pcap_t *p, char *fname)
32617683Spst{
32717683Spst	FILE *f;
32817683Spst	if (fname[0] == '-' && fname[1] == '\0')
32917683Spst		f = stdout;
33017683Spst	else {
33117683Spst		f = fopen(fname, "w");
33217683Spst		if (f == NULL) {
33317683Spst			sprintf(p->errbuf, "%s: %s",
33417683Spst			    fname, pcap_strerror(errno));
33517683Spst			return (NULL);
33617683Spst		}
33717683Spst	}
33817683Spst	(void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot);
33917683Spst	return ((pcap_dumper_t *)f);
34017683Spst}
34117683Spst
34217683Spstvoid
34317683Spstpcap_dump_close(pcap_dumper_t *p)
34417683Spst{
34517683Spst
34617683Spst#ifdef notyet
34717683Spst	if (ferror((FILE *)p))
34817683Spst		return-an-error;
34917683Spst	/* XXX should check return from fclose() too */
35017683Spst#endif
35117683Spst	(void)fclose((FILE *)p);
35217683Spst}
353