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.c302
1 files changed, 212 insertions, 90 deletions
diff --git a/freebsd/contrib/libpcap/sf-pcap.c b/freebsd/contrib/libpcap/sf-pcap.c
index d7cb806f..c9ac143d 100644
--- a/freebsd/contrib/libpcap/sf-pcap.c
+++ b/freebsd/contrib/libpcap/sf-pcap.c
@@ -30,27 +30,14 @@
* 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"
+#include <config.h>
#endif
+#include <pcap-types.h>
#ifdef _WIN32
-#include <pcap-stdinc.h>
-#else /* _WIN32 */
-#if HAVE_INTTYPES_H
-#include <inttypes.h>
-#elif HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#ifdef HAVE_SYS_BITYPES_H
-#include <sys/bitypes.h>
-#endif
-#include <sys/types.h>
+#include <io.h>
+#include <fcntl.h>
#endif /* _WIN32 */
#include <errno.h>
@@ -187,9 +174,8 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
sizeof(hdr) - sizeof(hdr.magic), fp);
if (amt_read != sizeof(hdr) - sizeof(hdr.magic)) {
if (ferror(fp)) {
- pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
- "error reading dump file: %s",
- pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+ errno, "error reading dump file");
} else {
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %lu file header bytes, only got %lu",
@@ -234,14 +220,6 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
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.
@@ -257,6 +235,17 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
p->version_minor = hdr.version_minor;
p->tzoff = hdr.thiszone;
p->snapshot = hdr.snaplen;
+ if (p->snapshot <= 0) {
+ /*
+ * Bogus snapshot length; use the maximum for this
+ * link-layer type as a fallback.
+ *
+ * XXX - the only reason why snapshot is signed is
+ * that pcap_snapshot() returns an int, not an
+ * unsigned int.
+ */
+ p->snapshot = max_snaplen_for_dlt(hdr.linktype);
+ }
p->linktype = linktype_to_dlt(LT_LINKTYPE(hdr.linktype));
p->linktype_ext = LT_LINKTYPE_EXT(hdr.linktype);
@@ -392,14 +381,16 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
/*
* Allocate a buffer for the packet data.
+ * Choose the minimum of the file's snapshot length and 2K bytes;
+ * that should be enough for most network packets - we'll grow it
+ * if necessary. That way, we don't allocate a huge chunk of
+ * memory just because there's a huge snapshot length, as the
+ * snapshot length might be larger than the size of the largest
+ * packet.
*/
p->bufsize = p->snapshot;
- if (p->bufsize <= 0) {
- /*
- * Bogus snapshot length; use the maximum as a fallback.
- */
- p->bufsize = MAXIMUM_SNAPLEN;
- }
+ if (p->bufsize > 2048)
+ p->bufsize = 2048;
p->buffer = malloc(p->bufsize);
if (p->buffer == NULL) {
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
@@ -414,6 +405,24 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
}
/*
+ * Grow the packet buffer to the specified size.
+ */
+static int
+grow_buffer(pcap_t *p, u_int bufsize)
+{
+ void *bigger_buffer;
+
+ bigger_buffer = realloc(p->buffer, bufsize);
+ if (bigger_buffer == NULL) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "out of memory");
+ return (0);
+ }
+ p->buffer = bigger_buffer;
+ p->bufsize = bufsize;
+ 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.
@@ -437,9 +446,8 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
amt_read = fread(&sf_hdr, 1, ps->hdrsize, fp);
if (amt_read != ps->hdrsize) {
if (ferror(fp)) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
- "error reading dump file: %s",
- pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "error reading dump file");
return (-1);
} else {
if (amt_read != 0) {
@@ -515,41 +523,87 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
break;
}
- if (hdr->caplen > p->bufsize) {
+ /*
+ * Is the packet bigger than we consider sane?
+ */
+ if (hdr->caplen > max_snaplen_for_dlt(p->linktype)) {
+ /*
+ * Yes. This may be a damaged or fuzzed file.
+ *
+ * Is it bigger than the snapshot length?
+ * (We don't treat that as an error if it's not
+ * bigger than the maximum we consider sane; see
+ * below.)
+ */
+ if (hdr->caplen > (bpf_u_int32)p->snapshot) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "invalid packet capture length %u, bigger than "
+ "snaplen of %d", hdr->caplen, p->snapshot);
+ } else {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "invalid packet capture length %u, bigger than "
+ "maximum of %u", hdr->caplen,
+ max_snaplen_for_dlt(p->linktype));
+ }
+ return (-1);
+ }
+
+ if (hdr->caplen > (bpf_u_int32)p->snapshot) {
/*
+ * The packet is bigger than the snapshot length
+ * for this file.
+ *
* This can happen due to Solaris 2.3 systems tripping
* over the BUFMOD problem and not setting the snapshot
- * 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.
+ * length correctly in the savefile header.
+ *
+ * libpcap 0.4 and later on Solaris 2.3 should set the
+ * snapshot length correctly in the pcap file header,
+ * even though they don't set a snapshot length in bufmod
+ * (the buggy bufmod chops off the *beginning* of the
+ * packet if a snapshot length is specified); they should
+ * also reduce the captured length, as supplied to the
+ * per-packet callback, to the snapshot length if it's
+ * greater than the snapshot length, so the code using
+ * libpcap should see the packet cut off at the snapshot
+ * length, even though the full packet is copied up to
+ * userland.
+ *
+ * However, perhaps some versions of libpcap failed to
+ * set the snapshot length currectly in the file header
+ * or the per-packet header, or perhaps this is a
+ * corrupted safefile or a savefile built/modified by a
+ * fuzz tester, so we check anyway. We grow the buffer
+ * to be big enough for the snapshot length, read up
+ * to the snapshot length, discard the rest of the
+ * packet, and report the snapshot length as the captured
+ * length; we don't want to hand our caller a packet
+ * bigger than the snapshot length, because they might
+ * be assuming they'll never be handed such a packet,
+ * and might copy the packet into a snapshot-length-
+ * sized buffer, assuming it'll fit.
*/
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 (hdr->caplen > p->bufsize) {
+ /*
+ * Grow the buffer to the snapshot length.
+ */
+ if (!grow_buffer(p, p->snapshot))
+ return (-1);
}
/*
- * 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.
+ * Read the first p->snapshot bytes into the buffer.
*/
- amt_read = fread(p->buffer, 1, p->bufsize, fp);
- if (amt_read != p->bufsize) {
+ amt_read = fread(p->buffer, 1, p->snapshot, fp);
+ if (amt_read != (bpf_u_int32)p->snapshot) {
if (ferror(fp)) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
- "error reading dump file: %s",
- pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(p->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "error reading dump file");
} else {
/*
* Yes, this uses hdr->caplen; technically,
@@ -560,7 +614,7 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
*/
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);
+ p->snapshot, (unsigned long)amt_read);
}
return (-1);
}
@@ -568,7 +622,7 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
/*
* Now read and discard what's left.
*/
- bytes_to_discard = hdr->caplen - p->bufsize;
+ bytes_to_discard = hdr->caplen - p->snapshot;
bytes_read = amt_read;
while (bytes_to_discard != 0) {
bytes_to_read = bytes_to_discard;
@@ -578,9 +632,9 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
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));
+ pcap_fmt_errmsg_for_errno(p->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "error reading dump file");
} else {
pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %u captured bytes, only got %lu",
@@ -595,15 +649,41 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
* Adjust caplen accordingly, so we don't get confused later
* as to how many bytes we have to play with.
*/
- hdr->caplen = p->bufsize;
+ hdr->caplen = p->snapshot;
} else {
+ if (hdr->caplen > p->bufsize) {
+ /*
+ * Grow the buffer to the next power of 2, or
+ * the snaplen, whichever is lower.
+ */
+ u_int new_bufsize;
+
+ new_bufsize = hdr->caplen;
+ /*
+ * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ */
+ new_bufsize--;
+ new_bufsize |= new_bufsize >> 1;
+ new_bufsize |= new_bufsize >> 2;
+ new_bufsize |= new_bufsize >> 4;
+ new_bufsize |= new_bufsize >> 8;
+ new_bufsize |= new_bufsize >> 16;
+ new_bufsize++;
+
+ if (new_bufsize > (u_int)p->snapshot)
+ new_bufsize = p->snapshot;
+
+ if (!grow_buffer(p, new_bufsize))
+ return (-1);
+ }
+
/* read the packet itself */
amt_read = fread(p->buffer, 1, hdr->caplen, fp);
if (amt_read != hdr->caplen) {
if (ferror(fp)) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
- "error reading dump file: %s",
- pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(p->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "error reading dump file");
} else {
pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"truncated dump file; tried to read %u captured bytes, only got %lu",
@@ -674,11 +754,11 @@ pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname)
if (f == stdout)
SET_BINMODE(f);
else
- setbuf(f, NULL);
+ setvbuf(f, NULL, _IONBF, 0);
#endif
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));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "Can't write to %s", fname);
if (f != stdout)
(void)fclose(f);
return (NULL);
@@ -723,14 +803,16 @@ pcap_dump_open(pcap_t *p, const char *fname)
f = stdout;
fname = "standard output";
} else {
-#if !defined(_WIN32) && !defined(MSDOS)
- f = fopen(fname, "w");
-#else
+ /*
+ * "b" is supported as of C90, so *all* UN*Xes should
+ * support it, even though it does nothing. It's
+ * required on Windows, as the file is a binary file
+ * and must be written in binary mode.
+ */
f = fopen(fname, "wb");
-#endif
if (f == NULL) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
- fname, pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "%s", fname);
return (NULL);
}
}
@@ -781,14 +863,15 @@ pcap_dump_open_append(pcap_t *p, const char *fname)
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
+ /*
+ * "b" is supported as of C90, so *all* UN*Xes should support it,
+ * even though it does nothing. It's required on Windows, as the
+ * file is a binary file and must be read in binary mode.
+ */
f = fopen(fname, "rb+");
-#endif
if (f == NULL) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
- fname, pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "%s", fname);
return (NULL);
}
@@ -798,8 +881,8 @@ pcap_dump_open_append(pcap_t *p, const char *fname)
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));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "%s", fname);
fclose(f);
return (NULL);
} else if (feof(f) && amt_read > 0) {
@@ -815,7 +898,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname)
* We turn off buffering.
* XXX - why? And why not on the standard output?
*/
- setbuf(f, NULL);
+ setvbuf(f, NULL, _IONBF, 0);
#endif
/*
@@ -906,8 +989,8 @@ pcap_dump_open_append(pcap_t *p, const char *fname)
* 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));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "Can't write to %s", fname);
(void)fclose(f);
return (NULL);
}
@@ -917,8 +1000,8 @@ pcap_dump_open_append(pcap_t *p, const char *fname)
* 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));
+ pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "Can't seek to end of %s", fname);
(void)fclose(f);
return (NULL);
}
@@ -937,6 +1020,45 @@ pcap_dump_ftell(pcap_dumper_t *p)
return (ftell((FILE *)p));
}
+#if defined(HAVE_FSEEKO)
+/*
+ * We have fseeko(), so we have ftello().
+ * If we have large file support (files larger than 2^31-1 bytes),
+ * ftello() will give us a current file position with more than 32
+ * bits.
+ */
+int64_t
+pcap_dump_ftell64(pcap_dumper_t *p)
+{
+ return (ftello((FILE *)p));
+}
+#elif defined(_MSC_VER)
+/*
+ * We have Visual Studio; we support only 2005 and later, so we have
+ * _ftelli64().
+ */
+int64_t
+pcap_dump_ftell64(pcap_dumper_t *p)
+{
+ return (_ftelli64((FILE *)p));
+}
+#else
+/*
+ * We don't have ftello() or _ftelli64(), so fall back on ftell().
+ * Either long is 64 bits, in which case ftell() should suffice,
+ * or this is probably an older 32-bit UN*X without large file
+ * support, which means you'll probably get errors trying to
+ * write files > 2^31-1, so it won't matter anyway.
+ *
+ * XXX - what about MinGW?
+ */
+int64_t
+pcap_dump_ftell64(pcap_dumper_t *p)
+{
+ return (ftell((FILE *)p));
+}
+#endif
+
int
pcap_dump_flush(pcap_dumper_t *p)
{