/* * Check whether this is a pcap savefile and, if it is, extract the * relevant information from the header. */ 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 && magic != NSEC_TCPDUMP_MAGIC) { magic = SWAPLONG(magic); if (magic != TCPDUMP_MAGIC && magic != KUZNETZOV_TCPDUMP_MAGIC && magic != NSEC_TCPDUMP_MAGIC) return (NULL); /* nope */ 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)) { 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", (unsigned long)sizeof(hdr), (unsigned long)amt_read); } *err = 1; return (NULL); } /* * If it's a byte-swapped capture file, byte-swap the header. */ if (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) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic pcap savefile format"); *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; } /* * 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->swapped = swapped; p->version_major = hdr.version_major; 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); 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, * 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) ps->lengths_swapped = SWAPPED; else if (hdr.version_minor == 3) ps->lengths_swapped = MAYBE_SWAPPED; else ps->lengths_swapped = NOT_SWAPPED; break; case 543: ps->lengths_swapped = SWAPPED; break; default: ps->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. */ ps->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 ps->hdrsize = sizeof(struct pcap_sf_pkthdr); /* * 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 > 2048) p->bufsize = 2048; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); free(p); *err = 1; return (NULL); } p->cleanup_op = sf_cleanup; return (p); }
/* * 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 *ps = p->priv; struct pcap_sf_patched_pkthdr sf_hdr; FILE *fp = p->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, ps->hdrsize, fp); if (amt_read != ps->hdrsize) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); return (-1); } else { if (amt_read != 0) { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %lu header bytes, only got %lu", (unsigned long)ps->hdrsize, (unsigned long)amt_read); return (-1); } /* EOF */ return (1); } } if (p->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; } 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 (ps->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; } /* * 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 * 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 > p->bufsize) { /* * Grow the buffer to the snapshot length. */ if (!grow_buffer(p, p->snapshot)) return (-1); } /* * 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)) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { /* * 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); } /* * 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_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", 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; } 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_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", hdr->caplen, (unsigned long)amt_read); } return (-1); } }
/* * Check whether this is a pcapng savefile and, if it is, extract the * relevant information from the header. */ pcap_t * pcap_ng_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf, int *err) { size_t amt_read; bpf_u_int32 total_length; bpf_u_int32 byte_order_magic; struct block_header *bhdrp; struct section_header_block *shbp; pcap_t *p; int swapped = 0; struct pcap_ng_sf *ps; int status; struct block_cursor cursor; struct interface_description_block *idbp; /* * Assume no read errors. */ *err = 0; /* * Check whether the first 4 bytes of the file are the block * type for a pcapng savefile. */ if (magic != BT_SHB) { /* * XXX - check whether this looks like what the block * type would be after being munged by mapping between * UN*X and DOS/Windows text file format and, if it * does, look for the byte-order magic number in * the appropriate place and, if we find it, report * this as possibly being a pcapng file transferred * between UN*X and Windows in text file format? */ return (NULL); /* nope */ } /* * OK, they are. However, that's just \n\r\r\n, so it could, * conceivably, be an ordinary text file. * * It could not, however, conceivably be any other type of * capture file, so we can read the rest of the putative * Section Header Block; put the block type in the common * header, read the rest of the common header and the * fixed-length portion of the SHB, and look for the byte-order * magic value. */ amt_read = fread(&total_length, 1, sizeof(total_length), fp); if (amt_read < sizeof(total_length)) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); *err = 1; return (NULL); /* fail */ } /* * Possibly a weird short text file, so just say * "not pcapng". */ return (NULL); } amt_read = fread(&byte_order_magic, 1, sizeof(byte_order_magic), fp); if (amt_read < sizeof(byte_order_magic)) { if (ferror(fp)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); *err = 1; return (NULL); /* fail */ } /* * Possibly a weird short text file, so just say * "not pcapng". */ return (NULL); } if (byte_order_magic != BYTE_ORDER_MAGIC) { byte_order_magic = SWAPLONG(byte_order_magic); if (byte_order_magic != BYTE_ORDER_MAGIC) { /* * Not a pcapng file. */ return (NULL); } swapped = 1; total_length = SWAPLONG(total_length); } /* * Check the sanity of the total length. */ if (total_length < sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer)) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Section Header Block in pcapng dump file has a length of %u < %" PRIsize, total_length, sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer)); *err = 1; return (NULL); } /* * Make sure it's not too big. */ if (total_length > INITIAL_MAX_BLOCKSIZE) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcapng block size %u > maximum %u", total_length, INITIAL_MAX_BLOCKSIZE); *err = 1; return (NULL); } /* * OK, this is a good pcapng file. * Allocate a pcap_t for it. */ p = pcap_open_offline_common(errbuf, sizeof (struct pcap_ng_sf)); if (p == NULL) { /* Allocation failed. */ *err = 1; return (NULL); } p->swapped = swapped; ps = p->priv; /* * What precision does the user want? */ switch (precision) { case PCAP_TSTAMP_PRECISION_MICRO: ps->user_tsresol = 1000000; break; case PCAP_TSTAMP_PRECISION_NANO: ps->user_tsresol = 1000000000; break; default: pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown time stamp resolution %u", precision); free(p); *err = 1; return (NULL); } p->opt.tstamp_precision = precision; /* * Allocate a buffer into which to read blocks. We default to * the maximum of: * * the total length of the SHB for which we read the header; * * 2K, which should be more than large enough for an Enhanced * Packet Block containing a full-size Ethernet frame, and * leaving room for some options. * * If we find a bigger block, we reallocate the buffer, up to * the maximum size. We start out with a maximum size of * INITIAL_MAX_BLOCKSIZE; if we see any link-layer header types * with a maximum snapshot that results in a larger maximum * block length, we boost the maximum. */ p->bufsize = 2048; if (p->bufsize < total_length) p->bufsize = total_length; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); free(p); *err = 1; return (NULL); } ps->max_blocksize = INITIAL_MAX_BLOCKSIZE; /* * Copy the stuff we've read to the buffer, and read the rest * of the SHB. */ bhdrp = (struct block_header *)p->buffer; shbp = (struct section_header_block *)((u_char *)p->buffer + sizeof(struct block_header)); bhdrp->block_type = magic; bhdrp->total_length = total_length; shbp->byte_order_magic = byte_order_magic; if (read_bytes(fp, (u_char *)p->buffer + (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), total_length - (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), 1, errbuf) == -1) goto fail; if (p->swapped) { /* * Byte-swap the fields we've read. */ shbp->major_version = SWAPSHORT(shbp->major_version); shbp->minor_version = SWAPSHORT(shbp->minor_version); /* * XXX - we don't care about the section length. */ } /* currently only SHB version 1.0 is supported */ if (! (shbp->major_version == PCAP_NG_VERSION_MAJOR && shbp->minor_version == PCAP_NG_VERSION_MINOR)) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unsupported pcapng savefile version %u.%u", shbp->major_version, shbp->minor_version); goto fail; } p->version_major = shbp->major_version; p->version_minor = shbp->minor_version; /* * Save the time stamp resolution the user requested. */ p->opt.tstamp_precision = precision; /* * Now start looking for an Interface Description Block. */ for (;;) { /* * Read the next block. */ status = read_block(fp, p, &cursor, errbuf); if (status == 0) { /* EOF - no IDB in this file */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has no Interface Description Blocks"); goto fail; } if (status == -1) goto fail; /* error */ switch (cursor.block_type) { case BT_IDB: /* * Get a pointer to the fixed-length portion of the * IDB. */ idbp = get_from_block_data(&cursor, sizeof(*idbp), errbuf); if (idbp == NULL) goto fail; /* error */ /* * Byte-swap it if necessary. */ if (p->swapped) { idbp->linktype = SWAPSHORT(idbp->linktype); idbp->snaplen = SWAPLONG(idbp->snaplen); } /* * Try to add this interface. */ if (!add_interface(p, &cursor, errbuf)) goto fail; goto done; case BT_EPB: case BT_SPB: case BT_PB: /* * Saw a packet before we saw any IDBs. That's * not valid, as we don't know what link-layer * encapsulation the packet has. */ pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has a packet block before any Interface Description Blocks"); goto fail; default: /* * Just ignore it. */ break; } } done: 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_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype)) > ps->max_blocksize) ps->max_blocksize = MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype)); p->next_packet_op = pcap_ng_next_packet; p->cleanup_op = pcap_ng_cleanup; return (p); fail: free(ps->ifaces); free(p->buffer); free(p); *err = 1; return (NULL); }