xref: /freebsd/contrib/libpcap/sf-pcap.c (revision 5b9c547c)
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
32 static 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 
123 static int pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **datap);
124 
125 /*
126  * Private data for reading pcap savefiles.
127  */
128 typedef enum {
129 	NOT_SWAPPED,
130 	SWAPPED,
131 	MAYBE_SWAPPED
132 } swapped_type_t;
133 
134 typedef enum {
135 	PASS_THROUGH,
136 	SCALE_UP,
137 	SCALE_DOWN
138 } tstamp_scale_type_t;
139 
140 struct 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  */
150 pcap_t *
151 pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
152     int *err)
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 the maximum as a fallback.
375 		 */
376 		p->bufsize = MAXIMUM_SNAPLEN;
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  */
396 static int
397 pcap_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 
432 	if (p->swapped) {
433 		/* these were written in opposite byte order */
434 		hdr->caplen = SWAPLONG(sf_hdr.caplen);
435 		hdr->len = SWAPLONG(sf_hdr.len);
436 		hdr->ts.tv_sec = SWAPLONG(sf_hdr.ts.tv_sec);
437 		hdr->ts.tv_usec = SWAPLONG(sf_hdr.ts.tv_usec);
438 	} else {
439 		hdr->caplen = sf_hdr.caplen;
440 		hdr->len = sf_hdr.len;
441 		hdr->ts.tv_sec = sf_hdr.ts.tv_sec;
442 		hdr->ts.tv_usec = sf_hdr.ts.tv_usec;
443 	}
444 
445 	switch (ps->scale_type) {
446 
447 	case PASS_THROUGH:
448 		/*
449 		 * Just pass the time stamp through.
450 		 */
451 		break;
452 
453 	case SCALE_UP:
454 		/*
455 		 * File has microseconds, user wants nanoseconds; convert
456 		 * it.
457 		 */
458 		hdr->ts.tv_usec = hdr->ts.tv_usec * 1000;
459 		break;
460 
461 	case SCALE_DOWN:
462 		/*
463 		 * File has nanoseconds, user wants microseconds; convert
464 		 * it.
465 		 */
466 		hdr->ts.tv_usec = hdr->ts.tv_usec / 1000;
467 		break;
468 	}
469 
470 	/* Swap the caplen and len fields, if necessary. */
471 	switch (ps->lengths_swapped) {
472 
473 	case NOT_SWAPPED:
474 		break;
475 
476 	case MAYBE_SWAPPED:
477 		if (hdr->caplen <= hdr->len) {
478 			/*
479 			 * The captured length is <= the actual length,
480 			 * so presumably they weren't swapped.
481 			 */
482 			break;
483 		}
484 		/* FALLTHROUGH */
485 
486 	case SWAPPED:
487 		t = hdr->caplen;
488 		hdr->caplen = hdr->len;
489 		hdr->len = t;
490 		break;
491 	}
492 
493 	if (hdr->caplen > p->bufsize) {
494 		/*
495 		 * This can happen due to Solaris 2.3 systems tripping
496 		 * over the BUFMOD problem and not setting the snapshot
497 		 * correctly in the savefile header.  If the caplen isn't
498 		 * grossly wrong, try to salvage.
499 		 */
500 		static u_char *tp = NULL;
501 		static size_t tsize = 0;
502 
503 		if (hdr->caplen > MAXIMUM_SNAPLEN) {
504 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
505 			    "bogus savefile header");
506 			return (-1);
507 		}
508 
509 		if (tsize < hdr->caplen) {
510 			tsize = ((hdr->caplen + 1023) / 1024) * 1024;
511 			if (tp != NULL)
512 				free((u_char *)tp);
513 			tp = (u_char *)malloc(tsize);
514 			if (tp == NULL) {
515 				tsize = 0;
516 				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
517 				    "BUFMOD hack malloc");
518 				return (-1);
519 			}
520 		}
521 		amt_read = fread((char *)tp, 1, hdr->caplen, fp);
522 		if (amt_read != hdr->caplen) {
523 			if (ferror(fp)) {
524 				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
525 				    "error reading dump file: %s",
526 				    pcap_strerror(errno));
527 			} else {
528 				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
529 				    "truncated dump file; tried to read %u captured bytes, only got %lu",
530 				    hdr->caplen, (unsigned long)amt_read);
531 			}
532 			return (-1);
533 		}
534 		/*
535 		 * We can only keep up to p->bufsize bytes.  Since
536 		 * caplen > p->bufsize is exactly how we got here,
537 		 * we know we can only keep the first p->bufsize bytes
538 		 * and must drop the remainder.  Adjust caplen accordingly,
539 		 * so we don't get confused later as to how many bytes we
540 		 * have to play with.
541 		 */
542 		hdr->caplen = p->bufsize;
543 		memcpy(p->buffer, (char *)tp, p->bufsize);
544 	} else {
545 		/* read the packet itself */
546 		amt_read = fread(p->buffer, 1, hdr->caplen, fp);
547 		if (amt_read != hdr->caplen) {
548 			if (ferror(fp)) {
549 				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
550 				    "error reading dump file: %s",
551 				    pcap_strerror(errno));
552 			} else {
553 				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
554 				    "truncated dump file; tried to read %u captured bytes, only got %lu",
555 				    hdr->caplen, (unsigned long)amt_read);
556 			}
557 			return (-1);
558 		}
559 	}
560 	*data = p->buffer;
561 
562 	if (p->swapped)
563 		swap_pseudo_headers(p->linktype, hdr, *data);
564 
565 	return (0);
566 }
567 
568 static int
569 sf_write_header(pcap_t *p, FILE *fp, int linktype, int thiszone, int snaplen)
570 {
571 	struct pcap_file_header hdr;
572 
573 	hdr.magic = p->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO ? NSEC_TCPDUMP_MAGIC : TCPDUMP_MAGIC;
574 	hdr.version_major = PCAP_VERSION_MAJOR;
575 	hdr.version_minor = PCAP_VERSION_MINOR;
576 
577 	hdr.thiszone = thiszone;
578 	hdr.snaplen = snaplen;
579 	hdr.sigfigs = 0;
580 	hdr.linktype = linktype;
581 
582 	if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
583 		return (-1);
584 
585 	return (0);
586 }
587 
588 /*
589  * Output a packet to the initialized dump file.
590  */
591 void
592 pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
593 {
594 	register FILE *f;
595 	struct pcap_sf_pkthdr sf_hdr;
596 
597 	f = (FILE *)user;
598 	sf_hdr.ts.tv_sec  = h->ts.tv_sec;
599 	sf_hdr.ts.tv_usec = h->ts.tv_usec;
600 	sf_hdr.caplen     = h->caplen;
601 	sf_hdr.len        = h->len;
602 	/* XXX we should check the return status */
603 	(void)fwrite(&sf_hdr, sizeof(sf_hdr), 1, f);
604 	(void)fwrite(sp, h->caplen, 1, f);
605 }
606 
607 static pcap_dumper_t *
608 pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname)
609 {
610 
611 #if defined(WIN32) || defined(MSDOS)
612 	/*
613 	 * If we're writing to the standard output, put it in binary
614 	 * mode, as savefiles are binary files.
615 	 *
616 	 * Otherwise, we turn off buffering.
617 	 * XXX - why?  And why not on the standard output?
618 	 */
619 	if (f == stdout)
620 		SET_BINMODE(f);
621 	else
622 		setbuf(f, NULL);
623 #endif
624 	if (sf_write_header(p, f, linktype, p->tzoff, p->snapshot) == -1) {
625 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s",
626 		    fname, pcap_strerror(errno));
627 		if (f != stdout)
628 			(void)fclose(f);
629 		return (NULL);
630 	}
631 	return ((pcap_dumper_t *)f);
632 }
633 
634 /*
635  * Initialize so that sf_write() will output to the file named 'fname'.
636  */
637 pcap_dumper_t *
638 pcap_dump_open(pcap_t *p, const char *fname)
639 {
640 	FILE *f;
641 	int linktype;
642 
643 	/*
644 	 * If this pcap_t hasn't been activated, it doesn't have a
645 	 * link-layer type, so we can't use it.
646 	 */
647 	if (!p->activated) {
648 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
649 		    "%s: not-yet-activated pcap_t passed to pcap_dump_open",
650 		    fname);
651 		return (NULL);
652 	}
653 	linktype = dlt_to_linktype(p->linktype);
654 	if (linktype == -1) {
655 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
656 		    "%s: link-layer type %d isn't supported in savefiles",
657 		    fname, p->linktype);
658 		return (NULL);
659 	}
660 	linktype |= p->linktype_ext;
661 
662 	if (fname[0] == '-' && fname[1] == '\0') {
663 		f = stdout;
664 		fname = "standard output";
665 	} else {
666 #if !defined(WIN32) && !defined(MSDOS)
667 		f = fopen(fname, "w");
668 #else
669 		f = fopen(fname, "wb");
670 #endif
671 		if (f == NULL) {
672 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
673 			    fname, pcap_strerror(errno));
674 			return (NULL);
675 		}
676 	}
677 	return (pcap_setup_dump(p, linktype, f, fname));
678 }
679 
680 /*
681  * Initialize so that sf_write() will output to the given stream.
682  */
683 pcap_dumper_t *
684 pcap_dump_fopen(pcap_t *p, FILE *f)
685 {
686 	int linktype;
687 
688 	linktype = dlt_to_linktype(p->linktype);
689 	if (linktype == -1) {
690 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
691 		    "stream: link-layer type %d isn't supported in savefiles",
692 		    p->linktype);
693 		return (NULL);
694 	}
695 	linktype |= p->linktype_ext;
696 
697 	return (pcap_setup_dump(p, linktype, f, "stream"));
698 }
699 
700 FILE *
701 pcap_dump_file(pcap_dumper_t *p)
702 {
703 	return ((FILE *)p);
704 }
705 
706 long
707 pcap_dump_ftell(pcap_dumper_t *p)
708 {
709 	return (ftell((FILE *)p));
710 }
711 
712 int
713 pcap_dump_flush(pcap_dumper_t *p)
714 {
715 
716 	if (fflush((FILE *)p) == EOF)
717 		return (-1);
718 	else
719 		return (0);
720 }
721 
722 void
723 pcap_dump_close(pcap_dumper_t *p)
724 {
725 
726 #ifdef notyet
727 	if (ferror((FILE *)p))
728 		return-an-error;
729 	/* XXX should check return from fclose() too */
730 #endif
731 	(void)fclose((FILE *)p);
732 }
733