diff options
Diffstat (limited to 'freebsd/contrib/libpcap/sf-pcap.c')
-rw-r--r-- | freebsd/contrib/libpcap/sf-pcap.c | 302 |
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) { |