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