/* * Copyright (c) 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * sf-pcap.c - libpcap-file-format-specific code from savefile.c * Extraction/creation by Jeffrey Mogul, DECWRL * Modified by Steve McCanne, LBL. * * Used to save the received packet headers, after filtering, to * a file, and then read them later. * The first record in the file contains saved values for the machine * dependent values so we can print the dump file on any architecture. */ #ifndef lint static const char rcsid[] _U_ = "@(#) $Header$ (LBL)"; #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include #else /* WIN32 */ #if HAVE_INTTYPES_H #include #elif HAVE_STDINT_H #include #endif #ifdef HAVE_SYS_BITYPES_H #include #endif #include #endif /* WIN32 */ #include #include #include #include #include #include "pcap-int.h" #include "pcap-common.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif #include "sf-pcap.h" /* * Setting O_BINARY on DOS/Windows is a bit tricky */ #if defined(WIN32) #define SET_BINMODE(f) _setmode(_fileno(f), _O_BINARY) #elif defined(MSDOS) #if defined(__HIGHC__) #define SET_BINMODE(f) setmode(f, O_BINARY) #else #define SET_BINMODE(f) setmode(fileno(f), O_BINARY) #endif #endif /* * Standard libpcap format. */ #define TCPDUMP_MAGIC 0xa1b2c3d4 /* * Alexey Kuznetzov's modified libpcap format. */ #define KUZNETZOV_TCPDUMP_MAGIC 0xa1b2cd34 /* * Reserved for Francisco Mesquita * for another modified format. */ #define FMESQUITA_TCPDUMP_MAGIC 0xa1b234cd /* * Navtel Communcations' format, with nanosecond timestamps, * as per a request from Dumas Hwang . */ #define NAVTEL_TCPDUMP_MAGIC 0xa12b3c4d /* * Normal libpcap format, except for seconds/nanoseconds timestamps, * as per a request by Ulf Lamping */ #define NSEC_TCPDUMP_MAGIC 0xa1b23c4d /* * Mechanism for storing information about a capture in the upper * 6 bits of a linktype value in a capture file. * * LT_LINKTYPE_EXT(x) extracts the additional information. * * The rest of the bits are for a value describing the link-layer * value. LT_LINKTYPE(x) extracts that value. */ #define LT_LINKTYPE(x) ((x) & 0x03FFFFFF) #define LT_LINKTYPE_EXT(x) ((x) & 0xFC000000) static int pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **datap); /* * Check whether this is a pcap savefile and, if it is, extract the * relevant information from the header. */ int pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) { struct pcap_file_header hdr; size_t amt_read; /* * Check whether the first 4 bytes of the file are the magic * number for a pcap savefile, or for a byte-swapped pcap * savefile. */ if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC) { magic = SWAPLONG(magic); if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC) return (0); /* nope */ p->sf.swapped = 1; } /* * They are. Put the magic number in the header, and read * the rest of the header. */ hdr.magic = magic; amt_read = fread(((char *)&hdr) + sizeof hdr.magic, 1, sizeof(hdr) - sizeof(hdr.magic), fp); if (amt_read != sizeof(hdr) - sizeof(hdr.magic)) { if (ferror(fp)) { snprintf(errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); } else { snprintf(errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %lu file header bytes, only got %lu", (unsigned long)sizeof(hdr), (unsigned long)amt_read); } return (-1); } /* * If it's a byte-swapped capture file, byte-swap the header. */ if (p->sf.swapped) { hdr.version_major = SWAPSHORT(hdr.version_major); hdr.version_minor = SWAPSHORT(hdr.version_minor); hdr.thiszone = SWAPLONG(hdr.thiszone); hdr.sigfigs = SWAPLONG(hdr.sigfigs); hdr.snaplen = SWAPLONG(hdr.snaplen); hdr.linktype = SWAPLONG(hdr.linktype); } if (hdr.version_major < PCAP_VERSION_MAJOR) { snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic pcap savefile format"); return (-1); } p->sf.version_major = hdr.version_major; p->sf.version_minor = hdr.version_minor; p->tzoff = hdr.thiszone; p->snapshot = hdr.snaplen; p->linktype = linktype_to_dlt(LT_LINKTYPE(hdr.linktype)); p->linktype_ext = LT_LINKTYPE_EXT(hdr.linktype); p->sf.next_packet_op = pcap_next_packet; /* * We interchanged the caplen and len fields at version 2.3, * in order to match the bpf header layout. But unfortunately * some files were written with version 2.3 in their headers * but without the interchanged fields. * * In addition, DG/UX tcpdump writes out files with a version * number of 543.0, and with the caplen and len fields in the * pre-2.3 order. */ switch (hdr.version_major) { case 2: if (hdr.version_minor < 3) p->sf.lengths_swapped = SWAPPED; else if (hdr.version_minor == 3) p->sf.lengths_swapped = MAYBE_SWAPPED; else p->sf.lengths_swapped = NOT_SWAPPED; break; case 543: p->sf.lengths_swapped = SWAPPED; break; default: p->sf.lengths_swapped = NOT_SWAPPED; break; } if (magic == KUZNETZOV_TCPDUMP_MAGIC) { /* * XXX - the patch that's in some versions of libpcap * changes the packet header but not the magic number, * and some other versions with this magic number have * some extra debugging information in the packet header; * we'd have to use some hacks^H^H^H^H^Hheuristics to * detect those variants. * * Ethereal does that, but it does so by trying to read * the first two packets of the file with each of the * record header formats. That currently means it seeks * backwards and retries the reads, which doesn't work * on pipes. We want to be able to read from a pipe, so * that strategy won't work; we'd have to buffer some * data ourselves and read from that buffer in order to * make that work. */ p->sf.hdrsize = sizeof(struct pcap_sf_patched_pkthdr); if (p->linktype == DLT_EN10MB) { /* * This capture might have been done in raw mode * or cooked mode. * * If it was done in cooked mode, p->snapshot was * passed to recvfrom() as the buffer size, meaning * that the most packet data that would be copied * would be p->snapshot. However, a faked Ethernet * header would then have been added to it, so the * most data that would be in a packet in the file * would be p->snapshot + 14. * * We can't easily tell whether the capture was done * in raw mode or cooked mode, so we'll assume it was * cooked mode, and add 14 to the snapshot length. * That means that, for a raw capture, the snapshot * length will be misleading if you use it to figure * out why a capture doesn't have all the packet data, * but there's not much we can do to avoid that. */ p->snapshot += 14; } } else p->sf.hdrsize = sizeof(struct pcap_sf_pkthdr); /* * Allocate a buffer for the packet data. */ p->bufsize = p->snapshot; if (p->bufsize <= 0) { /* * Bogus snapshot length; use 64KiB as a fallback. */ p->bufsize = 65536; } p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); return (-1); } return (1); } /* * Read and return the next packet from the savefile. Return the header * in hdr and a pointer to the contents in data. Return 0 on success, 1 * if there were no more packets, and -1 on an error. */ static int pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) { struct pcap_sf_patched_pkthdr sf_hdr; FILE *fp = p->sf.rfile; size_t amt_read; bpf_u_int32 t; /* * Read the packet header; the structure we use as a buffer * is the longer structure for files generated by the patched * libpcap, but if the file has the magic number for an * unpatched libpcap we only read as many bytes as the regular * header has. */ amt_read = fread(&sf_hdr, 1, p->sf.hdrsize, fp); if (amt_read != p->sf.hdrsize) { if (ferror(fp)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); return (-1); } else { if (amt_read != 0) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %lu header bytes, only got %lu", (unsigned long)p->sf.hdrsize, (unsigned long)amt_read); return (-1); } /* EOF */ return (1); } } if (p->sf.swapped) { /* these were written in opposite byte order */ hdr->caplen = SWAPLONG(sf_hdr.caplen); hdr->len = SWAPLONG(sf_hdr.len); hdr->ts.tv_sec = SWAPLONG(sf_hdr.ts.tv_sec); hdr->ts.tv_usec = SWAPLONG(sf_hdr.ts.tv_usec); } else { hdr->caplen = sf_hdr.caplen; hdr->len = sf_hdr.len; hdr->ts.tv_sec = sf_hdr.ts.tv_sec; hdr->ts.tv_usec = sf_hdr.ts.tv_usec; } /* Swap the caplen and len fields, if necessary. */ switch (p->sf.lengths_swapped) { case NOT_SWAPPED: break; case MAYBE_SWAPPED: if (hdr->caplen <= hdr->len) { /* * The captured length is <= the actual length, * so presumably they weren't swapped. */ break; } /* FALLTHROUGH */ case SWAPPED: t = hdr->caplen; hdr->caplen = hdr->len; hdr->len = t; break; } if (hdr->caplen > p->bufsize) { /* * This can happen due to Solaris 2.3 systems tripping * over the BUFMOD problem and not setting the snapshot * correctly in the savefile header. If the caplen isn't * grossly wrong, try to salvage. */ static u_char *tp = NULL; static size_t tsize = 0; if (hdr->caplen > 65535) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "bogus savefile header"); return (-1); } if (tsize < hdr->caplen) { tsize = ((hdr->caplen + 1023) / 1024) * 1024; if (tp != NULL) free((u_char *)tp); tp = (u_char *)malloc(tsize); if (tp == NULL) { tsize = 0; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BUFMOD hack malloc"); return (-1); } } amt_read = fread((char *)tp, 1, hdr->caplen, fp); if (amt_read != hdr->caplen) { if (ferror(fp)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); } else { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %u captured bytes, only got %lu", hdr->caplen, (unsigned long)amt_read); } return (-1); } /* * We can only keep up to p->bufsize bytes. Since * caplen > p->bufsize is exactly how we got here, * we know we can only keep the first p->bufsize bytes * and must drop the remainder. Adjust caplen accordingly, * so we don't get confused later as to how many bytes we * have to play with. */ hdr->caplen = p->bufsize; memcpy(p->buffer, (char *)tp, p->bufsize); } else { /* read the packet itself */ amt_read = fread(p->buffer, 1, hdr->caplen, fp); if (amt_read != hdr->caplen) { if (ferror(fp)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); } else { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %u captured bytes, only got %lu", hdr->caplen, (unsigned long)amt_read); } return (-1); } } *data = p->buffer; if (p->sf.swapped) { /* * Convert pseudo-headers from the byte order of * the host on which the file was saved to our * byte order, as necessary. */ switch (p->linktype) { case DLT_USB_LINUX: swap_linux_usb_header(hdr, *data, 0); break; case DLT_USB_LINUX_MMAPPED: swap_linux_usb_header(hdr, *data, 1); break; } } return (0); } static int sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen) { struct pcap_file_header hdr; hdr.magic = TCPDUMP_MAGIC; hdr.version_major = PCAP_VERSION_MAJOR; hdr.version_minor = PCAP_VERSION_MINOR; hdr.thiszone = thiszone; hdr.snaplen = snaplen; hdr.sigfigs = 0; hdr.linktype = linktype; if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) return (-1); return (0); } /* * Output a packet to the initialized dump file. */ void pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { register FILE *f; struct pcap_sf_pkthdr sf_hdr; f = (FILE *)user; sf_hdr.ts.tv_sec = h->ts.tv_sec; sf_hdr.ts.tv_usec = h->ts.tv_usec; sf_hdr.caplen = h->caplen; sf_hdr.len = h->len; /* XXX we should check the return status */ (void)fwrite(&sf_hdr, sizeof(sf_hdr), 1, f); (void)fwrite(sp, h->caplen, 1, f); } static pcap_dumper_t * pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname) { #if defined(WIN32) || defined(MSDOS) /* * If we're writing to the standard output, put it in binary * mode, as savefiles are binary files. * * Otherwise, we turn off buffering. * XXX - why? And why not on the standard output? */ if (f == stdout) SET_BINMODE(f); else setbuf(f, NULL); #endif if (sf_write_header(f, linktype, p->tzoff, p->snapshot) == -1) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s", fname, pcap_strerror(errno)); if (f != stdout) (void)fclose(f); return (NULL); } return ((pcap_dumper_t *)f); } /* * Initialize so that sf_write() will output to the file named 'fname'. */ pcap_dumper_t * pcap_dump_open(pcap_t *p, const char *fname) { FILE *f; int linktype; /* * If this pcap_t hasn't been activated, it doesn't have a * link-layer type, so we can't use it. */ if (!p->activated) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: not-yet-activated pcap_t passed to pcap_dump_open", fname); return (NULL); } linktype = dlt_to_linktype(p->linktype); if (linktype == -1) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: link-layer type %d isn't supported in savefiles", fname, p->linktype); return (NULL); } linktype |= p->linktype_ext; if (fname[0] == '-' && fname[1] == '\0') { f = stdout; fname = "standard output"; } else { #if !defined(WIN32) && !defined(MSDOS) f = fopen(fname, "w"); #else f = fopen(fname, "wb"); #endif if (f == NULL) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname, pcap_strerror(errno)); return (NULL); } } return (pcap_setup_dump(p, linktype, f, fname)); } /* * Initialize so that sf_write() will output to the given stream. */ pcap_dumper_t * pcap_dump_fopen(pcap_t *p, FILE *f) { int linktype; linktype = dlt_to_linktype(p->linktype); if (linktype == -1) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "stream: link-layer type %d isn't supported in savefiles", p->linktype); return (NULL); } linktype |= p->linktype_ext; return (pcap_setup_dump(p, linktype, f, "stream")); } FILE * pcap_dump_file(pcap_dumper_t *p) { return ((FILE *)p); } long pcap_dump_ftell(pcap_dumper_t *p) { return (ftell((FILE *)p)); } int pcap_dump_flush(pcap_dumper_t *p) { if (fflush((FILE *)p) == EOF) return (-1); else return (0); } void pcap_dump_close(pcap_dumper_t *p) { #ifdef notyet if (ferror((FILE *)p)) return-an-error; /* XXX should check return from fclose() too */ #endif (void)fclose((FILE *)p); }