summaryrefslogtreecommitdiffstats
path: root/freebsd/contrib/libpcap/sf-pcap.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/contrib/libpcap/sf-pcap.c')
-rw-r--r--freebsd/contrib/libpcap/sf-pcap.c540
1 files changed, 440 insertions, 100 deletions
diff --git a/freebsd/contrib/libpcap/sf-pcap.c b/freebsd/contrib/libpcap/sf-pcap.c
index 6fda84f2..d7cb806f 100644
--- a/freebsd/contrib/libpcap/sf-pcap.c
+++ b/freebsd/contrib/libpcap/sf-pcap.c
@@ -39,9 +39,9 @@ static const char rcsid[] _U_ =
#include "config.h"
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <pcap-stdinc.h>
-#else /* WIN32 */
+#else /* _WIN32 */
#if HAVE_INTTYPES_H
#include <inttypes.h>
#elif HAVE_STDINT_H
@@ -51,7 +51,7 @@ static const char rcsid[] _U_ =
#include <sys/bitypes.h>
#endif
#include <sys/types.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
#include <errno.h>
#include <memory.h>
@@ -72,7 +72,7 @@ static const char rcsid[] _U_ =
/*
* Setting O_BINARY on DOS/Windows is a bit tricky
*/
-#if defined(WIN32)
+#if defined(_WIN32)
#define SET_BINMODE(f) _setmode(_fileno(f), _O_BINARY)
#elif defined(MSDOS)
#if defined(__HIGHC__)
@@ -125,25 +125,57 @@ static const char rcsid[] _U_ =
static int pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **datap);
/*
+ * Private data for reading pcap savefiles.
+ */
+typedef enum {
+ NOT_SWAPPED,
+ SWAPPED,
+ MAYBE_SWAPPED
+} swapped_type_t;
+
+typedef enum {
+ PASS_THROUGH,
+ SCALE_UP,
+ SCALE_DOWN
+} tstamp_scale_type_t;
+
+struct pcap_sf {
+ size_t hdrsize;
+ swapped_type_t lengths_swapped;
+ tstamp_scale_type_t scale_type;
+};
+
+/*
* 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)
+pcap_t *
+pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
+ int *err)
{
struct pcap_file_header hdr;
size_t amt_read;
+ pcap_t *p;
+ int swapped = 0;
+ struct pcap_sf *ps;
+
+ /*
+ * Assume no read errors.
+ */
+ *err = 0;
/*
* 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) {
+ if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC &&
+ magic != NSEC_TCPDUMP_MAGIC) {
magic = SWAPLONG(magic);
- if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC)
- return (0); /* nope */
- p->sf.swapped = 1;
+ if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC &&
+ magic != NSEC_TCPDUMP_MAGIC)
+ return (NULL); /* nope */
+ swapped = 1;
}
/*
@@ -155,22 +187,23 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
sizeof(hdr) - sizeof(hdr.magic), fp);
if (amt_read != sizeof(hdr) - sizeof(hdr.magic)) {
if (ferror(fp)) {
- snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"error reading dump file: %s",
pcap_strerror(errno));
} else {
- snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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);
+ *err = 1;
+ return (NULL);
}
/*
* If it's a byte-swapped capture file, byte-swap the header.
*/
- if (p->sf.swapped) {
+ if (swapped) {
hdr.version_major = SWAPSHORT(hdr.version_major);
hdr.version_minor = SWAPSHORT(hdr.version_minor);
hdr.thiszone = SWAPLONG(hdr.thiszone);
@@ -180,18 +213,106 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
}
if (hdr.version_major < PCAP_VERSION_MAJOR) {
- snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"archaic pcap savefile format");
- return (-1);
+ *err = 1;
+ return (NULL);
+ }
+
+ /*
+ * currently only versions 2.[0-4] are supported with
+ * the exception of 543.0 for DG/UX tcpdump.
+ */
+ if (! ((hdr.version_major == PCAP_VERSION_MAJOR &&
+ hdr.version_minor <= PCAP_VERSION_MINOR) ||
+ (hdr.version_major == 543 &&
+ hdr.version_minor == 0))) {
+ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "unsupported pcap savefile version %u.%u",
+ hdr.version_major, hdr.version_minor);
+ *err = 1;
+ return NULL;
+ }
+
+ if (hdr.snaplen > MAXIMUM_SNAPLEN) {
+ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "invalid file capture length %u, bigger than "
+ "maximum of %u", hdr.snaplen, MAXIMUM_SNAPLEN);
+ *err = 1;
+ return NULL;
+ }
+
+ /*
+ * OK, this is a good pcap file.
+ * Allocate a pcap_t for it.
+ */
+ p = pcap_open_offline_common(errbuf, sizeof (struct pcap_sf));
+ if (p == NULL) {
+ /* Allocation failed. */
+ *err = 1;
+ return (NULL);
}
- p->sf.version_major = hdr.version_major;
- p->sf.version_minor = hdr.version_minor;
+ p->swapped = swapped;
+ p->version_major = hdr.version_major;
+ p->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;
+ p->next_packet_op = pcap_next_packet;
+
+ ps = p->priv;
+
+ p->opt.tstamp_precision = precision;
+
+ /*
+ * Will we need to scale the timestamps to match what the
+ * user wants?
+ */
+ switch (precision) {
+
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ if (magic == NSEC_TCPDUMP_MAGIC) {
+ /*
+ * The file has nanoseconds, the user
+ * wants microseconds; scale the
+ * precision down.
+ */
+ ps->scale_type = SCALE_DOWN;
+ } else {
+ /*
+ * The file has microseconds, the
+ * user wants microseconds; nothing to do.
+ */
+ ps->scale_type = PASS_THROUGH;
+ }
+ break;
+
+ case PCAP_TSTAMP_PRECISION_NANO:
+ if (magic == NSEC_TCPDUMP_MAGIC) {
+ /*
+ * The file has nanoseconds, the
+ * user wants nanoseconds; nothing to do.
+ */
+ ps->scale_type = PASS_THROUGH;
+ } else {
+ /*
+ * The file has microoseconds, the user
+ * wants nanoseconds; scale the
+ * precision up.
+ */
+ ps->scale_type = SCALE_UP;
+ }
+ break;
+
+ default:
+ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "unknown time stamp resolution %u", precision);
+ free(p);
+ *err = 1;
+ return (NULL);
+ }
/*
* We interchanged the caplen and len fields at version 2.3,
@@ -207,19 +328,19 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
case 2:
if (hdr.version_minor < 3)
- p->sf.lengths_swapped = SWAPPED;
+ ps->lengths_swapped = SWAPPED;
else if (hdr.version_minor == 3)
- p->sf.lengths_swapped = MAYBE_SWAPPED;
+ ps->lengths_swapped = MAYBE_SWAPPED;
else
- p->sf.lengths_swapped = NOT_SWAPPED;
+ ps->lengths_swapped = NOT_SWAPPED;
break;
case 543:
- p->sf.lengths_swapped = SWAPPED;
+ ps->lengths_swapped = SWAPPED;
break;
default:
- p->sf.lengths_swapped = NOT_SWAPPED;
+ ps->lengths_swapped = NOT_SWAPPED;
break;
}
@@ -241,7 +362,7 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
* data ourselves and read from that buffer in order to
* make that work.
*/
- p->sf.hdrsize = sizeof(struct pcap_sf_patched_pkthdr);
+ ps->hdrsize = sizeof(struct pcap_sf_patched_pkthdr);
if (p->linktype == DLT_EN10MB) {
/*
@@ -267,7 +388,7 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
p->snapshot += 14;
}
} else
- p->sf.hdrsize = sizeof(struct pcap_sf_pkthdr);
+ ps->hdrsize = sizeof(struct pcap_sf_pkthdr);
/*
* Allocate a buffer for the packet data.
@@ -275,17 +396,21 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
p->bufsize = p->snapshot;
if (p->bufsize <= 0) {
/*
- * Bogus snapshot length; use 64KiB as a fallback.
+ * Bogus snapshot length; use the maximum as a fallback.
*/
- p->bufsize = 65536;
+ p->bufsize = MAXIMUM_SNAPLEN;
}
p->buffer = malloc(p->bufsize);
if (p->buffer == NULL) {
- snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
- return (-1);
+ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
+ free(p);
+ *err = 1;
+ return (NULL);
}
- return (1);
+ p->cleanup_op = sf_cleanup;
+
+ return (p);
}
/*
@@ -296,8 +421,9 @@ pcap_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf)
static int
pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
{
+ struct pcap_sf *ps = p->priv;
struct pcap_sf_patched_pkthdr sf_hdr;
- FILE *fp = p->sf.rfile;
+ FILE *fp = p->rfile;
size_t amt_read;
bpf_u_int32 t;
@@ -308,18 +434,18 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
* 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) {
+ amt_read = fread(&sf_hdr, 1, ps->hdrsize, fp);
+ if (amt_read != ps->hdrsize) {
if (ferror(fp)) {
- snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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,
+ pcap_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)ps->hdrsize,
(unsigned long)amt_read);
return (-1);
}
@@ -328,7 +454,7 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
}
}
- if (p->sf.swapped) {
+ if (p->swapped) {
/* these were written in opposite byte order */
hdr->caplen = SWAPLONG(sf_hdr.caplen);
hdr->len = SWAPLONG(sf_hdr.len);
@@ -340,8 +466,34 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
hdr->ts.tv_sec = sf_hdr.ts.tv_sec;
hdr->ts.tv_usec = sf_hdr.ts.tv_usec;
}
+
+ switch (ps->scale_type) {
+
+ case PASS_THROUGH:
+ /*
+ * Just pass the time stamp through.
+ */
+ break;
+
+ case SCALE_UP:
+ /*
+ * File has microseconds, user wants nanoseconds; convert
+ * it.
+ */
+ hdr->ts.tv_usec = hdr->ts.tv_usec * 1000;
+ break;
+
+ case SCALE_DOWN:
+ /*
+ * File has nanoseconds, user wants microseconds; convert
+ * it.
+ */
+ hdr->ts.tv_usec = hdr->ts.tv_usec / 1000;
+ break;
+ }
+
/* Swap the caplen and len fields, if necessary. */
- switch (p->sf.lengths_swapped) {
+ switch (ps->lengths_swapped) {
case NOT_SWAPPED:
break;
@@ -367,63 +519,93 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
/*
* 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.
+ * correctly in the savefile header.
+ * This can also happen with a corrupted savefile or a
+ * savefile built/modified by a fuzz tester.
+ * 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");
+ size_t bytes_to_discard;
+ size_t bytes_to_read, bytes_read;
+ char discard_buf[4096];
+
+ if (hdr->caplen > MAXIMUM_SNAPLEN) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "invalid packet capture length %u, bigger than "
+ "maximum of %u", hdr->caplen, MAXIMUM_SNAPLEN);
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) {
+ /*
+ * XXX - we don't grow the buffer here because some
+ * program might assume that it will never get packets
+ * bigger than the snapshot length; for example, it might
+ * copy data from our buffer to a buffer of its own,
+ * allocated based on the return value of pcap_snapshot().
+ *
+ * Read the first p->bufsize bytes into the buffer.
+ */
+ amt_read = fread(p->buffer, 1, p->bufsize, fp);
+ if (amt_read != p->bufsize) {
if (ferror(fp)) {
- snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"error reading dump file: %s",
pcap_strerror(errno));
} else {
- snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ /*
+ * Yes, this uses hdr->caplen; technically,
+ * it's true, because we would try to read
+ * and discard the rest of those bytes, and
+ * that would fail because we got EOF before
+ * the read finished.
+ */
+ pcap_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.
+ * Now read and discard what's left.
+ */
+ bytes_to_discard = hdr->caplen - p->bufsize;
+ bytes_read = amt_read;
+ while (bytes_to_discard != 0) {
+ bytes_to_read = bytes_to_discard;
+ if (bytes_to_read > sizeof (discard_buf))
+ bytes_to_read = sizeof (discard_buf);
+ amt_read = fread(discard_buf, 1, bytes_to_read, fp);
+ bytes_read += amt_read;
+ if (amt_read != bytes_to_read) {
+ if (ferror(fp)) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "error reading dump file: %s",
+ pcap_strerror(errno));
+ } else {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "truncated dump file; tried to read %u captured bytes, only got %lu",
+ hdr->caplen, (unsigned long)bytes_read);
+ }
+ return (-1);
+ }
+ bytes_to_discard -= amt_read;
+ }
+
+ /*
+ * 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,
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"error reading dump file: %s",
pcap_strerror(errno));
} else {
- snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %u captured bytes, only got %lu",
hdr->caplen, (unsigned long)amt_read);
}
@@ -432,33 +614,18 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
}
*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;
- }
- }
+ if (p->swapped)
+ swap_pseudo_headers(p->linktype, hdr, *data);
return (0);
}
static int
-sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
+sf_write_header(pcap_t *p, FILE *fp, int linktype, int thiszone, int snaplen)
{
struct pcap_file_header hdr;
- hdr.magic = TCPDUMP_MAGIC;
+ hdr.magic = p->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO ? NSEC_TCPDUMP_MAGIC : TCPDUMP_MAGIC;
hdr.version_major = PCAP_VERSION_MAJOR;
hdr.version_minor = PCAP_VERSION_MINOR;
@@ -496,7 +663,7 @@ static pcap_dumper_t *
pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname)
{
-#if defined(WIN32) || defined(MSDOS)
+#if defined(_WIN32) || defined(MSDOS)
/*
* If we're writing to the standard output, put it in binary
* mode, as savefiles are binary files.
@@ -509,8 +676,8 @@ pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname)
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",
+ if (sf_write_header(p, f, linktype, p->tzoff, p->snapshot) == -1) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s",
fname, pcap_strerror(errno));
if (f != stdout)
(void)fclose(f);
@@ -533,31 +700,36 @@ pcap_dump_open(pcap_t *p, const char *fname)
* link-layer type, so we can't use it.
*/
if (!p->activated) {
- snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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,
+ pcap_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 == NULL) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "A null pointer was supplied as the file name");
+ return NULL;
+ }
if (fname[0] == '-' && fname[1] == '\0') {
f = stdout;
fname = "standard output";
} else {
-#if !defined(WIN32) && !defined(MSDOS)
+#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",
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
fname, pcap_strerror(errno));
return (NULL);
}
@@ -570,12 +742,12 @@ pcap_dump_open(pcap_t *p, const char *fname)
*/
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,
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"stream: link-layer type %d isn't supported in savefiles",
p->linktype);
return (NULL);
@@ -585,6 +757,174 @@ pcap_dump_fopen(pcap_t *p, FILE *f)
return (pcap_setup_dump(p, linktype, f, "stream"));
}
+pcap_dumper_t *
+pcap_dump_open_append(pcap_t *p, const char *fname)
+{
+ FILE *f;
+ int linktype;
+ size_t amt_read;
+ struct pcap_file_header ph;
+
+ linktype = dlt_to_linktype(p->linktype);
+ if (linktype == -1) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: link-layer type %d isn't supported in savefiles",
+ fname, linktype);
+ return (NULL);
+ }
+
+ if (fname == NULL) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "A null pointer was supplied as the file name");
+ return NULL;
+ }
+ if (fname[0] == '-' && fname[1] == '\0')
+ return (pcap_setup_dump(p, linktype, stdout, "standard output"));
+
+#if !defined(_WIN32) && !defined(MSDOS)
+ f = fopen(fname, "r+");
+#else
+ f = fopen(fname, "rb+");
+#endif
+ if (f == NULL) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
+ fname, pcap_strerror(errno));
+ return (NULL);
+ }
+
+ /*
+ * Try to read a pcap header.
+ */
+ amt_read = fread(&ph, 1, sizeof (ph), f);
+ if (amt_read != sizeof (ph)) {
+ if (ferror(f)) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
+ fname, pcap_strerror(errno));
+ fclose(f);
+ return (NULL);
+ } else if (feof(f) && amt_read > 0) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: truncated pcap file header", fname);
+ fclose(f);
+ return (NULL);
+ }
+ }
+
+#if defined(_WIN32) || defined(MSDOS)
+ /*
+ * We turn off buffering.
+ * XXX - why? And why not on the standard output?
+ */
+ setbuf(f, NULL);
+#endif
+
+ /*
+ * If a header is already present and:
+ *
+ * it's not for a pcap file of the appropriate resolution
+ * and the right byte order for this machine;
+ *
+ * the link-layer header types don't match;
+ *
+ * the snapshot lengths don't match;
+ *
+ * return an error.
+ */
+ if (amt_read > 0) {
+ /*
+ * A header is already present.
+ * Do the checks.
+ */
+ switch (ph.magic) {
+
+ case TCPDUMP_MAGIC:
+ if (p->opt.tstamp_precision != PCAP_TSTAMP_PRECISION_MICRO) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: different time stamp precision, cannot append to file", fname);
+ fclose(f);
+ return (NULL);
+ }
+ break;
+
+ case NSEC_TCPDUMP_MAGIC:
+ if (p->opt.tstamp_precision != PCAP_TSTAMP_PRECISION_NANO) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: different time stamp precision, cannot append to file", fname);
+ fclose(f);
+ return (NULL);
+ }
+ break;
+
+ case SWAPLONG(TCPDUMP_MAGIC):
+ case SWAPLONG(NSEC_TCPDUMP_MAGIC):
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: different byte order, cannot append to file", fname);
+ fclose(f);
+ return (NULL);
+
+ case KUZNETZOV_TCPDUMP_MAGIC:
+ case SWAPLONG(KUZNETZOV_TCPDUMP_MAGIC):
+ case NAVTEL_TCPDUMP_MAGIC:
+ case SWAPLONG(NAVTEL_TCPDUMP_MAGIC):
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: not a pcap file to which we can append", fname);
+ fclose(f);
+ return (NULL);
+
+ default:
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: not a pcap file", fname);
+ fclose(f);
+ return (NULL);
+ }
+
+ /*
+ * Good version?
+ */
+ if (ph.version_major != PCAP_VERSION_MAJOR ||
+ ph.version_minor != PCAP_VERSION_MINOR) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: version is %u.%u, cannot append to file", fname,
+ ph.version_major, ph.version_minor);
+ fclose(f);
+ return (NULL);
+ }
+ if ((bpf_u_int32)linktype != ph.linktype) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: different linktype, cannot append to file", fname);
+ fclose(f);
+ return (NULL);
+ }
+ if ((bpf_u_int32)p->snapshot != ph.snaplen) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: different snaplen, cannot append to file", fname);
+ fclose(f);
+ return (NULL);
+ }
+ } else {
+ /*
+ * A header isn't present; attempt to write it.
+ */
+ if (sf_write_header(p, f, linktype, p->tzoff, p->snapshot) == -1) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s",
+ fname, pcap_strerror(errno));
+ (void)fclose(f);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Start writing at the end of the file.
+ */
+ if (fseek(f, 0, SEEK_END) == -1) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't seek to end of %s: %s",
+ fname, pcap_strerror(errno));
+ (void)fclose(f);
+ return (NULL);
+ }
+ return ((pcap_dumper_t *)f);
+}
+
FILE *
pcap_dump_file(pcap_dumper_t *p)
{