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