diff options
Diffstat (limited to 'lib/libpcap/libpcap/sf-pcapng.c')
-rw-r--r-- | lib/libpcap/libpcap/sf-pcapng.c | 1888 |
1 files changed, 1888 insertions, 0 deletions
diff --git a/lib/libpcap/libpcap/sf-pcapng.c b/lib/libpcap/libpcap/sf-pcapng.c new file mode 100644 index 0000000..4909447 --- /dev/null +++ b/lib/libpcap/libpcap/sf-pcapng.c @@ -0,0 +1,1888 @@ +/* + * 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. + * + * sf-pcapng.c - pcapng-file-format-specific code from savefile.c + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pcap/pcap-inttypes.h> + +#include <errno.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pcap-int.h" + +#include "pcap-common.h" + +#ifdef __APPLE__ +#include "pcap-util.h" +#include <limits.h> + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#endif /* __APPLE__ */ + +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "sf-pcapng.h" + +/* + * Block types. + */ + +/* + * Common part at the beginning of all blocks. + */ +struct block_header { + bpf_u_int32 block_type; + bpf_u_int32 total_length; +}; + +/* + * Common trailer at the end of all blocks. + */ +struct block_trailer { + bpf_u_int32 total_length; +}; + +/* + * Common options. + */ +#define OPT_ENDOFOPT 0 /* end of options */ +#define OPT_COMMENT 1 /* comment string */ + +/* + * Option header. + */ +struct option_header { + u_short option_code; + u_short option_length; +}; + +/* + * Structures for the part of each block type following the common + * part. + */ + +/* + * Section Header Block. + */ +#define BT_SHB 0x0A0D0D0A +#define BT_SHB_INSANE_MAX 1024U*1024U*1U /* 1MB should be enough */ +struct section_header_block { + bpf_u_int32 byte_order_magic; + u_short major_version; + u_short minor_version; + uint64_t section_length; + /* followed by options and trailer */ +}; + +/* + * Byte-order magic value. + */ +#define BYTE_ORDER_MAGIC 0x1A2B3C4D + +/* + * Current version number. If major_version isn't PCAP_NG_VERSION_MAJOR, + * that means that this code can't read the file. + */ +#define PCAP_NG_VERSION_MAJOR 1 +#define PCAP_NG_VERSION_MINOR 0 + +/* + * Interface Description Block. + */ +#define BT_IDB 0x00000001 + +struct interface_description_block { + u_short linktype; + u_short reserved; + bpf_u_int32 snaplen; + /* followed by options and trailer */ +}; + +/* + * Options in the IDB. + */ +#define IF_NAME 2 /* interface name string */ +#define IF_DESCRIPTION 3 /* interface description string */ +#define IF_IPV4ADDR 4 /* interface's IPv4 address and netmask */ +#define IF_IPV6ADDR 5 /* interface's IPv6 address and prefix length */ +#define IF_MACADDR 6 /* interface's MAC address */ +#define IF_EUIADDR 7 /* interface's EUI address */ +#define IF_SPEED 8 /* interface's speed, in bits/s */ +#define IF_TSRESOL 9 /* interface's time stamp resolution */ +#define IF_TZONE 10 /* interface's time zone */ +#define IF_FILTER 11 /* filter used when capturing on interface */ +#define IF_OS 12 /* string OS on which capture on this interface was done */ +#define IF_FCSLEN 13 /* FCS length for this interface */ +#define IF_TSOFFSET 14 /* time stamp offset for this interface */ + +/* + * Enhanced Packet Block. + */ +#define BT_EPB 0x00000006 + +struct enhanced_packet_block { + bpf_u_int32 interface_id; + bpf_u_int32 timestamp_high; + bpf_u_int32 timestamp_low; + bpf_u_int32 caplen; + bpf_u_int32 len; + /* followed by packet data, options, and trailer */ +}; + +/* + * Simple Packet Block. + */ +#define BT_SPB 0x00000003 + +struct simple_packet_block { + bpf_u_int32 len; + /* followed by packet data and trailer */ +}; + +/* + * Packet Block. + */ +#define BT_PB 0x00000002 + +struct packet_block { + u_short interface_id; + u_short drops_count; + bpf_u_int32 timestamp_high; + bpf_u_int32 timestamp_low; + bpf_u_int32 caplen; + bpf_u_int32 len; + /* followed by packet data, options, and trailer */ +}; + +/* + * Block cursor - used when processing the contents of a block. + * Contains a pointer into the data being processed and a count + * of bytes remaining in the block. + */ +struct block_cursor { + u_char *data; + size_t data_remaining; + bpf_u_int32 block_type; +}; + +typedef enum { + PASS_THROUGH, + SCALE_UP_DEC, + SCALE_DOWN_DEC, + SCALE_UP_BIN, + SCALE_DOWN_BIN +} tstamp_scale_type_t; + +/* + * Per-interface information. + */ +struct pcap_ng_if { +#ifdef __APPLE__ + /* + * Note: + * The code related to snaplen field is taken from master of tcpdump.org + * so we will have to remove the __APPLE__ conditionals related to this field + * the next time we sync-up with the next RELEASE version from tcpdump.org + */ + uint32_t snaplen; /* snapshot length */ +#endif /* __APPLE__ */ + uint64_t tsresol; /* time stamp resolution */ + tstamp_scale_type_t scale_type; /* how to scale */ + uint64_t scale_factor; /* time stamp scale factor for power-of-10 tsresol */ + uint64_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 { + uint64_t 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 */ +}; + +/* + * The maximum block size we start with; we use an arbitrary value of + * 16 MiB. + */ +#define INITIAL_MAX_BLOCKSIZE (16*1024*1024) + +/* + * Maximum block size for a given maximum snapshot length; we define it + * as the size of an EPB with a max_snaplen-sized packet and 128KB of + * options. + */ +#define MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen) \ + (sizeof (struct block_header) + \ + sizeof (struct enhanced_packet_block) + \ + (max_snaplen) + 131072 + \ + sizeof (struct block_trailer)) + +#ifdef __APPLE__ +static int pcap_ng_next_block(pcap_t *p, struct pcap_pkthdr *hdr, + u_char **data); +static int pcap_ng_next_internal(pcap_t *p, struct pcap_pkthdr *hdr, + u_char **data, int pktonly); +void pcap_ng_cleanup(pcap_t *p); +#else +static void pcap_ng_cleanup(pcap_t *p); +#endif /* __APPLE__ */ + +static int pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, + u_char **data); + +static int +read_bytes(FILE *fp, void *buf, size_t bytes_to_read, int fail_on_eof, + char *errbuf) +{ + size_t amt_read; + + amt_read = fread(buf, 1, bytes_to_read, fp); + if (amt_read != bytes_to_read) { + if (ferror(fp)) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "error reading dump file"); + } else { + if (amt_read == 0 && !fail_on_eof) + return (0); /* EOF */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "truncated pcapng dump file; tried to read %" PRIsize " bytes, only got %" PRIsize, + bytes_to_read, amt_read); + } + return (-1); + } + return (1); +} + +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; + struct block_trailer *btrlr; + 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 */ + + if (p->swapped) { + bhdr.block_type = SWAPLONG(bhdr.block_type); + bhdr.total_length = SWAPLONG(bhdr.total_length); + } + + /* + * Is this block "too small" - i.e., is it shorter than a block + * header plus a block trailer? + */ + if (bhdr.total_length < sizeof(struct block_header) + + sizeof(struct block_trailer)) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "block in pcapng dump file has a length of %u < %" PRIsize, + bhdr.total_length, + sizeof(struct block_header) + sizeof(struct block_trailer)); + return (-1); + } + + /* + * Is the block total length a multiple of 4? + */ + if ((bhdr.total_length % 4) != 0) { + /* + * No. Report that as an error. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "block in pcapng dump file has a length of %u that is not a multiple of 4" PRIsize, + bhdr.total_length); + return (-1); + } + + /* + * Is the buffer big enough? + */ + if (p->bufsize < bhdr.total_length) { + /* + * No - make it big enough, unless it's too big, in + * which case we fail. + */ + void *bigger_buffer; + + if (bhdr.total_length > ps->max_blocksize) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcapng block size %u > maximum %u", bhdr.total_length, + ps->max_blocksize); + return (-1); + } + bigger_buffer = realloc(p->buffer, bhdr.total_length); + if (bigger_buffer == NULL) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + return (-1); + } + p->buffer = bigger_buffer; + } + + /* + * Copy the stuff we've read to the buffer, and read the rest + * of the block. + */ + memcpy(p->buffer, &bhdr, sizeof(bhdr)); + bdata = (u_char *)p->buffer + sizeof(bhdr); + data_remaining = bhdr.total_length - sizeof(bhdr); + if (read_bytes(fp, bdata, data_remaining, 1, errbuf) == -1) + return (-1); + + /* + * Get the block size from the trailer. + */ + btrlr = (struct block_trailer *)(bdata + data_remaining - sizeof (struct block_trailer)); + if (p->swapped) + btrlr->total_length = SWAPLONG(btrlr->total_length); + + /* + * Is the total length from the trailer the same as the total + * length from the header? + */ + if (bhdr.total_length != btrlr->total_length) { + /* + * No. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "block total length in header and trailer don't match"); + return (-1); + } + + /* + * Initialize the cursor. + */ + cursor->data = bdata; + cursor->data_remaining = data_remaining - sizeof(struct block_trailer); + cursor->block_type = bhdr.block_type; + return (1); +} + +#ifdef __APPLE__ +void * +#else +static void * +#endif /* __APPLE__ */ +get_from_block_data(struct block_cursor *cursor, size_t chunk_size, + char *errbuf) +{ + void *data; + + /* + * Make sure we have the specified amount of data remaining in + * the block data. + */ + if (cursor->data_remaining < chunk_size) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "block of type %u in pcapng dump file is too short", + cursor->block_type); + return (NULL); + } + + /* + * Return the current pointer, and skip past the chunk. + */ + data = cursor->data; + cursor->data += chunk_size; + cursor->data_remaining -= chunk_size; + return (data); +} + +static struct option_header * +get_opthdr_from_block_data(pcap_t *p, struct block_cursor *cursor, char *errbuf) +{ + struct option_header *opthdr; + + opthdr = get_from_block_data(cursor, sizeof(*opthdr), errbuf); + if (opthdr == NULL) { + /* + * Option header is cut short. + */ + return (NULL); + } + + /* + * Byte-swap it if necessary. + */ + if (p->swapped) { + opthdr->option_code = SWAPSHORT(opthdr->option_code); + opthdr->option_length = SWAPSHORT(opthdr->option_length); + } + + return (opthdr); +} + +static void * +get_optvalue_from_block_data(struct block_cursor *cursor, + struct option_header *opthdr, char *errbuf) +{ + size_t padded_option_len; + void *optvalue; + + /* Pad option length to 4-byte boundary */ + padded_option_len = opthdr->option_length; + padded_option_len = ((padded_option_len + 3)/4)*4; + + optvalue = get_from_block_data(cursor, padded_option_len, errbuf); + if (optvalue == NULL) { + /* + * Option value is cut short. + */ + return (NULL); + } + + return (optvalue); +} + +static int +process_idb_options(pcap_t *p, struct block_cursor *cursor, uint64_t *tsresol, + uint64_t *tsoffset, int *is_binary, char *errbuf) +{ + struct option_header *opthdr; + void *optvalue; + int saw_tsresol, saw_tsoffset; + uint8_t tsresol_opt; + u_int i; + + saw_tsresol = 0; + saw_tsoffset = 0; + while (cursor->data_remaining != 0) { + /* + * Get the option header. + */ + opthdr = get_opthdr_from_block_data(p, cursor, errbuf); + if (opthdr == NULL) { + /* + * Option header is cut short. + */ + return (-1); + } + + /* + * Get option value. + */ + optvalue = get_optvalue_from_block_data(cursor, opthdr, + errbuf); + if (optvalue == NULL) { + /* + * Option value is cut short. + */ + return (-1); + } + + switch (opthdr->option_code) { + + case OPT_ENDOFOPT: + if (opthdr->option_length != 0) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block has opt_endofopt option with length %u != 0", + opthdr->option_length); + return (-1); + } + goto done; + + case IF_TSRESOL: + if (opthdr->option_length != 1) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block has if_tsresol option with length %u != 1", + opthdr->option_length); + return (-1); + } + if (saw_tsresol) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block has more than one if_tsresol option"); + return (-1); + } + saw_tsresol = 1; + memcpy(&tsresol_opt, optvalue, sizeof(tsresol_opt)); + if (tsresol_opt & 0x80) { + /* + * Resolution is negative power of 2. + */ + uint8_t tsresol_shift = (tsresol_opt & 0x7F); + + if (tsresol_shift > 63) { + /* + * Resolution is too high; 2^-{res} + * won't fit in a 64-bit value. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block if_tsresol option resolution 2^-%u is too high", + tsresol_shift); + return (-1); + } + *is_binary = 1; + *tsresol = ((uint64_t)1) << tsresol_shift; + } else { + /* + * Resolution is negative power of 10. + */ + if (tsresol_opt > 19) { + /* + * Resolution is too high; 2^-{res} + * won't fit in a 64-bit value (the + * largest power of 10 that fits + * in a 64-bit value is 10^19, as + * the largest 64-bit unsigned + * value is ~1.8*10^19). + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block if_tsresol option resolution 10^-%u is too high", + tsresol_opt); + return (-1); + } + *is_binary = 0; + *tsresol = 1; + for (i = 0; i < tsresol_opt; i++) + *tsresol *= 10; + } + break; + + case IF_TSOFFSET: + if (opthdr->option_length != 8) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block has if_tsoffset option with length %u != 8", + opthdr->option_length); + return (-1); + } + if (saw_tsoffset) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Interface Description Block has more than one if_tsoffset option"); + return (-1); + } + saw_tsoffset = 1; + memcpy(tsoffset, optvalue, sizeof(*tsoffset)); + if (p->swapped) + *tsoffset = SWAPLL(*tsoffset); + break; + + default: + break; + } + } + +done: + return (0); +} + +static int +#ifdef __APPLE__ +add_interface(pcap_t *p, struct interface_description_block *idbp, + struct block_cursor *cursor, char *errbuf) +#else +add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) +#endif /* __APPLE__ */ +{ + struct pcap_ng_sf *ps; + uint64_t tsresol; + uint64_t tsoffset; + int is_binary; + + ps = p->priv; + + /* + * Count this interface. + */ + ps->ifcount++; + + /* + * Grow the array of per-interface information as necessary. + */ + if (ps->ifcount > ps->ifaces_size) { + /* + * We need to grow the array. + */ + bpf_u_int32 new_ifaces_size; + struct pcap_ng_if *new_ifaces; + + if (ps->ifaces_size == 0) { + /* + * It's currently empty. + * + * (The Clang static analyzer doesn't do enough, + * err, umm, dataflow *analysis* to realize that + * ps->ifaces_size == 0 if ps->ifaces == NULL, + * and so complains about a possible zero argument + * to realloc(), so we check for the former + * condition to shut it up. + * + * However, it doesn't complain that one of the + * multiplications below could overflow, which is + * a real, albeit extremely unlikely, problem (you'd + * need a pcapng file with tens of millions of + * interfaces).) + */ + new_ifaces_size = 1; + new_ifaces = malloc(sizeof (struct pcap_ng_if)); + } else { + /* + * It's not currently empty; double its size. + * (Perhaps overkill once we have a lot of interfaces.) + * + * Check for overflow if we double it. + */ + if (ps->ifaces_size * 2 < ps->ifaces_size) { + /* + * The maximum number of interfaces before + * ps->ifaces_size overflows is the largest + * possible 32-bit power of 2, as we do + * size doubling. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "more than %u interfaces in the file", + 0x80000000U); + return (0); + } + + /* + * ps->ifaces_size * 2 doesn't overflow, so it's + * safe to multiply. + */ + new_ifaces_size = ps->ifaces_size * 2; + + /* + * Now make sure that's not so big that it overflows + * if we multiply by sizeof (struct pcap_ng_if). + * + * That can happen on 32-bit platforms, with a 32-bit + * size_t; it shouldn't happen on 64-bit platforms, + * with a 64-bit size_t, as new_ifaces_size is + * 32 bits. + */ + if (new_ifaces_size * sizeof (struct pcap_ng_if) < new_ifaces_size) { + /* + * As this fails only with 32-bit size_t, + * the multiplication was 32x32->32, and + * the largest 32-bit value that can safely + * be multiplied by sizeof (struct pcap_ng_if) + * without overflow is the largest 32-bit + * (unsigned) value divided by + * sizeof (struct pcap_ng_if). + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "more than %u interfaces in the file", + 0xFFFFFFFFU / ((u_int)sizeof (struct pcap_ng_if))); + return (0); + } + new_ifaces = realloc(ps->ifaces, new_ifaces_size * sizeof (struct pcap_ng_if)); + } + if (new_ifaces == NULL) { + /* + * We ran out of memory. + * Give up. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "out of memory for per-interface information (%u interfaces)", + ps->ifcount); + return (0); + } + ps->ifaces_size = new_ifaces_size; + ps->ifaces = new_ifaces; + } + +#ifdef __APPLE__ + ps->ifaces[ps->ifcount - 1].snaplen = idbp->snaplen; +#endif /* __APPLE__*/ + + /* + * Set the default time stamp resolution and offset. + */ + tsresol = 1000000; /* microsecond resolution */ + is_binary = 0; /* which is a power of 10 */ + tsoffset = 0; /* absolute timestamps */ + + /* + * Now look for various time stamp options, so we know + * how to interpret the time stamps for this interface. + */ + if (process_idb_options(p, cursor, &tsresol, &tsoffset, &is_binary, + errbuf) == -1) + return (0); + + ps->ifaces[ps->ifcount - 1].tsresol = tsresol; + ps->ifaces[ps->ifcount - 1].tsoffset = tsoffset; + + /* + * Determine whether we're scaling up or down or not + * at all for this interface. + */ + if (tsresol == ps->user_tsresol) { + /* + * The resolution is the resolution the user wants, + * so we don't have to do scaling. + */ + ps->ifaces[ps->ifcount - 1].scale_type = PASS_THROUGH; + } else if (tsresol > ps->user_tsresol) { + /* + * The resolution is greater than what the user wants, + * so we have to scale the timestamps down. + */ + if (is_binary) + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_DOWN_BIN; + else { + /* + * Calculate the scale factor. + */ + ps->ifaces[ps->ifcount - 1].scale_factor = tsresol/ps->user_tsresol; + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_DOWN_DEC; + } + } else { + /* + * The resolution is less than what the user wants, + * so we have to scale the timestamps up. + */ + if (is_binary) + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_UP_BIN; + else { + /* + * Calculate the scale factor. + */ + ps->ifaces[ps->ifcount - 1].scale_factor = ps->user_tsresol/tsresol; + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_UP_DEC; + } + } + return (1); +} + +/* + * Check whether this is a pcapng savefile and, if it is, extract the + * relevant information from the header. + */ +#ifdef __APPLE__ +pcap_t * +pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, + char *errbuf, int *err, int isng) +#else +pcap_t * +pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, + char *errbuf, int *err) +#endif /* __APPLE__ */ +{ + bpf_u_int32 magic_int; + 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; +#ifdef __APPLE__ + struct interface_description_block *idbp = NULL; + long file_offset = ftell(fp); +#else + struct interface_description_block *idbp; +#endif /* __APPLE__ */ + /* + * Assume no read errors. + */ + *err = 0; + + /* + * Check whether the first 4 bytes of the file are the block + * type for a pcapng savefile. + */ + memcpy(&magic_int, magic, sizeof(magic_int)); + if (magic_int != 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) || + (total_length > BT_SHB_INSANE_MAX)) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Section Header Block in pcapng dump file has invalid length %" PRIsize " < _%u_ < %u (BT_SHB_INSANE_MAX)", + sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer), + total_length, + BT_SHB_INSANE_MAX); + + *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_int; + bhdrp->total_length = total_length; + shbp->byte_order_magic = byte_order_magic; + if (read_bytes(fp, + (u_char *)p->buffer + (sizeof(magic_int) + sizeof(total_length) + sizeof(byte_order_magic)), + total_length - (sizeof(magic_int) + 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. + */ +#ifdef __APPLE__ + if (!add_interface(p, idbp, &cursor, errbuf)) + goto fail; +#else + if (!add_interface(p, &cursor, errbuf)) + goto fail; +#endif /* __APPLE__ */ + 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->tzoff = 0; /* XXX - not used in pcap */ + p->linktype = linktype_to_dlt(idbp->linktype); + p->snapshot = pcap_adjust_snapshot(p->linktype, idbp->snaplen); + 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; + +#ifdef __APPLE__ + /* + * Special using block based API + */ + if (isng) { + p->next_packet_op = pcap_ng_next_block; + /* + * Rewind to begining of Section Header Block + */ + if (file_offset < 4) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "bad file offset"); + goto fail; + } + file_offset -= 4; + fseek(fp, file_offset, SEEK_SET); + + p->linktype = DLT_PCAPNG; + } +#endif /* __APPLE__ */ + + return (p); + +fail: + free(ps->ifaces); + free(p->buffer); + free(p); + *err = 1; + return (NULL); +} + +#ifdef __APPLE__ +void +#else +static void +#endif /* __APPLE__ */ +pcap_ng_cleanup(pcap_t *p) +{ + struct pcap_ng_sf *ps = p->priv; + + free(ps->ifaces); + sf_cleanup(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_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) +{ +#ifdef __APPLE__ + return (pcap_ng_next_internal(p, hdr, data, 1)); +} + +static int +pcap_ng_next_block(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) +{ + return (pcap_ng_next_internal(p, hdr, data, 0)); +} + +static int +pcap_ng_next_internal(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data, int pktonly) +{ +#endif /* __APPLE__ */ + struct pcap_ng_sf *ps = p->priv; + struct block_cursor cursor; + int status; + struct enhanced_packet_block *epbp; + struct simple_packet_block *spbp; + struct packet_block *pbp; + bpf_u_int32 interface_id = 0xFFFFFFFF; + struct interface_description_block *idbp; + struct section_header_block *shbp; + FILE *fp = p->rfile; + uint64_t t, sec, frac; + +#ifdef __APPLE__ + struct option_header *opthdr; + unsigned char packetpad; + + /* + * To silence Xcode static analyzer that doesn't know this depends on pcap_ng_check_header + * that initialized user_tsresol to non-zero value + */ + if (ps->user_tsresol == 0) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "internal error, check_header did not set user_tsresol"); + return (-1); + } +#endif /* __APPLE__ */ + + /* + * Look for an Enhanced Packet Block, a Simple Packet Block, + * or a Packet Block. + */ + for (;;) { + /* + * Read the block type and length; those are common + * to all blocks. + */ + status = read_block(fp, p, &cursor, p->errbuf); + if (status == 0) + return (1); /* EOF */ + if (status == -1) + return (-1); /* error */ + switch (cursor.block_type) { + + case BT_EPB: + /* + * Get a pointer to the fixed-length portion of the + * EPB. + */ + epbp = get_from_block_data(&cursor, sizeof(*epbp), + p->errbuf); + if (epbp == NULL) + return (-1); /* error */ + + /* + * Byte-swap it if necessary. + */ + if (p->swapped) { + /* these were written in opposite byte order */ + interface_id = SWAPLONG(epbp->interface_id); + hdr->caplen = SWAPLONG(epbp->caplen); + hdr->len = SWAPLONG(epbp->len); + t = ((uint64_t)SWAPLONG(epbp->timestamp_high)) << 32 | + SWAPLONG(epbp->timestamp_low); + } else { + interface_id = epbp->interface_id; + hdr->caplen = epbp->caplen; + hdr->len = epbp->len; + t = ((uint64_t)epbp->timestamp_high) << 32 | + epbp->timestamp_low; + } + goto found; + + case BT_SPB: + /* + * Get a pointer to the fixed-length portion of the + * SPB. + */ + spbp = get_from_block_data(&cursor, sizeof(*spbp), + p->errbuf); + if (spbp == NULL) + return (-1); /* error */ + + /* + * SPB packets are assumed to have arrived on + * the first interface. + */ + interface_id = 0; + + /* + * Byte-swap it if necessary. + */ + if (p->swapped) { + /* these were written in opposite byte order */ + hdr->len = SWAPLONG(spbp->len); + } else + hdr->len = spbp->len; + + /* + * The SPB doesn't give the captured length; + * it's the minimum of the snapshot length + * and the packet length. + */ + hdr->caplen = hdr->len; + if (hdr->caplen > (bpf_u_int32)p->snapshot) + hdr->caplen = p->snapshot; + t = 0; /* no time stamps */ + goto found; + + case BT_PB: + /* + * Get a pointer to the fixed-length portion of the + * PB. + */ + pbp = get_from_block_data(&cursor, sizeof(*pbp), + p->errbuf); + if (pbp == NULL) + return (-1); /* error */ + + /* + * Byte-swap it if necessary. + */ + if (p->swapped) { + /* these were written in opposite byte order */ + interface_id = SWAPSHORT(pbp->interface_id); + hdr->caplen = SWAPLONG(pbp->caplen); + hdr->len = SWAPLONG(pbp->len); + t = ((uint64_t)SWAPLONG(pbp->timestamp_high)) << 32 | + SWAPLONG(pbp->timestamp_low); + } else { + interface_id = pbp->interface_id; + hdr->caplen = pbp->caplen; + hdr->len = pbp->len; + t = ((uint64_t)pbp->timestamp_high) << 32 | + pbp->timestamp_low; + } + goto found; + + case BT_IDB: + /* + * Interface Description Block. Get a pointer + * to its fixed-length portion. + */ + idbp = get_from_block_data(&cursor, sizeof(*idbp), + p->errbuf); + if (idbp == NULL) + return (-1); /* error */ + + /* + * Byte-swap it if necessary. + */ + if (p->swapped) { + idbp->linktype = SWAPSHORT(idbp->linktype); + idbp->snaplen = SWAPLONG(idbp->snaplen); + } + + /* + * If the link-layer type or snapshot length + * differ from the ones for the first IDB we + * saw, quit. + * + * XXX - just discard packets from those + * interfaces? + */ +#ifdef __APPLE__ + if (!pktonly) { + goto skip_link_checks; + } +#endif /* __APPLE__ */ + if (p->linktype != idbp->linktype) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "an interface has a type %u different from the type of the first interface", + idbp->linktype); + return (-1); + } + + /* + * Check against the *adjusted* value of this IDB's + * snapshot length. + */ + if ((bpf_u_int32)p->snapshot != + pcap_adjust_snapshot(p->linktype, idbp->snaplen)) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "an interface has a snapshot length %u different from the type of the first interface", + idbp->snaplen); + return (-1); + } + /* + * Try to add this interface. + */ +#ifdef __APPLE__ +skip_link_checks: + if (!add_interface(p, idbp, &cursor, p->errbuf)) + return (-1); +#else + if (!add_interface(p, &cursor, p->errbuf)) + return (-1); +#endif /* __APPLE__ */ + break; + + case BT_SHB: + /* + * Section Header Block. Get a pointer + * to its fixed-length portion. + */ + shbp = get_from_block_data(&cursor, sizeof(*shbp), + p->errbuf); + if (shbp == NULL) + return (-1); /* error */ + + /* + * Assume the byte order of this section is + * the same as that of the previous section. + * We'll check for that later. + */ + if (p->swapped) { + shbp->byte_order_magic = + SWAPLONG(shbp->byte_order_magic); + shbp->major_version = + SWAPSHORT(shbp->major_version); + } + + /* + * Make sure the byte order doesn't change; + * pcap_is_swapped() shouldn't change its + * return value in the middle of reading a capture. + */ + switch (shbp->byte_order_magic) { + + case BYTE_ORDER_MAGIC: + /* + * OK. + */ + break; + + case SWAPLONG(BYTE_ORDER_MAGIC): + /* + * Byte order changes. + */ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "the file has sections with different byte orders"); + return (-1); + + default: + /* + * Not a valid SHB. + */ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "the file has a section with a bad byte order magic field"); + return (-1); + } + + /* + * Make sure the major version is the version + * we handle. + */ + if (shbp->major_version != PCAP_NG_VERSION_MAJOR) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "unknown pcapng savefile major version number %u", + shbp->major_version); + return (-1); + } + + /* + * Reset the interface count; this section should + * have its own set of IDBs. If any of them + * don't have the same interface type, snapshot + * length, or resolution as the first interface + * we saw, we'll fail. (And if we don't see + * any IDBs, we'll fail when we see a packet + * block.) + */ + ps->ifcount = 0; + break; + + default: + /* + * Not a packet block, IDB, or SHB; ignore it. + */ + break; + } + +#ifdef __APPLE__ + if (pktonly == 0) + return (0); +#endif /* __APPLE__ */ + } + +found: + /* + * Is the interface ID an interface we know? + */ + if (interface_id >= ps->ifcount) { + /* + * Yes. Fail. + */ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "a packet arrived on interface %u, but there's no Interface Description Block for that interface", + interface_id); + return (-1); + } + +#ifdef __APPLE__ + if (hdr->caplen > (bpf_u_int32)ps->ifaces[interface_id].snaplen) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "invalid packet capture length %u, bigger than " + "snaplen of %d", hdr->caplen, p->snapshot); + return (-1); + } +#else /* __APPLE__ */ + 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); + return (-1); + } +#endif /* __APPLE__ */ + + /* + * Convert the time stamp to seconds and fractions of a second, + * with the fractions being in units of the file-supplied resolution. + */ + sec = t / ps->ifaces[interface_id].tsresol + ps->ifaces[interface_id].tsoffset; + frac = t % ps->ifaces[interface_id].tsresol; + + /* + * Convert the fractions from units of the file-supplied resolution + * to units of the user-requested resolution. + */ + switch (ps->ifaces[interface_id].scale_type) { + + case PASS_THROUGH: + /* + * The interface resolution is what the user wants, + * so we're done. + */ + break; + + case SCALE_UP_DEC: + /* + * The interface resolution is less than what the user + * wants; scale the fractional part up to the units of + * the resolution the user requested by multiplying by + * the quotient of the user-requested resolution and the + * file-supplied resolution. + * + * Those resolutions are both powers of 10, and the user- + * requested resolution is greater than the file-supplied + * resolution, so the quotient in question is an integer. + * We've calculated that quotient already, so we just + * multiply by it. + */ + frac *= ps->ifaces[interface_id].scale_factor; + break; + + case SCALE_UP_BIN: + /* + * The interface resolution is less than what the user + * wants; scale the fractional part up to the units of + * the resolution the user requested by multiplying by + * the quotient of the user-requested resolution and the + * file-supplied resolution. + * + * The file-supplied resolution is a power of 2, so the + * quotient is not an integer, so, in order to do this + * entirely with integer arithmetic, we multiply by the + * user-requested resolution and divide by the file- + * supplied resolution. + * + * XXX - Is there something clever we could do here, + * given that we know that the file-supplied resolution + * is a power of 2? Doing a multiplication followed by + * a division runs the risk of overflowing, and involves + * two non-simple arithmetic operations. + */ + frac *= ps->user_tsresol; + frac /= ps->ifaces[interface_id].tsresol; + break; + + case SCALE_DOWN_DEC: + /* + * The interface resolution is greater than what the user + * wants; scale the fractional part up to the units of + * the resolution the user requested by multiplying by + * the quotient of the user-requested resolution and the + * file-supplied resolution. + * + * Those resolutions are both powers of 10, and the user- + * requested resolution is less than the file-supplied + * resolution, so the quotient in question isn't an + * integer, but its reciprocal is, and we can just divide + * by the reciprocal of the quotient. We've calculated + * the reciprocal of that quotient already, so we must + * divide by it. + */ + frac /= ps->ifaces[interface_id].scale_factor; + break; + + + case SCALE_DOWN_BIN: + /* + * The interface resolution is greater than what the user + * wants; convert the fractional part to units of the + * resolution the user requested by multiplying by the + * quotient of the user-requested resolution and the + * file-supplied resolution. We do that by multiplying + * by the user-requested resolution and dividing by the + * file-supplied resolution, as the quotient might not + * fit in an integer. + * + * The file-supplied resolution is a power of 2, so the + * quotient is not an integer, and neither is its + * reciprocal, so, in order to do this entirely with + * integer arithmetic, we multiply by the user-requested + * resolution and divide by the file-supplied resolution. + * + * XXX - Is there something clever we could do here, + * given that we know that the file-supplied resolution + * is a power of 2? Doing a multiplication followed by + * a division runs the risk of overflowing, and involves + * two non-simple arithmetic operations. + */ + frac *= ps->user_tsresol; + frac /= ps->ifaces[interface_id].tsresol; + break; + } +#ifdef _WIN32 + /* + * tv_sec and tv_used in the Windows struct timeval are both + * longs. + */ + hdr->ts.tv_sec = (long)sec; + hdr->ts.tv_usec = (long)frac; +#else + /* + * tv_sec in the UN*X struct timeval is a time_t; tv_usec is + * suseconds_t in UN*Xes that work the way the current Single + * UNIX Standard specify - but not all older UN*Xes necessarily + * support that type, so just cast to int. + */ + hdr->ts.tv_sec = (time_t)sec; + hdr->ts.tv_usec = (int)frac; +#endif + + /* + * Get a pointer to the packet data. + */ + *data = get_from_block_data(&cursor, hdr->caplen, p->errbuf); + if (*data == NULL) + return (-1); +#ifdef __APPLE__ + /* + * Skip padding. + */ + packetpad = 4 - (hdr->caplen % 4); + if (hdr->caplen % 4 != 0 && + get_from_block_data(&cursor, packetpad, p->errbuf) == NULL) + return (-1); + + memset(hdr->comment, 0, sizeof(hdr->comment)); + + if ((opthdr = get_opthdr_from_block_data(p, &cursor, p->errbuf)) != NULL && + opthdr->option_code == OPT_COMMENT && opthdr->option_length > 0) { + char *optvalue; + optvalue = get_optvalue_from_block_data(&cursor, opthdr, p->errbuf); + if (optvalue == NULL) + return (-1); + memcpy(hdr->comment, optvalue, sizeof(hdr->comment)); + } +#endif /* __APPLE */ + + if (p->swapped) + swap_pseudo_headers(p->linktype, hdr, *data); + + return (0); +} + +#ifdef __APPLE__ + +static int +sf_ng_write_header(FILE *fp, int linktype, int thiszone, int snaplen) +{ + struct pcapng_block_header bh; + struct pcapng_section_header_fields shb; + struct pcapng_interface_description_fields idb; + struct pcapng_block_trailer bt; + size_t len; + + /* + * Section Header Block + */ + len = sizeof(bh) + sizeof(shb) + sizeof(bt); + bh.block_type = PCAPNG_BT_SHB; + bh.total_length = (bpf_u_int32)len; + + shb.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC; + shb.major_version = PCAPNG_VERSION_MAJOR; + shb.minor_version = 0; + shb.section_length = 0xFFFFFFFFFFFFFFFF; + + bt.total_length = (bpf_u_int32)len; + + if (fwrite((char *)&bh, sizeof(bh), 1, fp) != 1) + return (-1); + + if (fwrite((char *)&shb, sizeof(shb), 1, fp) != 1) + return (-1); + + if (fwrite((char *)&bt, sizeof(bt), 1, fp) != 1) + return (-1); + + /* + * Interface Description Block + */ + len = sizeof(bh) + sizeof(idb) + sizeof(bt); + bh.block_type = PCAPNG_BT_IDB; + bh.total_length = (bpf_u_int32)len; + + idb.idb_reserved = 0; + idb.idb_linktype = linktype; + idb.idb_snaplen = snaplen; + + bt.total_length = (bpf_u_int32)len; + + if (fwrite((char *)&bh, sizeof(bh), 1, fp) != 1) + return (-1); + + if (fwrite((char *)&idb, sizeof(idb), 1, fp) != 1) + return (-1); + + if (fwrite((char *)&bt, sizeof(bt), 1, fp) != 1) + return (-1); + + return (0); +} + +static pcap_dumper_t * +pcap_ng_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname) +{ + pcap_dumper_t *dumper; + + dumper = pcap_alloc_dumper(p,f); + if (dumper == NULL) + return (NULL); + + if (sf_ng_write_header(dumper->f, linktype, p->tzoff, p->snapshot) == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s", + fname, pcap_strerror(errno)); + pcap_dump_close(dumper); + return (NULL); + } + fflush(dumper->f); + p->shb_added = 1; + dumper->shb_added = 1; + + return (dumper); +} + +pcap_dumper_t * +pcap_ng_dump_open(pcap_t *p, const char *fname) +{ + FILE *f; + int linktype; + + /* + * If this pcap_t hasn't been activated, it doesn't have a + * link-layer type, so we can't use it. + */ + if (!p->activated) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "%s: not-yet-activated pcap_t passed to pcap_ng_dump_open", + fname); + return (NULL); + } + + if (fname[0] == '-' && fname[1] == '\0') { + f = stdout; + fname = "standard output"; + } else { + f = fopen(fname, "wb"); + if (f == NULL) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", + fname, pcap_strerror(errno)); + return (NULL); + } + } + + /* + * Make sure a section header will be added and that + * any information to a previous section gets cleared. + */ + pcap_ng_init_section_info(p); + + /* + * When using the block based API, the section header and + * interface description blocks are given by the caller + */ + if (p->linktype != DLT_PKTAP && p->linktype != DLT_PCAPNG) { + linktype = dlt_to_linktype(p->linktype); + if (linktype == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "%s: link-layer type %d isn't supported in savefiles", + fname, p->linktype); + if (f != stdout) + fclose(f); + return (NULL); + } + linktype |= p->linktype_ext; + + return (pcap_ng_setup_dump(p, linktype, f, fname)); + } else { + return (pcap_alloc_dumper(p, f)); + } +} + +pcap_dumper_t * +pcap_ng_dump_fopen(pcap_t *p, FILE *f) +{ + int linktype; + + linktype = dlt_to_linktype(p->linktype); + if (linktype == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "stream: link-layer type %d isn't supported in savefiles", + p->linktype); + return (NULL); + } + linktype |= p->linktype_ext; + + return (pcap_ng_setup_dump(p, linktype, f, "stream")); +} + +void +pcap_ng_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) +{ + FILE *f; + uint64_t ts; + struct pcapng_block_header bh; + struct pcapng_enhanced_packet_fields epb; + struct pcapng_block_trailer bt; + unsigned char packetpad = 0; + size_t len = sizeof(bh) + sizeof(epb) + h->caplen + sizeof(bt); + struct pcapng_option_header ohcomm; + struct pcapng_option_header oht; + size_t commlen = 0; + unsigned char commpad = 0; + + if (h->comment[0]) { + /* Comment option */ + commlen = strlen(h->comment); + ohcomm.option_code = PCAPNG_OPT_COMMENT; + ohcomm.option_length = commlen; + len += sizeof(ohcomm) + commlen; + if (commlen % 4 != 0) { + commpad = 4 - (commlen % 4); + len += commpad; + } + /* Option terminator */ + oht.option_code = PCAPNG_OPT_ENDOFOPT; + oht.option_length = 0; + len += sizeof(oht); + } + + if (h->caplen % 4 != 0) { + packetpad = 4 - (h->caplen % 4); + len += packetpad; + } + + bh.block_type = PCAPNG_BT_EPB; + bh.total_length = (bpf_u_int32)len; + + epb.caplen = h->caplen; + epb.interface_id = 0; + epb.len = h->len; + /* Microsecond resolution */ + ts = h->ts.tv_sec * 1000000 + h->ts.tv_usec; + epb.timestamp_high = ts >> 32; + epb.timestamp_low = ts & 0xffffffff; + + bt.total_length = (bpf_u_int32)len; + + f = ((pcap_dumper_t *)user)->f; + /* XXX we should check the return status */ + /* Header */ + (void)fwrite(&bh, sizeof(bh), 1, f); + (void)fwrite(&epb, sizeof(epb), 1, f); + /* Packet */ + (void)fwrite(sp, h->caplen, 1, f); + if (packetpad) + (void)fseek(f, packetpad, SEEK_CUR); + /* Options */ + if (h->comment[0]) { + (void)fwrite(&ohcomm, sizeof(ohcomm), 1, f); + (void)fwrite(h->comment, commlen, 1, f); + if (commpad) + (void)fseek(f, commpad, SEEK_CUR); + (void)fwrite(&oht, sizeof(oht), 1, f); + } + (void)fwrite(&bt, sizeof(bt), 1, f); +} + +void +pcap_ng_dump_close(pcap_dumper_t *p) +{ + /* + * XXX we could add an interface statistics block at the end + * of the file. + */ + return pcap_dump_close(p); +} + +#endif /* __APPLE__ */ + |