return linktype;
}
-#define EXTRACT_
+/*
+ * Return the maximum snapshot length for a given DLT_ value.
+ *
+ * For most link-layer types, we use MAXIMUM_SNAPLEN, but for DLT_DBUS,
+ * the maximum is 134217728, as per
+ *
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
+ */
+u_int
+max_snaplen_for_dlt(int dlt)
+{
+ if (dlt == DLT_DBUS)
+ return 134217728;
+ else
+ return MAXIMUM_SNAPLEN;
+}
/*
* DLT_LINUX_SLL packets with a protocol type of LINUX_SLL_P_CAN or
+/*
+ * 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.
+ *
+ * pcap-common.h - common code for pcap and pcap-ng files
+ */
/*
* We use the "receiver-makes-right" approach to byte order,
extern void swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr,
u_char *data);
+
+extern u_int max_snaplen_for_dlt(int dlt);
u_int64_t tsoffset; /* time stamp offset */
};
+/*
+ * Per-pcap_t private data.
+ *
+ * max_blocksize is the maximum size of a block that we'll accept. We
+ * reject blocks bigger than this, so we don't consume too much memory
+ * with a truly huge block. It can change as we see IDBs with different
+ * link-layer header types. (Currently, we don't support IDBs with
+ * different link-layer header types, but we will support it in the
+ * future, when we offer file-reading APIs that support it.)
+ *
+ * XXX - that's an issue on ILP32 platforms, where the maximum block
+ * size of 2^31-1 would eat all but one byte of the entire address space.
+ * It's less of an issue on ILP64/LLP64 platforms, but the actual size
+ * of the address space may be limited by 1) the number of *significant*
+ * address bits (currently, x86-64 only supports 48 bits of address), 2)
+ * any limitations imposed by the operating system; 3) any limitations
+ * imposed by the amount of available backing store for anonymous pages,
+ * so we impose a limit regardless of the size of a pointer.
+ */
struct pcap_ng_sf {
u_int user_tsresol; /* time stamp resolution requested by the user */
+ u_int max_blocksize; /* don't grow buffer size past this */
bpf_u_int32 ifcount; /* number of interfaces seen in this capture */
bpf_u_int32 ifaces_size; /* size of array below */
struct pcap_ng_if *ifaces; /* array of interface information */
};
/*
- * Maximum block size; we reject blocks bigger than this, so we don't
- * consume too much memory with a truly huge block.
+ * Maximum block size for a given maximum snapshot length; we calculate
+ * this based
*
- * We define it as the size of an EPB with a MAXIMUM_SNAPLEN-sized
+ * We define it as the size of an EPB with a max_snaplen-sized
* packet and 128KB of options.
- *
- * XXX - that's an issue on ILP32 platforms, where the maximum block
- * size of 2^31-1 would eat all but one byte of the entire address space.
- * It's less of an issue on ILP64/LLP64 platforms, but the actual size
- * of the address space may be limited by 1) the number of *significant*
- * address bits (currently, x86-64 only supports 48 bits of address), 2)
- * any limitations imposed by the operating system; 3) any limitations
- * imposed by the amount of available backing store for anonymous pages.
*/
-#define MAX_BLOCKSIZE (sizeof (struct block_header) + \
- sizeof (struct enhanced_packet_block) + \
- MAXIMUM_SNAPLEN + 131072 + \
- sizeof (struct block_trailer))
+#define MAX_BLOCKSIZE(max_snaplen) (sizeof (struct block_header) + \
+ sizeof (struct enhanced_packet_block) + \
+ (max_snaplen) + 131072 + \
+ sizeof (struct block_trailer))
static void pcap_ng_cleanup(pcap_t *p);
static int pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr,
static int
read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf)
{
+ struct pcap_ng_sf *ps;
int status;
struct block_header bhdr;
u_char *bdata;
size_t data_remaining;
+ ps = p->priv;
+
status = read_bytes(fp, &bhdr, sizeof(bhdr), 0, errbuf);
if (status <= 0)
return (status); /* error or EOF */
*/
void *bigger_buffer;
- if (bhdr.total_length > MAX_BLOCKSIZE) {
+ if (bhdr.total_length > ps->max_blocksize) {
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "block is larger than maximum block size %u",
- (u_int) MAX_BLOCKSIZE);
+ ps->max_blocksize);
return (-1);
}
bigger_buffer = realloc(p->buffer, bhdr.total_length);
* Packet Block containing a full-size Ethernet frame, and
* leaving room for some options.
*
- * If we find a bigger block, we reallocate the buffer.
+ * If we find a bigger block, we reallocate the buffer, up to
+ * the maximum size. We start out with a maximum size based
+ * on a maximum snapshot length of MAXIMUM_SNAPLEN; if we see
+ * any link-layer header types with a larger maximum snapshot
+ * length, we boost the maximum.
*/
p->bufsize = 2048;
if (p->bufsize < total_length)
*err = 1;
return (NULL);
}
+ ps->max_blocksize = MAX_BLOCKSIZE(MAXIMUM_SNAPLEN);
/*
* Copy the stuff we've read to the buffer, and read the rest
done:
p->tzoff = 0; /* XXX - not used in pcap */
p->snapshot = idbp->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(idbp->linktype);
+ }
p->linktype = linktype_to_dlt(idbp->linktype);
p->linktype_ext = 0;
+ /*
+ * If the maximum block size for a packet with the maximum
+ * snapshot length for this DLT_ is bigger than the current
+ * maximum block size, increase the maximum.
+ */
+ if (MAX_BLOCKSIZE(max_snaplen_for_dlt(p->linktype)) > ps->max_blocksize)
+ ps->max_blocksize = MAX_BLOCKSIZE(max_snaplen_for_dlt(p->linktype));
+
p->next_packet_op = pcap_ng_next_packet;
p->cleanup_op = pcap_ng_cleanup;
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);
/*
* Allocate a buffer for the packet data.
- * Don't allocate more than MAXIMUM_SNAPLEN.
+ * 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 > MAXIMUM_SNAPLEN) {
- /*
- * Too-large snapshot length; trim it at the maximum.
- */
- p->bufsize = MAXIMUM_SNAPLEN;
- } else 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");
return (p);
}
+/*
+ * 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
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.
*/
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.
*/
amt_read = fread(p->buffer, 1, p->bufsize, fp);
*/
hdr->caplen = p->bufsize;
} 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) {