savefile.c revision 17683
173134Ssobomax/*
273134Ssobomax * Copyright (c) 1993, 1994, 1995, 1996
373134Ssobomax *	The Regents of the University of California.  All rights reserved.
473134Ssobomax *
573134Ssobomax * Redistribution and use in source and binary forms, with or without
673134Ssobomax * modification, are permitted provided that: (1) source code distributions
773134Ssobomax * retain the above copyright notice and this paragraph in its entirety, (2)
873134Ssobomax * distributions including binary code include the above copyright notice and
973134Ssobomax * this paragraph in its entirety in the documentation or other materials
1073134Ssobomax * provided with the distribution, and (3) all advertising materials mentioning
1173134Ssobomax * features or use of this software display the following acknowledgement:
1273134Ssobomax * ``This product includes software developed by the University of California,
1373134Ssobomax * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
1473134Ssobomax * the University nor the names of its contributors may be used to endorse
1573134Ssobomax * or promote products derived from this software without specific prior
1673134Ssobomax * written permission.
1773134Ssobomax * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
1873134Ssobomax * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1973134Ssobomax * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2073134Ssobomax */
2173134Ssobomax#ifndef lint
2273134Ssobomaxstatic char rcsid[] =
2373134Ssobomax    "@(#)$Header: savefile.c,v 1.30 96/07/15 00:48:52 leres Exp $ (LBL)";
2473134Ssobomax#endif
2573134Ssobomax
2673134Ssobomax/*
2773134Ssobomax * savefile.c - supports offline use of tcpdump
2873134Ssobomax *	Extraction/creation by Jeffrey Mogul, DECWRL
2973134Ssobomax *	Modified by Steve McCanne, LBL.
3073134Ssobomax *
3173134Ssobomax * Used to save the received packet headers, after filtering, to
3273134Ssobomax * a file, and then read them later.
3373134Ssobomax * The first record in the file contains saved values for the machine
3473134Ssobomax * dependent values so we can print the dump file on any architecture.
3573134Ssobomax */
3673134Ssobomax
3773134Ssobomax#include <sys/types.h>
3873134Ssobomax#include <sys/time.h>
3973134Ssobomax
4073134Ssobomax#include <errno.h>
4173134Ssobomax#include <memory.h>
4273134Ssobomax#include <stdio.h>
4373134Ssobomax#include <stdlib.h>
4473144Ssobomax#include <unistd.h>
4573134Ssobomax
4673134Ssobomax#include "pcap-int.h"
4773134Ssobomax
4873134Ssobomax#include "gnuc.h"
4973134Ssobomax#ifdef HAVE_OS_PROTO_H
5073134Ssobomax#include "os-proto.h"
5173134Ssobomax#endif
5273134Ssobomax
5373134Ssobomax#define TCPDUMP_MAGIC 0xa1b2c3d4
5473134Ssobomax
5573134Ssobomax/*
5673134Ssobomax * We use the "receiver-makes-right" approach to byte order,
5773134Ssobomax * because time is at a premium when we are writing the file.
5873134Ssobomax * In other words, the pcap_file_header and pcap_pkthdr,
5973134Ssobomax * records are written in host byte order.
6073134Ssobomax * Note that the packets are always written in network byte order.
6174258Ssobomax *
6273144Ssobomax * ntoh[ls] aren't sufficient because we might need to swap on a big-endian
6373134Ssobomax * machine (if the file was written in little-end order).
6473134Ssobomax */
6573134Ssobomax#define	SWAPLONG(y) \
6673134Ssobomax((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
6774258Ssobomax#define	SWAPSHORT(y) \
6873134Ssobomax	( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )
6973134Ssobomax
7073134Ssobomax#define SFERR_TRUNC		1
7173144Ssobomax#define SFERR_BADVERSION	2
7273144Ssobomax#define SFERR_BADF		3
7373144Ssobomax#define SFERR_EOF		4 /* not really an error, just a status */
7473144Ssobomax
7573144Ssobomaxstatic int
7673144Ssobomaxsf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
7773134Ssobomax{
7873134Ssobomax	struct pcap_file_header hdr;
7973144Ssobomax
8073144Ssobomax	hdr.magic = TCPDUMP_MAGIC;
8173134Ssobomax	hdr.version_major = PCAP_VERSION_MAJOR;
8273134Ssobomax	hdr.version_minor = PCAP_VERSION_MINOR;
8373134Ssobomax
8473134Ssobomax	hdr.thiszone = thiszone;
8573134Ssobomax	hdr.snaplen = snaplen;
8673134Ssobomax	hdr.sigfigs = 0;
8773134Ssobomax	hdr.linktype = linktype;
8873134Ssobomax
8973134Ssobomax	if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
9073134Ssobomax		return (-1);
9173134Ssobomax
9273134Ssobomax	return (0);
9373134Ssobomax}
9473134Ssobomax
9573134Ssobomaxstatic void
9673134Ssobomaxswap_hdr(struct pcap_file_header *hp)
9773134Ssobomax{
9873134Ssobomax	hp->version_major = SWAPSHORT(hp->version_major);
9974258Ssobomax	hp->version_minor = SWAPSHORT(hp->version_minor);
10074258Ssobomax	hp->thiszone = SWAPLONG(hp->thiszone);
10174258Ssobomax	hp->sigfigs = SWAPLONG(hp->sigfigs);
10274258Ssobomax	hp->snaplen = SWAPLONG(hp->snaplen);
10374258Ssobomax	hp->linktype = SWAPLONG(hp->linktype);
10474258Ssobomax}
10574258Ssobomax
10674258Ssobomaxpcap_t *
10774258Ssobomaxpcap_open_offline(char *fname, char *errbuf)
10874258Ssobomax{
10974258Ssobomax	register pcap_t *p;
11074258Ssobomax	register FILE *fp;
11173134Ssobomax	struct pcap_file_header hdr;
11273134Ssobomax	int linklen;
11373134Ssobomax
11473134Ssobomax	p = (pcap_t *)malloc(sizeof(*p));
11573134Ssobomax	if (p == NULL) {
11673134Ssobomax		strcpy(errbuf, "out of swap");
11773134Ssobomax		return (NULL);
11873144Ssobomax	}
11973144Ssobomax
12073144Ssobomax	memset((char *)p, 0, sizeof(*p));
12173144Ssobomax	/*
12273144Ssobomax	 * Set this field so we don't close stdin in pcap_close!
12373144Ssobomax	 */
12473144Ssobomax	p->fd = -1;
12573144Ssobomax
12673144Ssobomax	if (fname[0] == '-' && fname[1] == '\0')
12773144Ssobomax		fp = stdin;
12873144Ssobomax	else {
12973144Ssobomax		fp = fopen(fname, "r");
13073144Ssobomax		if (fp == NULL) {
13173144Ssobomax			sprintf(errbuf, "%s: %s", fname, pcap_strerror(errno));
13273144Ssobomax			goto bad;
13374258Ssobomax		}
13473144Ssobomax	}
13574258Ssobomax	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
13674258Ssobomax		sprintf(errbuf, "fread: %s", pcap_strerror(errno));
13773144Ssobomax		goto bad;
13873144Ssobomax	}
13973144Ssobomax	if (hdr.magic != TCPDUMP_MAGIC) {
14073134Ssobomax		if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) {
14173144Ssobomax			sprintf(errbuf, "bad dump file format");
14273144Ssobomax			goto bad;
14373134Ssobomax		}
14473144Ssobomax		p->sf.swapped = 1;
14573144Ssobomax		swap_hdr(&hdr);
14673144Ssobomax	}
14773144Ssobomax	if (hdr.version_major < PCAP_VERSION_MAJOR) {
14873144Ssobomax		sprintf(errbuf, "archaic file format");
14973144Ssobomax		goto bad;
15073144Ssobomax	}
15173134Ssobomax	p->tzoff = hdr.thiszone;
15273134Ssobomax	p->snapshot = hdr.snaplen;
15373134Ssobomax	p->linktype = hdr.linktype;
15473134Ssobomax	p->sf.rfile = fp;
15573134Ssobomax	p->bufsize = hdr.snaplen;
15673134Ssobomax
15774258Ssobomax	/* Align link header as required for proper data alignment */
15874258Ssobomax	/* XXX should handle all types */
15974258Ssobomax	switch (p->linktype) {
16074258Ssobomax
16174258Ssobomax	case DLT_EN10MB:
16274258Ssobomax		linklen = 14;
16373134Ssobomax		break;
16473134Ssobomax
16573134Ssobomax	case DLT_FDDI:
16673134Ssobomax		linklen = 13 + 8;	/* fddi_header + llc */
16773134Ssobomax		break;
16873134Ssobomax
16973134Ssobomax	case DLT_NULL:
17073134Ssobomax	default:
17173134Ssobomax		linklen = 0;
17273134Ssobomax		break;
17373134Ssobomax	}
17473134Ssobomax
17573134Ssobomax	p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT);
17673134Ssobomax	p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
17773134Ssobomax	p->sf.version_major = hdr.version_major;
17873134Ssobomax	p->sf.version_minor = hdr.version_minor;
17973134Ssobomax#ifdef PCAP_FDDIPAD
18073134Ssobomax	/* XXX padding only needed for kernel fcode */
18173134Ssobomax	pcap_fddipad = 0;
18273134Ssobomax#endif
18373134Ssobomax
18473134Ssobomax	return (p);
18573134Ssobomax bad:
18673134Ssobomax	free(p);
18773134Ssobomax	return (NULL);
18873134Ssobomax}
18973134Ssobomax
19073134Ssobomax/*
19173134Ssobomax * Read sf_readfile and return the next packet.  Return the header in hdr
19273134Ssobomax * and the contents in buf.  Return 0 on success, SFERR_EOF if there were
19373134Ssobomax * no more packets, and SFERR_TRUNC if a partial packet was encountered.
19473134Ssobomax */
19573134Ssobomaxstatic int
19673134Ssobomaxsf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen)
19773134Ssobomax{
19873134Ssobomax	FILE *fp = p->sf.rfile;
19973134Ssobomax
20073134Ssobomax	/* read the stamp */
20173144Ssobomax	if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) {
20273134Ssobomax		/* probably an EOF, though could be a truncated packet */
20373134Ssobomax		return (1);
20473134Ssobomax	}
20573134Ssobomax
20673134Ssobomax	if (p->sf.swapped) {
20773134Ssobomax		/* these were written in opposite byte order */
20873134Ssobomax		hdr->caplen = SWAPLONG(hdr->caplen);
20973134Ssobomax		hdr->len = SWAPLONG(hdr->len);
21073144Ssobomax		hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
21173144Ssobomax		hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
21273144Ssobomax	}
21373144Ssobomax	/*
21473134Ssobomax	 * We interchanged the caplen and len fields at version 2.3,
21573134Ssobomax	 * in order to match the bpf header layout.  But unfortunately
21673134Ssobomax	 * some files were written with version 2.3 in their headers
21773134Ssobomax	 * but without the interchanged fields.
21873134Ssobomax	 */
21973144Ssobomax	if (p->sf.version_minor < 3 ||
22073144Ssobomax	    (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) {
22173144Ssobomax		int t = hdr->caplen;
22273144Ssobomax		hdr->caplen = hdr->len;
22373134Ssobomax		hdr->len = t;
22473134Ssobomax	}
22573144Ssobomax
22673144Ssobomax	if (hdr->caplen > buflen) {
22773134Ssobomax		/*
22873134Ssobomax		 * This can happen due to Solaris 2.3 systems tripping
22973134Ssobomax		 * over the BUFMOD problem and not setting the snapshot
23073134Ssobomax		 * correctly in the savefile header.  If the caplen isn't
23173134Ssobomax		 * grossly wrong, try to salvage.
23273134Ssobomax		 */
23373134Ssobomax		static u_char *tp = NULL;
234		static int tsize = 0;
235
236		if (tsize < hdr->caplen) {
237			tsize = ((hdr->caplen + 1023) / 1024) * 1024;
238			if (tp != NULL)
239				free((u_char *)tp);
240			tp = (u_char *)malloc(tsize);
241			if (tp == NULL) {
242				sprintf(p->errbuf, "BUFMOD hack malloc");
243				return (-1);
244			}
245		}
246		if (fread((char *)tp, hdr->caplen, 1, fp) != 1) {
247			sprintf(p->errbuf, "truncated dump file");
248			return (-1);
249		}
250		memcpy((char *)buf, (char *)tp, buflen);
251
252	} else {
253		/* read the packet itself */
254
255		if (fread((char *)buf, hdr->caplen, 1, fp) != 1) {
256			sprintf(p->errbuf, "truncated dump file");
257			return (-1);
258		}
259	}
260	return (0);
261}
262
263/*
264 * Print out packets stored in the file initialized by sf_read_init().
265 * If cnt > 0, return after 'cnt' packets, otherwise continue until eof.
266 */
267int
268pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
269{
270	struct bpf_insn *fcode = p->fcode.bf_insns;
271	int status = 0;
272	int n = 0;
273
274	while (status == 0) {
275		struct pcap_pkthdr h;
276
277		status = sf_next_packet(p, &h, p->buffer, p->bufsize);
278		if (status) {
279			if (status == 1)
280				return (0);
281			return (status);
282		}
283
284		if (fcode == NULL ||
285		    bpf_filter(fcode, p->buffer, h.len, h.caplen)) {
286			(*callback)(user, &h, p->buffer);
287			if (++n >= cnt && cnt > 0)
288				break;
289		}
290	}
291	/*XXX this breaks semantics tcpslice expects */
292	return (n);
293}
294
295/*
296 * Output a packet to the initialized dump file.
297 */
298void
299pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
300{
301	register FILE *f;
302
303	f = (FILE *)user;
304	/* XXX we should check the return status */
305	(void)fwrite((char *)h, sizeof(*h), 1, f);
306	(void)fwrite((char *)sp, h->caplen, 1, f);
307}
308
309/*
310 * Initialize so that sf_write() will output to the file named 'fname'.
311 */
312pcap_dumper_t *
313pcap_dump_open(pcap_t *p, char *fname)
314{
315	FILE *f;
316	if (fname[0] == '-' && fname[1] == '\0')
317		f = stdout;
318	else {
319		f = fopen(fname, "w");
320		if (f == NULL) {
321			sprintf(p->errbuf, "%s: %s",
322			    fname, pcap_strerror(errno));
323			return (NULL);
324		}
325	}
326	(void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot);
327	return ((pcap_dumper_t *)f);
328}
329
330void
331pcap_dump_close(pcap_dumper_t *p)
332{
333
334#ifdef notyet
335	if (ferror((FILE *)p))
336		return-an-error;
337	/* XXX should check return from fclose() too */
338#endif
339	(void)fclose((FILE *)p);
340}
341