1/*
2 * Copyright (c) 1993, 1994, 1995, 1996, 1997
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 * sf-pcap.c - libpcap-file-format-specific code from savefile.c
22 *	Extraction/creation by Jeffrey Mogul, DECWRL
23 *	Modified by Steve McCanne, LBL.
24 *
25 * Used to save the received packet headers, after filtering, to
26 * a file, and then read them later.
27 * The first record in the file contains saved values for the machine
28 * dependent values so we can print the dump file on any architecture.
29 */
30
31#ifndef lint
32static const char rcsid[] _U_ =
33    "@(#) $Header$ (LBL)";
34#endif
35
36#ifdef HAVE_CONFIG_H
37#include "config.h"
38#endif
39
40#ifdef WIN32
41#include <pcap-stdinc.h>
42#else /* WIN32 */
43#if HAVE_INTTYPES_H
44#include <inttypes.h>
45#elif HAVE_STDINT_H
46#include <stdint.h>
47#endif
48#ifdef HAVE_SYS_BITYPES_H
49#include <sys/bitypes.h>
50#endif
51#include <sys/types.h>
52#endif /* WIN32 */
53
54#include <errno.h>
55#include <memory.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59
60#include "pcap-int.h"
61
62#include "pcap-common.h"
63
64#ifdef HAVE_OS_PROTO_H
65#include "os-proto.h"
66#endif
67
68#include "sf-pcap.h"
69
70/*
71 * Setting O_BINARY on DOS/Windows is a bit tricky
72 */
73#if defined(WIN32)
74  #define SET_BINMODE(f)  _setmode(_fileno(f), _O_BINARY)
75#elif defined(MSDOS)
76  #if defined(__HIGHC__)
77  #define SET_BINMODE(f)  setmode(f, O_BINARY)
78  #else
79  #define SET_BINMODE(f)  setmode(fileno(f), O_BINARY)
80  #endif
81#endif
82
83/*
84 * Standard libpcap format.
85 */
86#define TCPDUMP_MAGIC		0xa1b2c3d4
87
88/*
89 * Alexey Kuznetzov's modified libpcap format.
90 */
91#define KUZNETZOV_TCPDUMP_MAGIC	0xa1b2cd34
92
93/*
94 * Reserved for Francisco Mesquita <francisco.mesquita@radiomovel.pt>
95 * for another modified format.
96 */
97#define FMESQUITA_TCPDUMP_MAGIC	0xa1b234cd
98
99/*
100 * Navtel Communcations' format, with nanosecond timestamps,
101 * as per a request from Dumas Hwang <dumas.hwang@navtelcom.com>.
102 */
103#define NAVTEL_TCPDUMP_MAGIC	0xa12b3c4d
104
105/*
106 * Normal libpcap format, except for seconds/nanoseconds timestamps,
107 * as per a request by Ulf Lamping <ulf.lamping@web.de>
108 */
109#define NSEC_TCPDUMP_MAGIC	0xa1b23c4d
110
111/*
112 * Mechanism for storing information about a capture in the upper
113 * 6 bits of a linktype value in a capture file.
114 *
115 * LT_LINKTYPE_EXT(x) extracts the additional information.
116 *
117 * The rest of the bits are for a value describing the link-layer
118 * value.  LT_LINKTYPE(x) extracts that value.
119 */
120#define LT_LINKTYPE(x)		((x) & 0x03FFFFFF)
121#define LT_LINKTYPE_EXT(x)	((x) & 0xFC000000)
122
123static int pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **datap);
124
125/*
126 * Private data for reading pcap savefiles.
127 */
128typedef enum {
129	NOT_SWAPPED,
130	SWAPPED,
131	MAYBE_SWAPPED
132} swapped_type_t;
133
134typedef enum {
135	PASS_THROUGH,
136	SCALE_UP,
137	SCALE_DOWN
138} tstamp_scale_type_t;
139
140struct pcap_sf {
141	size_t hdrsize;
142	swapped_type_t lengths_swapped;
143	tstamp_scale_type_t scale_type;
144};
145
146/*
147 * Check whether this is a pcap savefile and, if it is, extract the
148 * relevant information from the header.
149 */
150pcap_t *
151pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
152    int *err, int isng)
153{
154	struct pcap_file_header hdr;
155	size_t amt_read;
156	pcap_t *p;
157	int swapped = 0;
158	struct pcap_sf *ps;
159
160	/*
161	 * Assume no read errors.
162	 */
163	*err = 0;
164
165	/*
166	 * Check whether the first 4 bytes of the file are the magic
167	 * number for a pcap savefile, or for a byte-swapped pcap
168	 * savefile.
169	 */
170	if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC &&
171	    magic != NSEC_TCPDUMP_MAGIC) {
172		magic = SWAPLONG(magic);
173		if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC &&
174		    magic != NSEC_TCPDUMP_MAGIC)
175			return (NULL);	/* nope */
176		swapped = 1;
177	}
178
179	/*
180	 * They are.  Put the magic number in the header, and read
181	 * the rest of the header.
182	 */
183	hdr.magic = magic;
184	amt_read = fread(((char *)&hdr) + sizeof hdr.magic, 1,
185	    sizeof(hdr) - sizeof(hdr.magic), fp);
186	if (amt_read != sizeof(hdr) - sizeof(hdr.magic)) {
187		if (ferror(fp)) {
188			snprintf(errbuf, PCAP_ERRBUF_SIZE,
189			    "error reading dump file: %s",
190			    pcap_strerror(errno));
191		} else {
192			snprintf(errbuf, PCAP_ERRBUF_SIZE,
193			    "truncated dump file; tried to read %lu file header bytes, only got %lu",
194			    (unsigned long)sizeof(hdr),
195			    (unsigned long)amt_read);
196		}
197		*err = 1;
198		return (NULL);
199	}
200
201	/*
202	 * If it's a byte-swapped capture file, byte-swap the header.
203	 */
204	if (swapped) {
205		hdr.version_major = SWAPSHORT(hdr.version_major);
206		hdr.version_minor = SWAPSHORT(hdr.version_minor);
207		hdr.thiszone = SWAPLONG(hdr.thiszone);
208		hdr.sigfigs = SWAPLONG(hdr.sigfigs);
209		hdr.snaplen = SWAPLONG(hdr.snaplen);
210		hdr.linktype = SWAPLONG(hdr.linktype);
211	}
212
213	if (hdr.version_major < PCAP_VERSION_MAJOR) {
214		snprintf(errbuf, PCAP_ERRBUF_SIZE,
215		    "archaic pcap savefile format");
216		*err = 1;
217		return (NULL);
218	}
219
220	/*
221	 * OK, this is a good pcap file.
222	 * Allocate a pcap_t for it.
223	 */
224	p = pcap_open_offline_common(errbuf, sizeof (struct pcap_sf));
225	if (p == NULL) {
226		/* Allocation failed. */
227		*err = 1;
228		return (NULL);
229	}
230	p->swapped = swapped;
231	p->version_major = hdr.version_major;
232	p->version_minor = hdr.version_minor;
233	p->tzoff = hdr.thiszone;
234	p->snapshot = hdr.snaplen;
235	p->linktype = linktype_to_dlt(LT_LINKTYPE(hdr.linktype));
236	p->linktype_ext = LT_LINKTYPE_EXT(hdr.linktype);
237
238	p->next_packet_op = pcap_next_packet;
239
240	ps = p->priv;
241
242	p->opt.tstamp_precision = precision;
243
244	/*
245	 * Will we need to scale the timestamps to match what the
246	 * user wants?
247	 */
248	switch (precision) {
249
250	case PCAP_TSTAMP_PRECISION_MICRO:
251		if (magic == NSEC_TCPDUMP_MAGIC) {
252			/*
253			 * The file has nanoseconds, the user
254			 * wants microseconds; scale the
255			 * precision down.
256			 */
257			ps->scale_type = SCALE_DOWN;
258		} else {
259			/*
260			 * The file has microseconds, the
261			 * user wants microseconds; nothing to do.
262			 */
263			ps->scale_type = PASS_THROUGH;
264		}
265		break;
266
267	case PCAP_TSTAMP_PRECISION_NANO:
268		if (magic == NSEC_TCPDUMP_MAGIC) {
269			/*
270			 * The file has nanoseconds, the
271			 * user wants nanoseconds; nothing to do.
272			 */
273			ps->scale_type = PASS_THROUGH;
274		} else {
275			/*
276			 * The file has microoseconds, the user
277			 * wants nanoseconds; scale the
278			 * precision up.
279			 */
280			ps->scale_type = SCALE_UP;
281		}
282		break;
283
284	default:
285		snprintf(errbuf, PCAP_ERRBUF_SIZE,
286		    "unknown time stamp resolution %u", precision);
287		free(p);
288		*err = 1;
289		return (NULL);
290	}
291
292	/*
293	 * We interchanged the caplen and len fields at version 2.3,
294	 * in order to match the bpf header layout.  But unfortunately
295	 * some files were written with version 2.3 in their headers
296	 * but without the interchanged fields.
297	 *
298	 * In addition, DG/UX tcpdump writes out files with a version
299	 * number of 543.0, and with the caplen and len fields in the
300	 * pre-2.3 order.
301	 */
302	switch (hdr.version_major) {
303
304	case 2:
305		if (hdr.version_minor < 3)
306			ps->lengths_swapped = SWAPPED;
307		else if (hdr.version_minor == 3)
308			ps->lengths_swapped = MAYBE_SWAPPED;
309		else
310			ps->lengths_swapped = NOT_SWAPPED;
311		break;
312
313	case 543:
314		ps->lengths_swapped = SWAPPED;
315		break;
316
317	default:
318		ps->lengths_swapped = NOT_SWAPPED;
319		break;
320	}
321
322	if (magic == KUZNETZOV_TCPDUMP_MAGIC) {
323		/*
324		 * XXX - the patch that's in some versions of libpcap
325		 * changes the packet header but not the magic number,
326		 * and some other versions with this magic number have
327		 * some extra debugging information in the packet header;
328		 * we'd have to use some hacks^H^H^H^H^Hheuristics to
329		 * detect those variants.
330		 *
331		 * Ethereal does that, but it does so by trying to read
332		 * the first two packets of the file with each of the
333		 * record header formats.  That currently means it seeks
334		 * backwards and retries the reads, which doesn't work
335		 * on pipes.  We want to be able to read from a pipe, so
336		 * that strategy won't work; we'd have to buffer some
337		 * data ourselves and read from that buffer in order to
338		 * make that work.
339		 */
340		ps->hdrsize = sizeof(struct pcap_sf_patched_pkthdr);
341
342		if (p->linktype == DLT_EN10MB) {
343			/*
344			 * This capture might have been done in raw mode
345			 * or cooked mode.
346			 *
347			 * If it was done in cooked mode, p->snapshot was
348			 * passed to recvfrom() as the buffer size, meaning
349			 * that the most packet data that would be copied
350			 * would be p->snapshot.  However, a faked Ethernet
351			 * header would then have been added to it, so the
352			 * most data that would be in a packet in the file
353			 * would be p->snapshot + 14.
354			 *
355			 * We can't easily tell whether the capture was done
356			 * in raw mode or cooked mode, so we'll assume it was
357			 * cooked mode, and add 14 to the snapshot length.
358			 * That means that, for a raw capture, the snapshot
359			 * length will be misleading if you use it to figure
360			 * out why a capture doesn't have all the packet data,
361			 * but there's not much we can do to avoid that.
362			 */
363			p->snapshot += 14;
364		}
365	} else
366		ps->hdrsize = sizeof(struct pcap_sf_pkthdr);
367
368	/*
369	 * Allocate a buffer for the packet data.
370	 */
371	p->bufsize = p->snapshot;
372	if (p->bufsize <= 0) {
373		/*
374		 * Bogus snapshot length; use 64KiB as a fallback.
375		 */
376		p->bufsize = 65536;
377	}
378	p->buffer = malloc(p->bufsize);
379	if (p->buffer == NULL) {
380		snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
381		free(p);
382		*err = 1;
383		return (NULL);
384	}
385
386	p->cleanup_op = sf_cleanup;
387
388	return (p);
389}
390
391/*
392 * Read and return the next packet from the savefile.  Return the header
393 * in hdr and a pointer to the contents in data.  Return 0 on success, 1
394 * if there were no more packets, and -1 on an error.
395 */
396static int
397pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
398{
399	struct pcap_sf *ps = p->priv;
400	struct pcap_sf_patched_pkthdr sf_hdr;
401	FILE *fp = p->rfile;
402	size_t amt_read;
403	bpf_u_int32 t;
404
405	/*
406	 * Read the packet header; the structure we use as a buffer
407	 * is the longer structure for files generated by the patched
408	 * libpcap, but if the file has the magic number for an
409	 * unpatched libpcap we only read as many bytes as the regular
410	 * header has.
411	 */
412	amt_read = fread(&sf_hdr, 1, ps->hdrsize, fp);
413	if (amt_read != ps->hdrsize) {
414		if (ferror(fp)) {
415			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
416			    "error reading dump file: %s",
417			    pcap_strerror(errno));
418			return (-1);
419		} else {
420			if (amt_read != 0) {
421				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
422				    "truncated dump file; tried to read %lu header bytes, only got %lu",
423				    (unsigned long)ps->hdrsize,
424				    (unsigned long)amt_read);
425				return (-1);
426			}
427			/* EOF */
428			return (1);
429		}
430	}
431#ifdef __APPLE__
432	memset(hdr->comment, 0, sizeof(hdr->comment));
433#endif
434
435	if (p->swapped) {
436		/* these were written in opposite byte order */
437		hdr->caplen = SWAPLONG(sf_hdr.caplen);
438		hdr->len = SWAPLONG(sf_hdr.len);
439		hdr->ts.tv_sec = SWAPLONG(sf_hdr.ts.tv_sec);
440		hdr->ts.tv_usec = SWAPLONG(sf_hdr.ts.tv_usec);
441	} else {
442		hdr->caplen = sf_hdr.caplen;
443		hdr->len = sf_hdr.len;
444		hdr->ts.tv_sec = sf_hdr.ts.tv_sec;
445		hdr->ts.tv_usec = sf_hdr.ts.tv_usec;
446	}
447
448	switch (ps->scale_type) {
449
450	case PASS_THROUGH:
451		/*
452		 * Just pass the time stamp through.
453		 */
454		break;
455
456	case SCALE_UP:
457		/*
458		 * File has microseconds, user wants nanoseconds; convert
459		 * it.
460		 */
461		hdr->ts.tv_usec = hdr->ts.tv_usec * 1000;
462		break;
463
464	case SCALE_DOWN:
465		/*
466		 * File has nanoseconds, user wants microseconds; convert
467		 * it.
468		 */
469		hdr->ts.tv_usec = hdr->ts.tv_usec / 1000;
470		break;
471	}
472
473	/* Swap the caplen and len fields, if necessary. */
474	switch (ps->lengths_swapped) {
475
476	case NOT_SWAPPED:
477		break;
478
479	case MAYBE_SWAPPED:
480		if (hdr->caplen <= hdr->len) {
481			/*
482			 * The captured length is <= the actual length,
483			 * so presumably they weren't swapped.
484			 */
485			break;
486		}
487		/* FALLTHROUGH */
488
489	case SWAPPED:
490		t = hdr->caplen;
491		hdr->caplen = hdr->len;
492		hdr->len = t;
493		break;
494	}
495
496	if (hdr->caplen > p->bufsize) {
497		/*
498		 * This can happen due to Solaris 2.3 systems tripping
499		 * over the BUFMOD problem and not setting the snapshot
500		 * correctly in the savefile header.  If the caplen isn't
501		 * grossly wrong, try to salvage.
502		 */
503		static u_char *tp = NULL;
504		static size_t tsize = 0;
505
506		if (hdr->caplen > 65535) {
507			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
508			    "bogus savefile header");
509			return (-1);
510		}
511
512		if (tsize < hdr->caplen) {
513			tsize = ((hdr->caplen + 1023) / 1024) * 1024;
514			if (tp != NULL)
515				free((u_char *)tp);
516			tp = (u_char *)malloc(tsize);
517			if (tp == NULL) {
518				tsize = 0;
519				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
520				    "BUFMOD hack malloc");
521				return (-1);
522			}
523		}
524		amt_read = fread((char *)tp, 1, hdr->caplen, fp);
525		if (amt_read != hdr->caplen) {
526			if (ferror(fp)) {
527				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
528				    "error reading dump file: %s",
529				    pcap_strerror(errno));
530			} else {
531				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
532				    "truncated dump file; tried to read %u captured bytes, only got %lu",
533				    hdr->caplen, (unsigned long)amt_read);
534			}
535			return (-1);
536		}
537		/*
538		 * We can only keep up to p->bufsize bytes.  Since
539		 * caplen > p->bufsize is exactly how we got here,
540		 * we know we can only keep the first p->bufsize bytes
541		 * and must drop the remainder.  Adjust caplen accordingly,
542		 * so we don't get confused later as to how many bytes we
543		 * have to play with.
544		 */
545		hdr->caplen = p->bufsize;
546		memcpy(p->buffer, (char *)tp, p->bufsize);
547	} else {
548		/* read the packet itself */
549		amt_read = fread(p->buffer, 1, hdr->caplen, fp);
550		if (amt_read != hdr->caplen) {
551			if (ferror(fp)) {
552				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
553				    "error reading dump file: %s",
554				    pcap_strerror(errno));
555			} else {
556				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
557				    "truncated dump file; tried to read %u captured bytes, only got %lu",
558				    hdr->caplen, (unsigned long)amt_read);
559			}
560			return (-1);
561		}
562	}
563	*data = p->buffer;
564
565	if (p->swapped) {
566		/*
567		 * Convert pseudo-headers from the byte order of
568		 * the host on which the file was saved to our
569		 * byte order, as necessary.
570		 */
571		switch (p->linktype) {
572
573		case DLT_USB_LINUX:
574			swap_linux_usb_header(hdr, *data, 0);
575			break;
576
577		case DLT_USB_LINUX_MMAPPED:
578			swap_linux_usb_header(hdr, *data, 1);
579			break;
580		}
581	}
582
583	return (0);
584}
585
586static int
587sf_write_header(pcap_t *p, FILE *fp, int linktype, int thiszone, int snaplen)
588{
589	struct pcap_file_header hdr;
590
591	hdr.magic = p->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO ? NSEC_TCPDUMP_MAGIC : TCPDUMP_MAGIC;
592	hdr.version_major = PCAP_VERSION_MAJOR;
593	hdr.version_minor = PCAP_VERSION_MINOR;
594
595	hdr.thiszone = thiszone;
596	hdr.snaplen = snaplen;
597	hdr.sigfigs = 0;
598	hdr.linktype = linktype;
599
600	if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
601		return (-1);
602
603	return (0);
604}
605
606/*
607 * Output a packet to the initialized dump file.
608 */
609void
610pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
611{
612	register FILE *f;
613	struct pcap_sf_pkthdr sf_hdr;
614
615	f = (FILE *)user;
616	sf_hdr.ts.tv_sec  = h->ts.tv_sec;
617	sf_hdr.ts.tv_usec = h->ts.tv_usec;
618	sf_hdr.caplen     = h->caplen;
619	sf_hdr.len        = h->len;
620	/* XXX we should check the return status */
621	(void)fwrite(&sf_hdr, sizeof(sf_hdr), 1, f);
622	(void)fwrite(sp, h->caplen, 1, f);
623}
624
625static pcap_dumper_t *
626pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname)
627{
628
629#if defined(WIN32) || defined(MSDOS)
630	/*
631	 * If we're writing to the standard output, put it in binary
632	 * mode, as savefiles are binary files.
633	 *
634	 * Otherwise, we turn off buffering.
635	 * XXX - why?  And why not on the standard output?
636	 */
637	if (f == stdout)
638		SET_BINMODE(f);
639	else
640		setbuf(f, NULL);
641#endif
642	if (sf_write_header(p, f, linktype, p->tzoff, p->snapshot) == -1) {
643		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s",
644		    fname, pcap_strerror(errno));
645		if (f != stdout)
646			(void)fclose(f);
647		return (NULL);
648	}
649	return ((pcap_dumper_t *)f);
650}
651
652/*
653 * Initialize so that sf_write() will output to the file named 'fname'.
654 */
655pcap_dumper_t *
656pcap_dump_open(pcap_t *p, const char *fname)
657{
658	FILE *f;
659	int linktype;
660
661	/*
662	 * If this pcap_t hasn't been activated, it doesn't have a
663	 * link-layer type, so we can't use it.
664	 */
665	if (!p->activated) {
666		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
667		    "%s: not-yet-activated pcap_t passed to pcap_dump_open",
668		    fname);
669		return (NULL);
670	}
671	linktype = dlt_to_linktype(p->linktype);
672	if (linktype == -1) {
673		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
674		    "%s: link-layer type %d isn't supported in savefiles",
675		    fname, p->linktype);
676		return (NULL);
677	}
678	linktype |= p->linktype_ext;
679
680	if (fname[0] == '-' && fname[1] == '\0') {
681		f = stdout;
682		fname = "standard output";
683	} else {
684#if !defined(WIN32) && !defined(MSDOS)
685		f = fopen(fname, "w");
686#else
687		f = fopen(fname, "wb");
688#endif
689		if (f == NULL) {
690			snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
691			    fname, pcap_strerror(errno));
692			return (NULL);
693		}
694	}
695	return (pcap_setup_dump(p, linktype, f, fname));
696}
697
698/*
699 * Initialize so that sf_write() will output to the given stream.
700 */
701pcap_dumper_t *
702pcap_dump_fopen(pcap_t *p, FILE *f)
703{
704	int linktype;
705
706	linktype = dlt_to_linktype(p->linktype);
707	if (linktype == -1) {
708		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
709		    "stream: link-layer type %d isn't supported in savefiles",
710		    p->linktype);
711		return (NULL);
712	}
713	linktype |= p->linktype_ext;
714
715	return (pcap_setup_dump(p, linktype, f, "stream"));
716}
717
718FILE *
719pcap_dump_file(pcap_dumper_t *p)
720{
721	return ((FILE *)p);
722}
723
724long
725pcap_dump_ftell(pcap_dumper_t *p)
726{
727	return (ftell((FILE *)p));
728}
729
730int
731pcap_dump_flush(pcap_dumper_t *p)
732{
733
734	if (fflush((FILE *)p) == EOF)
735		return (-1);
736	else
737		return (0);
738}
739
740void
741pcap_dump_close(pcap_dumper_t *p)
742{
743
744#ifdef notyet
745	if (ferror((FILE *)p))
746		return-an-error;
747	/* XXX should check return from fclose() too */
748#endif
749	(void)fclose((FILE *)p);
750}
751