/* pcapng.c * * $Id$ * * Wiretap Library * Copyright (c) 1998 by Gilbert Ramirez * * File format support for pcap-ng file format * Copyright (c) 2007 by Ulf Lamping * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* File format reference: * http://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html * Related Wiki page: * http://wiki.wireshark.org/Development/PcapNg */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "wtap-int.h" #include "file_wrappers.h" #include "buffer.h" #include "libpcap.h" #include "pcap-common.h" #include "pcap-encap.h" #include "pcapng.h" #if 0 #define pcapng_debug0(str) g_warning(str) #define pcapng_debug1(str,p1) g_warning(str,p1) #define pcapng_debug2(str,p1,p2) g_warning(str,p1,p2) #define pcapng_debug3(str,p1,p2,p3) g_warning(str,p1,p2,p3) #else #define pcapng_debug0(str) #define pcapng_debug1(str,p1) #define pcapng_debug2(str,p1,p2) #define pcapng_debug3(str,p1,p2,p3) #endif static gboolean pcapng_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset); static gboolean pcapng_seek_read(wtap *wth, gint64 seek_off, union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err, gchar **err_info); static void pcapng_close(wtap *wth); /* pcapng: common block header for every block type */ typedef struct pcapng_block_header_s { guint32 block_type; guint32 block_total_length; /* x bytes block_body */ /* guint32 block_total_length */ } pcapng_block_header_t; /* pcapng: section header block */ typedef struct pcapng_section_header_block_s { /* pcapng_block_header_t */ guint32 magic; guint16 version_major; guint16 version_minor; guint64 section_length; /* might be -1 for unknown */ /* ... Options ... */ } pcapng_section_header_block_t; /* pcapng: interface description block */ typedef struct pcapng_interface_description_block_s { guint16 linktype; guint16 reserved; guint32 snaplen; /* ... Options ... */ } pcapng_interface_description_block_t; /* pcapng: packet block (obsolete) */ typedef struct pcapng_packet_block_s { guint16 interface_id; guint16 drops_count; guint32 timestamp_high; guint32 timestamp_low; guint32 captured_len; guint32 packet_len; /* ... Packet Data ... */ /* ... Padding ... */ /* ... Options ... */ } pcapng_packet_block_t; /* pcapng: enhanced packet block */ typedef struct pcapng_enhanced_packet_block_s { guint32 interface_id; guint32 timestamp_high; guint32 timestamp_low; guint32 captured_len; guint32 packet_len; /* ... Packet Data ... */ /* ... Padding ... */ /* ... Options ... */ } pcapng_enhanced_packet_block_t; /* pcapng: simple packet block */ typedef struct pcapng_simple_packet_block_s { guint32 packet_len; /* ... Packet Data ... */ /* ... Padding ... */ } pcapng_simple_packet_block_t; /* pcapng: interface statistics block */ typedef struct pcapng_interface_statistics_block_s { guint32 interface_id; guint32 timestamp_high; guint32 timestamp_low; /* ... Options ... */ } pcapng_interface_statistics_block_t; /* pcapng: common option header for every option type */ typedef struct pcapng_option_header_s { guint16 option_code; guint16 option_length; /* ... x bytes Option Body ... */ /* ... Padding ... */ } pcapng_option_header_t; /* Block types */ #define BLOCK_TYPE_IDB 0x00000001 /* Interface Description Block */ #define BLOCK_TYPE_PB 0x00000002 /* Packet Block (obsolete) */ #define BLOCK_TYPE_SPB 0x00000003 /* Simple Packet Block */ #define BLOCK_TYPE_NRB 0x00000004 /* Name Resolution Block */ #define BLOCK_TYPE_ISB 0x00000005 /* Interface Statistics Block */ #define BLOCK_TYPE_EPB 0x00000006 /* Enhanced Packet Block */ #define BLOCK_TYPE_SHB 0x0A0D0D0A /* Section Header Block */ /* Capture section */ typedef struct wtapng_section_s { /* mandatory */ guint64 section_length; /* options */ gchar *opt_comment; /* NULL if not available */ gchar *shb_hardware; /* NULL if not available */ gchar *shb_os; /* NULL if not available */ gchar *shb_user_appl; /* NULL if not available */ } wtapng_section_t; /* Interface Description */ typedef struct wtapng_if_descr_s { /* mandatory */ guint16 link_type; guint32 snap_len; /* options */ gchar *opt_comment; /* NULL if not available */ gchar *if_name; /* NULL if not available */ gchar *if_description;/* NULL if not available */ /* XXX: if_IPv4addr */ /* XXX: if_IPv6addr */ /* XXX: if_MACaddr */ /* XXX: if_EUIaddr */ guint64 if_speed; /* 0xFFFFFFFF if unknown */ guint8 if_tsresol; /* default is 6 for microsecond resolution */ gchar *if_filter; /* NULL if not available */ gchar *if_os; /* NULL if not available */ gint8 if_fcslen; /* -1 if unknown or changes between packets */ /* XXX: guint64 if_tsoffset; */ } wtapng_if_descr_t; /* Packets */ typedef struct wtapng_packet_s { /* mandatory */ guint32 ts_high; /* seconds since 1.1.1970 */ guint32 ts_low; /* fraction of seconds, depends on if_tsresol */ guint32 cap_len; /* data length in the file */ guint32 packet_len; /* data length on the wire */ guint32 interface_id; /* identifier of the interface. */ guint16 drops_count; /* drops count, only valid for packet block */ /* 0xffff if information no available */ /* options */ gchar *opt_comment; /* NULL if not available */ guint64 drop_count; guint32 pack_flags; /* XXX - 0 for now (any value for "we don't have it"?) */ /* pack_hash */ guint32 pseudo_header_len; int wtap_encap; /* XXX - put the packet data / pseudo_header here as well? */ } wtapng_packet_t; /* Simple Packets */ typedef struct wtapng_simple_packet_s { /* mandatory */ guint32 cap_len; /* data length in the file */ guint32 packet_len; /* data length on the wire */ guint32 pseudo_header_len; int wtap_encap; /* XXX - put the packet data / pseudo_header here as well? */ } wtapng_simple_packet_t; /* Name Resolution */ typedef struct wtapng_name_res_s { /* options */ gchar *opt_comment; /* NULL if not available */ /* XXX */ } wtapng_name_res_t; /* Interface Statistics */ typedef struct wtapng_if_stats_s { /* mandatory */ guint64 interface_id; guint32 ts_high; guint32 ts_low; /* options */ gchar *opt_comment; /* NULL if not available */ /* XXX */ /*guint32 isb_starttime_high;*/ /*guint32 isb_starttime_low;*/ /*guint32 isb_endtime_high;*/ /*guint32 isb_endtime_low;*/ guint64 isb_ifrecv; guint64 isb_ifdrop; /*guint64 isb_filteraccept;*/ /*guint64 isb_osdrop;*/ /*guint64 isb_usrdeliv;*/ } wtapng_if_stats_t; typedef struct wtapng_block_s { guint32 type; /* block_type as defined by pcapng */ union { wtapng_section_t section; wtapng_if_descr_t if_descr; wtapng_packet_t packet; wtapng_simple_packet_t simple_packet; wtapng_name_res_t name_res; wtapng_if_stats_t if_stats; } data; /* * XXX - currently don't know how to handle these! * * For one thing, when we're reading a block, they must be * writable, i.e. not const, so that we can read into them, * but, when we're writing a block, they can be const, and, * in fact, they sometimes point to const values. */ const union wtap_pseudo_header *pseudo_header; struct wtap_pkthdr *packet_header; const guchar *frame_buffer; int *file_encap; } wtapng_block_t; typedef struct interface_data_s { int wtap_encap; guint64 time_units_per_second; } interface_data_t; typedef struct { gboolean byte_swapped; guint16 version_major; guint16 version_minor; gint8 if_fcslen; GArray *interface_data; guint number_of_interfaces; } pcapng_t; static int pcapng_get_encap(gint id, pcapng_t *pn) { interface_data_t int_data; if ((id >= 0) && ((guint)id < pn->number_of_interfaces)) { int_data = g_array_index(pn->interface_data, interface_data_t, id); return int_data.wtap_encap; } else { return WTAP_ERR_UNSUPPORTED_ENCAP; } } static int pcapng_read_option(FILE_T fh, pcapng_t *pn, pcapng_option_header_t *oh, char *content, int len, int *err, gchar **err_info _U_) { int bytes_read; int block_read; guint64 file_offset64; /* read option header */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(oh, 1, sizeof (*oh), fh); if (bytes_read != sizeof (*oh)) { pcapng_debug0("pcapng_read_option: failed to read option"); *err = file_error(fh); if (*err != 0) return -1; return 0; } block_read = sizeof (*oh); if(pn->byte_swapped) { oh->option_code = BSWAP16(oh->option_code); oh->option_length = BSWAP16(oh->option_length); } /* sanity check: option length */ if (oh->option_length > len) { pcapng_debug2("pcapng_read_option: option_length %u larger than buffer (%u)", oh->option_length, len); return 0; } /* read option content */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(content, 1, oh->option_length, fh); if (bytes_read != oh->option_length) { pcapng_debug1("pcapng_read_if_descr_block: failed to read content of option %u", oh->option_code); *err = file_error(fh); if (*err != 0) return -1; return 0; } block_read += oh->option_length; /* jump over potential padding bytes at end of option */ if( (oh->option_length % 4) != 0) { file_offset64 = file_seek(fh, 4 - (oh->option_length % 4), SEEK_CUR, err); if (file_offset64 <= 0) { if (*err != 0) return -1; return 0; } block_read += 4 - (oh->option_length % 4); } return block_read; } static int pcapng_read_section_header_block(FILE_T fh, gboolean first_block, pcapng_block_header_t *bh, pcapng_t *pn, wtapng_block_t *wblock, int *err, gchar **err_info) { int bytes_read; int block_read; int to_read; pcapng_section_header_block_t shb; pcapng_option_header_t oh; char option_content[100]; /* XXX - size might need to be increased, if we see longer options */ /* read block content */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(&shb, 1, sizeof shb, fh); if (bytes_read != sizeof shb) { *err = file_error(fh); if (*err == 0) { if (first_block) { /* * We're reading this as part of an open, * and this block is too short to be * an SHB, so the file is too short * to be a pcap-ng file. */ return 0; } /* * Otherwise, just report this as an error. */ *err = WTAP_ERR_SHORT_READ; } return -1; } block_read = bytes_read; /* is the magic number one we expect? */ switch(shb.magic) { case(0x1A2B3C4D): /* this seems pcapng with correct byte order */ pn->byte_swapped = FALSE; pn->version_major = shb.version_major; pn->version_minor = shb.version_minor; pcapng_debug3("pcapng_read_section_header_block: SHB (little endian) V%u.%u, len %u", pn->version_major, pn->version_minor, bh->block_total_length); break; case(0x4D3C2B1A): /* this seems pcapng with swapped byte order */ pn->byte_swapped = TRUE; pn->version_major = BSWAP16(shb.version_major); pn->version_minor = BSWAP16(shb.version_minor); /* tweak the block length to meet current swapping that we know now */ bh->block_total_length = BSWAP32(bh->block_total_length); pcapng_debug3("pcapng_read_section_header_block: SHB (big endian) V%u.%u, len %u", pn->version_major, pn->version_minor, bh->block_total_length); break; default: /* Not a "pcapng" magic number we know about. */ if (first_block) { /* Not a pcap-ng file. */ return 0; } /* A bad block */ *err = WTAP_ERR_BAD_RECORD; *err_info = g_strdup_printf("pcapng_read_section_header_block: unknown byte-order magic number 0x%08x", shb.magic); return 0; } /* OK, at this point we assume it's a pcap-ng file. */ /* we currently only understand SHB V1.0 */ if (pn->version_major != 1 || pn->version_minor > 0) { *err = WTAP_ERR_UNSUPPORTED; *err_info = g_strdup_printf("pcapng_read_section_header_block: unknown SHB version %u.%u", pn->version_major, pn->version_minor); return -1; } /* 64bit section_length (currently unused) */ if (pn->byte_swapped) { wblock->data.section.section_length = BSWAP64(shb.section_length); } else { wblock->data.section.section_length = shb.section_length; } /* Option defaults */ wblock->data.section.opt_comment = NULL; wblock->data.section.shb_hardware = NULL; wblock->data.section.shb_os = NULL; wblock->data.section.shb_user_appl = NULL; /* Options */ errno = WTAP_ERR_CANT_READ; to_read = bh->block_total_length - (int)sizeof(pcapng_block_header_t) - (int)sizeof (pcapng_section_header_block_t) - (int)sizeof(bh->block_total_length); while(to_read > 0) { /* read option */ bytes_read = pcapng_read_option(fh, pn, &oh, option_content, sizeof(option_content), err, err_info); if (bytes_read <= 0) { pcapng_debug0("pcapng_read_section_header_block: failed to read option"); return bytes_read; } block_read += bytes_read; to_read -= bytes_read; /* handle option content */ switch(oh.option_code) { case(0): /* opt_endofopt */ if(to_read != 0) { pcapng_debug1("pcapng_read_section_header_block: %u bytes after opt_endofopt", to_read); } /* padding should be ok here, just get out of this */ to_read = 0; break; case(1): /* opt_comment */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.opt_comment = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_section_header_block: opt_comment %s", wblock->data.section.opt_comment); } else { pcapng_debug1("pcapng_read_section_header_block: opt_comment length %u seems strange", oh.option_length); } break; case(2): /* shb_hardware */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.shb_hardware = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_section_header_block: shb_hardware %s", wblock->data.section.shb_hardware); } else { pcapng_debug1("pcapng_read_section_header_block: shb_hardware length %u seems strange", oh.option_length); } break; case(3): /* shb_os */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.shb_os = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_section_header_block: shb_os %s", wblock->data.section.shb_os); } else { pcapng_debug1("pcapng_read_section_header_block: shb_os length %u seems strange", oh.option_length); } break; case(4): /* shb_userappl */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.shb_user_appl = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_section_header_block: shb_userappl %s", wblock->data.section.shb_user_appl); } else { pcapng_debug1("pcapng_read_section_header_block: shb_userappl length %u seems strange", oh.option_length); } break; default: pcapng_debug2("pcapng_read_section_header_block: unknown option %u - ignoring %u bytes", oh.option_code, oh.option_length); } } if (pn->interface_data != NULL) { pcapng_debug0("pcapng_read_section_header_block: Multiple section header blocks!"); g_array_free(pn->interface_data, TRUE); pn->interface_data = NULL; *err = WTAP_ERR_BAD_RECORD; *err_info = g_strdup_printf("pcapng: multiple section header blocks not supported."); return 0; } pn->interface_data = g_array_new(FALSE, FALSE, sizeof(interface_data_t)); pn->number_of_interfaces = 0; return block_read; } /* "Interface Description Block" */ static int pcapng_read_if_descr_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wtapng_block_t *wblock, int *err, gchar **err_info _U_) { guint64 time_units_per_second; int bytes_read; int block_read; int to_read; pcapng_interface_description_block_t idb; pcapng_option_header_t oh; interface_data_t int_data; gint encap; char option_content[100]; /* XXX - size might need to be increased, if we see longer options */ time_units_per_second = 1000000; /* default */ /* read block content */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(&idb, 1, sizeof idb, fh); if (bytes_read != sizeof idb) { pcapng_debug0("pcapng_read_if_descr_block: failed to read IDB"); *err = file_error(fh); if (*err != 0) return -1; return 0; } block_read = bytes_read; /* mandatory values */ if (pn->byte_swapped) { wblock->data.if_descr.link_type = BSWAP16(idb.linktype); wblock->data.if_descr.snap_len = BSWAP32(idb.snaplen); } else { wblock->data.if_descr.link_type = idb.linktype; wblock->data.if_descr.snap_len = idb.snaplen; } pcapng_debug3("pcapng_read_if_descr_block: IDB link_type %u (%s), snap %u", wblock->data.if_descr.link_type, wtap_encap_string(wtap_pcap_encap_to_wtap_encap(wblock->data.if_descr.link_type)), wblock->data.if_descr.snap_len); if (wblock->data.if_descr.snap_len > WTAP_MAX_PACKET_SIZE) { /* This is unrealisitic, but text2pcap currently uses 102400. * We do not use this value, maybe we should check the * snap_len of the packets against it. For now, only warn. */ pcapng_debug1("pcapng_read_if_descr_block: snapshot length %u unrealistic.", wblock->data.if_descr.snap_len); /*wblock->data.if_descr.snap_len = WTAP_MAX_PACKET_SIZE;*/ } /* Option defaults */ wblock->data.if_descr.opt_comment = NULL; wblock->data.if_descr.if_name = NULL; wblock->data.if_descr.if_description = NULL; /* XXX: if_IPv4addr */ /* XXX: if_IPv6addr */ /* XXX: if_MACaddr */ /* XXX: if_EUIaddr */ wblock->data.if_descr.if_speed = 0xFFFFFFFF; /* "unknown" */ wblock->data.if_descr.if_tsresol = 6; /* default is 6 for microsecond resolution */ wblock->data.if_descr.if_filter = NULL; wblock->data.if_descr.if_os = NULL; wblock->data.if_descr.if_fcslen = -1; /* unknown or changes between packets */ /* XXX: guint64 if_tsoffset; */ /* Options */ errno = WTAP_ERR_CANT_READ; to_read = bh->block_total_length - (int)sizeof(pcapng_block_header_t) - (int)sizeof (pcapng_interface_description_block_t) - (int)sizeof(bh->block_total_length); while (to_read > 0) { /* read option */ bytes_read = pcapng_read_option(fh, pn, &oh, option_content, sizeof(option_content), err, err_info); if (bytes_read <= 0) { pcapng_debug0("pcapng_read_if_descr_block: failed to read option"); return bytes_read; } block_read += bytes_read; to_read -= bytes_read; /* handle option content */ switch(oh.option_code) { case(0): /* opt_endofopt */ if(to_read != 0) { pcapng_debug1("pcapng_read_if_descr_block: %u bytes after opt_endofopt", to_read); } /* padding should be ok here, just get out of this */ to_read = 0; break; case(1): /* opt_comment */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.opt_comment = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_if_descr_block: opt_comment %s", wblock->data.section.opt_comment); } else { pcapng_debug1("pcapng_read_if_descr_block: opt_comment length %u seems strange", oh.option_length); } break; case(2): /* if_name */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.if_descr.if_name = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_if_descr_block: if_name %s", wblock->data.if_descr.if_name); } else { pcapng_debug1("pcapng_read_if_descr_block: if_name length %u seems strange", oh.option_length); } break; case(3): /* if_description */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.if_descr.if_description = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_if_descr_block: if_description %s", wblock->data.if_descr.if_description); } else { pcapng_debug1("pcapng_read_if_descr_block: if_description length %u seems strange", oh.option_length); } break; case(8): /* if_speed */ if(oh.option_length == 8) { /* Don't cast a char[] into a guint64--the * char[] may not be aligned correctly. */ memcpy(&wblock->data.if_descr.if_speed, option_content, sizeof(guint64)); if(pn->byte_swapped) wblock->data.if_descr.if_speed = BSWAP64(wblock->data.if_descr.if_speed); pcapng_debug1("pcapng_read_if_descr_block: if_speed %" G_GINT64_MODIFIER "u (bps)", wblock->data.if_descr.if_speed); } else { pcapng_debug1("pcapng_read_if_descr_block: if_speed length %u not 8 as expected", oh.option_length); } break; case(9): /* if_tsresol */ if (oh.option_length == 1) { guint64 base; guint64 result; guint8 i, exponent; wblock->data.if_descr.if_tsresol = option_content[0]; if (wblock->data.if_descr.if_tsresol & 0x80) { base = 2; } else { base = 10; } exponent = (guint8)(wblock->data.if_descr.if_tsresol & 0x7f); if (((base == 2) && (exponent < 64)) || ((base == 10) && (exponent < 20))) { result = 1; for (i = 0; i < exponent; i++) { result *= base; } time_units_per_second = result; } else { time_units_per_second = G_MAXUINT64; } if (time_units_per_second > (((guint64)1) << 32)) { pcapng_debug0("pcapng_open: time conversion might be inaccurate"); } pcapng_debug1("pcapng_read_if_descr_block: if_tsresol %u", wblock->data.if_descr.if_tsresol); } else { pcapng_debug1("pcapng_read_if_descr_block: if_tsresol length %u not 1 as expected", oh.option_length); } break; case(11): /* if_filter */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.if_descr.if_filter = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_if_descr_block: if_filter %s", wblock->data.if_descr.if_filter); } else { pcapng_debug1("pcapng_read_if_descr_block: if_filter length %u seems strange", oh.option_length); } break; case(13): /* if_fcslen */ if(oh.option_length == 1) { wblock->data.if_descr.if_fcslen = option_content[0]; pn->if_fcslen = wblock->data.if_descr.if_fcslen; pcapng_debug1("pcapng_read_if_descr_block: if_fcslen %u", wblock->data.if_descr.if_fcslen); /* XXX - add sanity check */ } else { pcapng_debug1("pcapng_read_if_descr_block: if_fcslen length %u not 1 as expected", oh.option_length); } break; default: pcapng_debug2("pcapng_read_if_descr_block: unknown option %u - ignoring %u bytes", oh.option_code, oh.option_length); } } encap = wtap_pcap_encap_to_wtap_encap(wblock->data.if_descr.link_type); if (*wblock->file_encap == WTAP_ENCAP_UNKNOWN) { *wblock->file_encap = encap; } else { if (*wblock->file_encap != encap) { *wblock->file_encap = WTAP_ENCAP_PER_PACKET; } } int_data.wtap_encap = encap; int_data.time_units_per_second = time_units_per_second; g_array_append_val(pn->interface_data, int_data); pn->number_of_interfaces++; return block_read; } static int pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wtapng_block_t *wblock, int *err, gchar **err_info _U_, gboolean enhanced) { int bytes_read; int block_read; int to_read; guint64 file_offset64; pcapng_enhanced_packet_block_t epb; pcapng_packet_block_t pb; guint32 block_total_length; pcapng_option_header_t oh; gint wtap_encap; int pseudo_header_len; char option_content[100]; /* XXX - size might need to be increased, if we see longer options */ /* "(Enhanced) Packet Block" read fixed part */ errno = WTAP_ERR_CANT_READ; if (enhanced) { bytes_read = file_read(&epb, 1, sizeof epb, fh); if (bytes_read != sizeof epb) { pcapng_debug0("pcapng_read_packet_block: failed to read packet data"); *err = file_error(fh); return 0; } block_read = bytes_read; if (pn->byte_swapped) { wblock->data.packet.interface_id = BSWAP32(epb.interface_id); wblock->data.packet.drops_count = -1; /* invalid */ wblock->data.packet.ts_high = BSWAP32(epb.timestamp_high); wblock->data.packet.ts_low = BSWAP32(epb.timestamp_low); wblock->data.packet.cap_len = BSWAP32(epb.captured_len); wblock->data.packet.packet_len = BSWAP32(epb.packet_len); } else { wblock->data.packet.interface_id = epb.interface_id; wblock->data.packet.drops_count = -1; /* invalid */ wblock->data.packet.ts_high = epb.timestamp_high; wblock->data.packet.ts_low = epb.timestamp_low; wblock->data.packet.cap_len = epb.captured_len; wblock->data.packet.packet_len = epb.packet_len; } } else { bytes_read = file_read(&pb, 1, sizeof pb, fh); if (bytes_read != sizeof pb) { pcapng_debug0("pcapng_read_packet_block: failed to read packet data"); *err = file_error(fh); return 0; } block_read = bytes_read; if (pn->byte_swapped) { wblock->data.packet.interface_id = BSWAP16(pb.interface_id); wblock->data.packet.drops_count = BSWAP16(pb.drops_count); wblock->data.packet.ts_high = BSWAP32(pb.timestamp_high); wblock->data.packet.ts_low = BSWAP32(pb.timestamp_low); wblock->data.packet.cap_len = BSWAP32(pb.captured_len); wblock->data.packet.packet_len = BSWAP32(pb.packet_len); } else { wblock->data.packet.interface_id = pb.interface_id; wblock->data.packet.drops_count = pb.drops_count; wblock->data.packet.ts_high = pb.timestamp_high; wblock->data.packet.ts_low = pb.timestamp_low; wblock->data.packet.cap_len = pb.captured_len; wblock->data.packet.packet_len = pb.packet_len; } } if (wblock->data.packet.cap_len > wblock->data.packet.packet_len) { pcapng_debug2("pcapng_read_packet_block:cap_len %d is larger than packet_len %u.", wblock->data.packet.cap_len, wblock->data.packet.packet_len); *err = WTAP_ERR_BAD_RECORD; return 0; } if (wblock->data.packet.cap_len > WTAP_MAX_PACKET_SIZE) { pcapng_debug2("pcapng_read_packet_block:cap_len %d is larger than WTAP_MAX_PACKET_SIZE %u.", wblock->data.packet.cap_len, WTAP_MAX_PACKET_SIZE); *err = WTAP_ERR_BAD_RECORD; return 0; } pcapng_debug3("pcapng_read_packet_block: packet data: packet_len %u captured_len %u interface_id %u", wblock->data.packet.packet_len, wblock->data.packet.cap_len, wblock->data.packet.interface_id); wtap_encap = pcapng_get_encap(wblock->data.packet.interface_id, pn); pcapng_debug3("pcapng_read_packet_block: encapsulation = %d (%s), pseudo header size = %d.", wtap_encap, wtap_encap_string(wtap_encap), pcap_get_phdr_size(wtap_encap, wblock->pseudo_header)); memset((void *)wblock->pseudo_header, 0, sizeof(union wtap_pseudo_header)); pseudo_header_len = pcap_process_pseudo_header(fh, WTAP_FILE_PCAPNG, wtap_encap, pn->byte_swapped, wblock->data.packet.cap_len, TRUE, wblock->packet_header, (union wtap_pseudo_header *)wblock->pseudo_header, err, err_info); if (pseudo_header_len < 0) { return 0; } wblock->data.packet.pseudo_header_len = (guint32)pseudo_header_len; block_read += pseudo_header_len; if (pseudo_header_len != pcap_get_phdr_size(wtap_encap, wblock->pseudo_header)) { pcapng_debug1("pcapng_read_packet_block: Could only read %d bytes for pseudo header.", pseudo_header_len); } /* "(Enhanced) Packet Block" read capture data */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read((guchar *) (wblock->frame_buffer), 1, wblock->data.packet.cap_len - pseudo_header_len, fh); if (bytes_read != (int) (wblock->data.packet.cap_len - pseudo_header_len)) { *err = file_error(fh); pcapng_debug1("pcapng_read_packet_block: couldn't read %u bytes of captured data", wblock->data.packet.cap_len - pseudo_header_len); if (*err == 0) *err = WTAP_ERR_SHORT_READ; return 0; } block_read += bytes_read; /* jump over potential padding bytes at end of the packet data */ if( (wblock->data.packet.cap_len % 4) != 0) { file_offset64 = file_seek(fh, 4 - (wblock->data.packet.cap_len % 4), SEEK_CUR, err); if (file_offset64 <= 0) { if (*err != 0) return -1; return 0; } block_read += 4 - (wblock->data.packet.cap_len % 4); } /* add padding bytes to "block total length" */ /* (the "block total length" of some example files don't contain the packet data padding bytes!) */ if (bh->block_total_length % 4) { block_total_length = bh->block_total_length + 4 - (bh->block_total_length % 4); } else { block_total_length = bh->block_total_length; } /* Option defaults */ wblock->data.packet.opt_comment = NULL; wblock->data.packet.drop_count = -1; wblock->data.packet.pack_flags = 0; /* XXX - is 0 ok to signal "not used"? */ /* Options */ errno = WTAP_ERR_CANT_READ; to_read = block_total_length - (int)sizeof(pcapng_block_header_t) - block_read /* fixed and variable part, including padding */ - (int)sizeof(bh->block_total_length); while(to_read > 0) { /* read option */ bytes_read = pcapng_read_option(fh, pn, &oh, option_content, sizeof(option_content), err, err_info); if (bytes_read <= 0) { pcapng_debug0("pcapng_read_packet_block: failed to read option"); return bytes_read; } block_read += bytes_read; to_read -= bytes_read; /* handle option content */ switch(oh.option_code) { case(0): /* opt_endofopt */ if(to_read != 0) { pcapng_debug1("pcapng_read_packet_block: %u bytes after opt_endofopt", to_read); } /* padding should be ok here, just get out of this */ to_read = 0; break; case(1): /* opt_comment */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.opt_comment = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_packet_block: opt_comment %s", wblock->data.section.opt_comment); } else { pcapng_debug1("pcapng_read_packet_block: opt_comment length %u seems strange", oh.option_length); } break; case(2): /* pack_flags / epb_flags */ if(oh.option_length == 4) { /* Don't cast a char[] into a guint32--the * char[] may not be aligned correctly. */ memcpy(&wblock->data.packet.pack_flags, option_content, sizeof(guint32)); if(pn->byte_swapped) wblock->data.packet.pack_flags = BSWAP32(wblock->data.packet.pack_flags); pcapng_debug1("pcapng_read_if_descr_block: pack_flags %u (ignored)", wblock->data.packet.pack_flags); } else { pcapng_debug1("pcapng_read_if_descr_block: pack_flags length %u not 4 as expected", oh.option_length); } break; default: pcapng_debug2("pcapng_read_packet_block: unknown option %u - ignoring %u bytes", oh.option_code, oh.option_length); } } return block_read; } static int pcapng_read_simple_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wtapng_block_t *wblock, int *err, gchar **err_info _U_) { int bytes_read; int block_read; guint64 file_offset64; gint encap; int pseudo_header_len; pcapng_simple_packet_block_t spb; /* "Simple Packet Block" read fixed part */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(&spb, 1, sizeof spb, fh); if (bytes_read != sizeof spb) { pcapng_debug0("pcapng_read_simple_packet_block: failed to read packet data"); *err = file_error(fh); return 0; } block_read = bytes_read; if (pn->byte_swapped) { wblock->data.simple_packet.packet_len = BSWAP32(spb.packet_len); } else { wblock->data.simple_packet.packet_len = spb.packet_len; } wblock->data.simple_packet.cap_len = bh->block_total_length - (guint32)sizeof(pcapng_simple_packet_block_t) - (guint32)sizeof(bh->block_total_length); if (wblock->data.simple_packet.cap_len > WTAP_MAX_PACKET_SIZE) { pcapng_debug2("pcapng_read_simple_packet_block:cap_len %d is larger than WTAP_MAX_PACKET_SIZE %u.", wblock->data.simple_packet.cap_len, WTAP_MAX_PACKET_SIZE); *err = WTAP_ERR_BAD_RECORD; return 0; } pcapng_debug1("pcapng_read_simple_packet_block: packet data: packet_len %u", wblock->data.simple_packet.packet_len); encap = pcapng_get_encap(0, pn); pcapng_debug1("pcapng_read_simple_packet_block: Need to read pseudo header of size %d", pcap_get_phdr_size(encap, wblock->pseudo_header)); memset((void *)wblock->pseudo_header, 0, sizeof(union wtap_pseudo_header)); pseudo_header_len = pcap_process_pseudo_header(fh, WTAP_FILE_PCAPNG, encap, pn->byte_swapped, wblock->data.simple_packet.cap_len, TRUE, wblock->packet_header, (union wtap_pseudo_header *)wblock->pseudo_header, err, err_info); if (pseudo_header_len < 0) { return 0; } wblock->data.simple_packet.pseudo_header_len = (guint32)pseudo_header_len; block_read += pseudo_header_len; if (pseudo_header_len != pcap_get_phdr_size(encap, wblock->pseudo_header)) { pcapng_debug1("pcapng_read_simple_packet_block: Could only read %d bytes for pseudo header.", pseudo_header_len); } /* XXX - implement other linktypes then Ethernet */ /* (or even better share the code with libpcap.c) */ /* Ethernet FCS length, might be overwritten by "per packet" options */ memset((void *)wblock->pseudo_header, 0, sizeof(union wtap_pseudo_header)); ((union wtap_pseudo_header *) wblock->pseudo_header)->eth.fcs_len = pn->if_fcslen; /* "Simple Packet Block" read capture data */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read((guchar *) (wblock->frame_buffer), 1, wblock->data.simple_packet.cap_len, fh); if (bytes_read != (int) wblock->data.simple_packet.cap_len) { *err = file_error(fh); pcapng_debug1("pcapng_read_simple_packet_block: couldn't read %u bytes of captured data", wblock->data.simple_packet.cap_len); if (*err == 0) *err = WTAP_ERR_SHORT_READ; return 0; } block_read += bytes_read; /* jump over potential padding bytes at end of the packet data */ if ((wblock->data.simple_packet.cap_len % 4) != 0) { file_offset64 = file_seek(fh, 4 - (wblock->data.simple_packet.cap_len % 4), SEEK_CUR, err); if (file_offset64 <= 0) { if (*err != 0) return -1; return 0; } block_read += 4 - (wblock->data.simple_packet.cap_len % 4); } return block_read; } static int pcapng_read_interface_statistics_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wtapng_block_t *wblock,int *err, gchar **err_info _U_) { int bytes_read; int block_read; int to_read; pcapng_interface_statistics_block_t isb; pcapng_option_header_t oh; char option_content[100]; /* XXX - size might need to be increased, if we see longer options */ /* "Interface Statistics Block" read fixed part */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(&isb, 1, sizeof isb, fh); if (bytes_read != sizeof isb) { pcapng_debug0("pcapng_read_interface_statistics_block: failed to read packet data"); *err = file_error(fh); return 0; } block_read = bytes_read; if(pn->byte_swapped) { wblock->data.if_stats.interface_id = BSWAP64(isb.interface_id); wblock->data.if_stats.ts_high = BSWAP32(isb.timestamp_high); wblock->data.if_stats.ts_low = BSWAP32(isb.timestamp_low); } else { wblock->data.if_stats.interface_id = isb.interface_id; wblock->data.if_stats.ts_high = isb.timestamp_high; wblock->data.if_stats.ts_low = isb.timestamp_low; } pcapng_debug1("pcapng_read_interface_statistics_block: interface_id %" G_GINT64_MODIFIER "u", wblock->data.if_stats.interface_id); /* Option defaults */ wblock->data.if_stats.opt_comment = NULL; wblock->data.if_stats.isb_ifrecv = -1; wblock->data.if_stats.isb_ifdrop = -1; /* Options */ errno = WTAP_ERR_CANT_READ; to_read = bh->block_total_length - sizeof(pcapng_block_header_t) - block_read /* fixed and variable part, including padding */ - sizeof(bh->block_total_length); while(to_read > 0) { /* read option */ bytes_read = pcapng_read_option(fh, pn, &oh, option_content, sizeof(option_content), err, err_info); if (bytes_read <= 0) { pcapng_debug0("pcapng_read_interface_statistics_block: failed to read option"); return bytes_read; } block_read += bytes_read; to_read -= bytes_read; /* handle option content */ switch(oh.option_code) { case(0): /* opt_endofopt */ if(to_read != 0) { pcapng_debug1("pcapng_read_interface_statistics_block: %u bytes after opt_endofopt", to_read); } /* padding should be ok here, just get out of this */ to_read = 0; break; case(1): /* opt_comment */ if(oh.option_length > 0 && oh.option_length < sizeof(option_content)) { wblock->data.section.opt_comment = g_strndup(option_content, sizeof(option_content)); pcapng_debug1("pcapng_read_interface_statistics_block: opt_comment %s", wblock->data.section.opt_comment); } else { pcapng_debug1("pcapng_read_interface_statistics_block: opt_comment length %u seems strange", oh.option_length); } break; case(4): /* isb_ifrecv */ if(oh.option_length == 8) { /* Don't cast a char[] into a guint32--the * char[] may not be aligned correctly. */ memcpy(&wblock->data.if_stats.isb_ifrecv, option_content, sizeof(guint64)); if(pn->byte_swapped) wblock->data.if_stats.isb_ifrecv = BSWAP64(wblock->data.if_stats.isb_ifrecv); pcapng_debug1("pcapng_read_interface_statistics_block: isb_ifrecv %" G_GINT64_MODIFIER "u", wblock->data.if_stats.isb_ifrecv); } else { pcapng_debug1("pcapng_read_interface_statistics_block: isb_ifrecv length %u not 8 as expected", oh.option_length); } break; case(5): /* isb_ifdrop */ if(oh.option_length == 8) { /* Don't cast a char[] into a guint32--the * char[] may not be aligned correctly. */ memcpy(&wblock->data.if_stats.isb_ifdrop, option_content, sizeof(guint64)); if(pn->byte_swapped) wblock->data.if_stats.isb_ifdrop = BSWAP64(wblock->data.if_stats.isb_ifdrop); pcapng_debug1("pcapng_read_interface_statistics_block: isb_ifdrop %" G_GINT64_MODIFIER "u", wblock->data.if_stats.isb_ifdrop); } else { pcapng_debug1("pcapng_read_interface_statistics_block: isb_ifdrop length %u not 8 as expected", oh.option_length); } break; default: pcapng_debug2("pcapng_read_interface_statistics_block: unknown option %u - ignoring %u bytes", oh.option_code, oh.option_length); } } return block_read; } static int pcapng_read_unknown_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn _U_, wtapng_block_t *wblock _U_,int *err, gchar **err_info _U_) { int block_read; guint64 file_offset64; guint32 block_total_length; /* add padding bytes to "block total length" */ /* (the "block total length" of some example files don't contain any padding bytes!) */ if (bh->block_total_length % 4) { block_total_length = bh->block_total_length + 4 - (bh->block_total_length % 4); } else { block_total_length = bh->block_total_length; } block_read = block_total_length - (guint32)sizeof(pcapng_block_header_t) - (guint32)sizeof(bh->block_total_length); /* jump over this unknown block */ file_offset64 = file_seek(fh, block_read, SEEK_CUR, err); if (file_offset64 <= 0) { if (*err != 0) return -1; return 0; } return block_read; } static int pcapng_read_block(FILE_T fh, gboolean first_block, pcapng_t *pn, wtapng_block_t *wblock, int *err, gchar **err_info) { int block_read; int bytes_read; pcapng_block_header_t bh; guint32 block_total_length; /* Try to read the (next) block header */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(&bh, 1, sizeof bh, fh); if (bytes_read != sizeof bh) { pcapng_debug0("pcapng_read_block: end of file or error"); *err = file_error(fh); if (*err != 0) return -1; return 0; } block_read = bytes_read; if (pn->byte_swapped) { bh.block_type = BSWAP32(bh.block_type); bh.block_total_length = BSWAP32(bh.block_total_length); } wblock->type = bh.block_type; pcapng_debug1("pcapng_read_block: block_type 0x%x", bh.block_type); if (first_block) { /* * This is being read in by pcapng_open(), so this block * must be an SHB. If it's not, this is not a pcap-ng * file. * * XXX - check for various forms of Windows <-> UN*X * mangling, and suggest that the file might be a * pcap-ng file that was damaged in transit? */ if (bh.block_type != BLOCK_TYPE_SHB) return 0; /* not a pcap-ng file */ } switch(bh.block_type) { case(BLOCK_TYPE_SHB): bytes_read = pcapng_read_section_header_block(fh, first_block, &bh, pn, wblock, err, err_info); break; case(BLOCK_TYPE_IDB): bytes_read = pcapng_read_if_descr_block(fh, &bh, pn, wblock, err, err_info); break; case(BLOCK_TYPE_PB): bytes_read = pcapng_read_packet_block(fh, &bh, pn, wblock, err, err_info, FALSE); break; case(BLOCK_TYPE_SPB): bytes_read = pcapng_read_simple_packet_block(fh, &bh, pn, wblock, err, err_info); break; case(BLOCK_TYPE_EPB): bytes_read = pcapng_read_packet_block(fh, &bh, pn, wblock, err, err_info, TRUE); break; case(BLOCK_TYPE_ISB): bytes_read = pcapng_read_interface_statistics_block(fh, &bh, pn, wblock, err, err_info); break; default: pcapng_debug2("pcapng_read_block: Unknown block_type: 0x%x (block ignored), block total length %d", bh.block_type, bh.block_total_length); bytes_read = pcapng_read_unknown_block(fh, &bh, pn, wblock, err, err_info); } if (bytes_read <= 0) { return bytes_read; } block_read += bytes_read; /* sanity check: first and second block lengths must match */ errno = WTAP_ERR_CANT_READ; bytes_read = file_read(&block_total_length, 1, sizeof block_total_length, fh); if (bytes_read != sizeof block_total_length) { pcapng_debug0("pcapng_read_block: couldn't read second block length"); *err = file_error(fh); if (*err == 0) *err = WTAP_ERR_SHORT_READ; return -1; } block_read += bytes_read; if (pn->byte_swapped) block_total_length = BSWAP32(block_total_length); if (!(block_total_length == bh.block_total_length)) { *err = WTAP_ERR_BAD_RECORD; *err_info = g_strdup_printf("pcapng_read_block: total block lengths (first %u and second %u) don't match", bh.block_total_length, block_total_length); return -1; } return block_read; } /* classic wtap: open capture file */ int pcapng_open(wtap *wth, int *err, gchar **err_info) { int bytes_read; pcapng_t pn; wtapng_block_t wblock; pcapng_t *pcapng; /* we don't know the byte swapping of the file yet */ pn.byte_swapped = FALSE; pn.if_fcslen = -1; pn.version_major = -1; pn.version_minor = -1; pn.interface_data = NULL; pn.number_of_interfaces = 0; /* we don't expect any packet blocks yet */ wblock.frame_buffer = NULL; wblock.pseudo_header = NULL; wblock.packet_header = NULL; wblock.file_encap = &wth->file_encap; pcapng_debug0("pcapng_open: opening file"); /* read first block */ bytes_read = pcapng_read_block(wth->fh, TRUE, &pn, &wblock, err, err_info); if (bytes_read <= 0) { pcapng_debug0("pcapng_open: couldn't read first SHB"); *err = file_error(wth->fh); if (*err != 0) return -1; return 0; } wth->data_offset += bytes_read; /* first block must be a "Section Header Block" */ if (wblock.type != BLOCK_TYPE_SHB) { /* * XXX - check for damage from transferring a file * between Windows and UN*X as text rather than * binary data? */ pcapng_debug1("pcapng_open: first block type %u not SHB", wblock.type); return 0; } wth->file_encap = WTAP_ENCAP_UNKNOWN; wth->snapshot_length = 0; wth->tsprecision = WTAP_FILE_TSPREC_NSEC; pcapng = (pcapng_t *)g_malloc(sizeof(pcapng_t)); wth->priv = (void *)pcapng; *pcapng = pn; wth->subtype_read = pcapng_read; wth->subtype_seek_read = pcapng_seek_read; wth->subtype_close = pcapng_close; wth->file_type = WTAP_FILE_PCAPNG; return 1; } /* classic wtap: read packet */ static gboolean pcapng_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset) { pcapng_t *pcapng = (pcapng_t *)wth->priv; int bytes_read; guint64 ts; wtapng_block_t wblock; pcapng_debug1("pcapng_read: wth->data_offset is initially %" G_GINT64_MODIFIER "u", wth->data_offset); *data_offset = wth->data_offset; pcapng_debug1("pcapng_read: *data_offset is initially set to %" G_GINT64_MODIFIER "u", *data_offset); /* XXX - This should be done in the packet block reading function and * should make use of the caplen of the packet. */ if (wth->snapshot_length > 0) { buffer_assure_space(wth->frame_buffer, wth->snapshot_length); } else { buffer_assure_space(wth->frame_buffer, WTAP_MAX_PACKET_SIZE); } wblock.frame_buffer = buffer_start_ptr(wth->frame_buffer); wblock.pseudo_header = &wth->pseudo_header; wblock.packet_header = &wth->phdr; wblock.file_encap = &wth->file_encap; /* read next block */ while (1) { bytes_read = pcapng_read_block(wth->fh, FALSE, pcapng, &wblock, err, err_info); if (bytes_read <= 0) { pcapng_debug0("pcapng_read: couldn't read packet block"); return FALSE; } /* block must be a "Packet Block" or an "Enhanced Packet Block" -> otherwise continue */ if (wblock.type == BLOCK_TYPE_PB || wblock.type == BLOCK_TYPE_EPB) { break; } /* XXX - improve handling of "unknown" blocks */ pcapng_debug1("pcapng_read: block type 0x%x not PB/EPB", wblock.type); *data_offset += bytes_read; pcapng_debug1("pcapng_read: *data_offset is updated to %" G_GINT64_MODIFIER "u", *data_offset); } /* Combine the two 32-bit pieces of the timestamp into one 64-bit value */ ts = (((guint64)wblock.data.packet.ts_high) << 32) | ((guint64)wblock.data.packet.ts_low); wth->phdr.caplen = wblock.data.packet.cap_len - wblock.data.packet.pseudo_header_len; wth->phdr.len = wblock.data.packet.packet_len - wblock.data.packet.pseudo_header_len; if (wblock.data.packet.interface_id < pcapng->number_of_interfaces) { interface_data_t int_data; guint64 time_units_per_second; gint id; id = (gint)wblock.data.packet.interface_id; int_data = g_array_index(pcapng->interface_data, interface_data_t, id); time_units_per_second = int_data.time_units_per_second; wth->phdr.pkt_encap = int_data.wtap_encap; wth->phdr.ts.secs = (time_t)(ts / time_units_per_second); wth->phdr.ts.nsecs = (int)(((ts % time_units_per_second) * 1000000000) / time_units_per_second); } else { pcapng_debug1("pcapng_read: interface_id %d too large", wblock.data.packet.interface_id); wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN; *err = WTAP_ERR_BAD_RECORD; *err_info = g_strdup_printf("pcapng: interface index %u is too large", wblock.data.packet.interface_id); return FALSE; } /*pcapng_debug2("Read length: %u Packet length: %u", bytes_read, wth->phdr.caplen);*/ wth->data_offset = *data_offset + bytes_read; pcapng_debug1("pcapng_read: wth->data_offset is finally %" G_GINT64_MODIFIER "u", wth->data_offset); return TRUE; } /* classic wtap: seek to file position and read packet */ static gboolean pcapng_seek_read(wtap *wth, gint64 seek_off, union wtap_pseudo_header *pseudo_header, guchar *pd, int length _U_, int *err, gchar **err_info) { pcapng_t *pcapng = (pcapng_t *)wth->priv; guint64 bytes_read64; int bytes_read; wtapng_block_t wblock; /* seek to the right file position */ bytes_read64 = file_seek(wth->random_fh, seek_off, SEEK_SET, err); if (bytes_read64 <= 0) { return FALSE; /* Seek error */ } pcapng_debug1("pcapng_seek_read: reading at offset %" G_GINT64_MODIFIER "u", seek_off); wblock.frame_buffer = pd; wblock.pseudo_header = pseudo_header; wblock.packet_header = &wth->phdr; wblock.file_encap = &wth->file_encap; /* read the block */ bytes_read = pcapng_read_block(wth->random_fh, FALSE, pcapng, &wblock, err, err_info); if (bytes_read <= 0) { *err = file_error(wth->random_fh); pcapng_debug3("pcapng_seek_read: couldn't read packet block (err=%d, errno=%d, bytes_read=%d).", *err, errno, bytes_read); return FALSE; } /* block must be a "Packet Block" or an "Enhanced Packet Block" */ if (wblock.type != BLOCK_TYPE_PB && wblock.type != BLOCK_TYPE_EPB) { pcapng_debug1("pcapng_seek_read: block type %u not PB/EPB", wblock.type); return FALSE; } return TRUE; } /* classic wtap: close capture file */ static void pcapng_close(wtap *wth) { pcapng_t *pcapng = (pcapng_t *)wth->priv; pcapng_debug0("pcapng_close: closing file"); if (pcapng->interface_data != NULL) { g_array_free(pcapng->interface_data, TRUE); } } typedef struct { GArray *interface_data; guint number_of_interfaces; } pcapng_dump_t; static gboolean pcapng_write_section_header_block(wtap_dumper *wdh, wtapng_block_t *wblock, int *err) { pcapng_block_header_t bh; pcapng_section_header_block_t shb; size_t nwritten; /* write block header */ bh.block_type = wblock->type; bh.block_total_length = sizeof(bh) + sizeof(shb) /* + options */ + 4; nwritten = wtap_dump_file_write(wdh, &bh, sizeof bh); if (nwritten != sizeof bh) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof bh; /* write block fixed content */ /* XXX - get these values from wblock? */ shb.magic = 0x1A2B3C4D; shb.version_major = 1; shb.version_minor = 0; shb.section_length = -1; nwritten = wtap_dump_file_write(wdh, &shb, sizeof shb); if (nwritten != sizeof shb) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof shb; /* XXX - write (optional) block options */ /* write block footer */ nwritten = wtap_dump_file_write(wdh, &bh.block_total_length, sizeof bh.block_total_length); if (nwritten != sizeof bh.block_total_length) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof bh.block_total_length; return TRUE; } static gboolean pcapng_write_if_descr_block(wtap_dumper *wdh, wtapng_block_t *wblock, int *err) { pcapng_block_header_t bh; pcapng_interface_description_block_t idb; size_t nwritten; pcapng_debug3("pcapng_write_if_descr_block: encap = %d (%s), snaplen = %d", wblock->data.if_descr.link_type, wtap_encap_string(wtap_pcap_encap_to_wtap_encap(wblock->data.if_descr.link_type)), wblock->data.if_descr.snap_len); if (wblock->data.if_descr.link_type == (guint16)-1) { *err = WTAP_ERR_UNSUPPORTED_ENCAP; return FALSE; } /* write block header */ bh.block_type = wblock->type; bh.block_total_length = sizeof(bh) + sizeof(idb) /* + options */ + 4; nwritten = wtap_dump_file_write(wdh, &bh, sizeof bh); if (nwritten != sizeof bh) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof bh; /* write block fixed content */ idb.linktype = wblock->data.if_descr.link_type; idb.reserved = 0; idb.snaplen = wblock->data.if_descr.snap_len; nwritten = wtap_dump_file_write(wdh, &idb, sizeof idb); if (nwritten != sizeof idb) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof idb; /* XXX - write (optional) block options */ /* write block footer */ nwritten = wtap_dump_file_write(wdh, &bh.block_total_length, sizeof bh.block_total_length); if (nwritten != sizeof bh.block_total_length) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof bh.block_total_length; return TRUE; } static gboolean pcapng_write_packet_block(wtap_dumper *wdh, wtapng_block_t *wblock, int *err) { pcapng_block_header_t bh; pcapng_enhanced_packet_block_t epb; size_t nwritten; const guint32 zero_pad = 0; guint32 pad_len; guint32 phdr_len; phdr_len = (guint32)pcap_get_phdr_size(wblock->data.packet.wtap_encap, wblock->pseudo_header); if ((phdr_len + wblock->data.packet.cap_len) % 4) { pad_len = 4 - ((phdr_len + wblock->data.packet.cap_len) % 4); } else { pad_len = 0; } /* write (enhanced) packet block header */ bh.block_type = wblock->type; bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(epb) + phdr_len + wblock->data.packet.cap_len + pad_len /* + options */ + 4; nwritten = wtap_dump_file_write(wdh, &bh, sizeof bh); if (nwritten != sizeof bh) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof bh; /* write block fixed content */ epb.interface_id = wblock->data.packet.interface_id; epb.timestamp_high = wblock->data.packet.ts_high; epb.timestamp_low = wblock->data.packet.ts_low; epb.captured_len = wblock->data.packet.cap_len + phdr_len; epb.packet_len = wblock->data.packet.packet_len + phdr_len; nwritten = wtap_dump_file_write(wdh, &epb, sizeof epb); if (nwritten != sizeof epb) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof epb; /* write pseudo header */ if (!pcap_write_phdr(wdh, wblock->data.packet.wtap_encap, wblock->pseudo_header, err)) { return FALSE; } wdh->bytes_dumped += phdr_len; /* write packet data */ nwritten = wtap_dump_file_write(wdh, wblock->frame_buffer, wblock->data.packet.cap_len); if (nwritten != wblock->data.packet.cap_len) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += wblock->data.packet.cap_len; /* write padding (if any) */ if (pad_len != 0) { nwritten = wtap_dump_file_write(wdh, &zero_pad, pad_len); if (nwritten != pad_len) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += pad_len; } /* XXX - write (optional) block options */ /* write block footer */ nwritten = wtap_dump_file_write(wdh, &bh.block_total_length, sizeof bh.block_total_length); if (nwritten != sizeof bh.block_total_length) { if (nwritten == 0 && wtap_dump_file_ferror(wdh)) *err = wtap_dump_file_ferror(wdh); else *err = WTAP_ERR_SHORT_WRITE; return FALSE; } wdh->bytes_dumped += sizeof bh.block_total_length; return TRUE; } static gboolean pcapng_write_block(wtap_dumper *wdh, /*pcapng_t *pn, */wtapng_block_t *wblock, int *err) { switch(wblock->type) { case(BLOCK_TYPE_SHB): return pcapng_write_section_header_block(wdh, wblock, err); case(BLOCK_TYPE_IDB): return pcapng_write_if_descr_block(wdh, wblock, err); case(BLOCK_TYPE_PB): /* Packet Block is obsolete */ return FALSE; case(BLOCK_TYPE_EPB): return pcapng_write_packet_block(wdh, wblock, err); default: pcapng_debug1("Unknown block_type: 0x%x", wblock->type); return FALSE; } } static guint32 pcapng_lookup_interface_id_by_encap(int wtap_encap, wtap_dumper *wdh) { gint i; interface_data_t int_data; pcapng_dump_t *pcapng = (pcapng_dump_t *)wdh->priv; for(i = 0; i < (gint)pcapng->number_of_interfaces; i++) { int_data = g_array_index(pcapng->interface_data, interface_data_t, i); if (wtap_encap == int_data.wtap_encap) { return (guint32)i; } } return G_MAXUINT32; } static gboolean pcapng_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr, const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err) { wtapng_block_t wblock; interface_data_t int_data; guint32 interface_id; guint64 ts; pcapng_dump_t *pcapng = (pcapng_dump_t *)wdh->priv; pcapng_debug2("pcapng_dump: encap = %d (%s)", phdr->pkt_encap, wtap_encap_string(phdr->pkt_encap)); interface_id = pcapng_lookup_interface_id_by_encap(phdr->pkt_encap, wdh); if (interface_id == G_MAXUINT32) { /* write the interface description block */ wblock.frame_buffer = NULL; wblock.pseudo_header = NULL; wblock.packet_header = NULL; wblock.file_encap = NULL; wblock.type = BLOCK_TYPE_IDB; wblock.data.if_descr.link_type = wtap_wtap_encap_to_pcap_encap(phdr->pkt_encap); wblock.data.if_descr.snap_len = wdh->snaplen; /* XXX */ /* XXX - options unused */ wblock.data.if_descr.if_speed = -1; wblock.data.if_descr.if_tsresol = 6; /* default: usec */ wblock.data.if_descr.if_os = NULL; wblock.data.if_descr.if_fcslen = -1; if (!pcapng_write_block(wdh, &wblock, err)) { return FALSE; } interface_id = pcapng->number_of_interfaces; int_data.wtap_encap = phdr->pkt_encap; int_data.time_units_per_second = 0; g_array_append_val(pcapng->interface_data, int_data); pcapng->number_of_interfaces++; pcapng_debug3("pcapng_dump: added interface description block with index %u for encap = %d (%s).", interface_id, phdr->pkt_encap, wtap_encap_string(phdr->pkt_encap)); } wblock.frame_buffer = pd; wblock.pseudo_header = pseudo_header; wblock.packet_header = NULL; wblock.file_encap = NULL; /* write the (enhanced) packet block */ wblock.type = BLOCK_TYPE_EPB; /* default is to write out in microsecond resolution */ ts = (((guint64)phdr->ts.secs) * 1000000) + (phdr->ts.nsecs / 1000); /* Split the 64-bit timestamp into two 32-bit pieces */ wblock.data.packet.ts_high = (guint32)(ts >> 32); wblock.data.packet.ts_low = (guint32)ts; wblock.data.packet.cap_len = phdr->caplen; wblock.data.packet.packet_len = phdr->len; wblock.data.packet.interface_id = interface_id; wblock.data.packet.wtap_encap = phdr->pkt_encap; /* currently unused */ wblock.data.packet.drop_count = -1; wblock.data.packet.opt_comment = NULL; if (!pcapng_write_block(wdh, &wblock, err)) { return FALSE; } return TRUE; } /* Finish writing to a dump file. Returns TRUE on success, FALSE on failure. */ static gboolean pcapng_dump_close(wtap_dumper *wdh, int *err _U_) { pcapng_dump_t *pcapng = (pcapng_dump_t *)wdh->priv; pcapng_debug0("pcapng_dump_close"); g_array_free(pcapng->interface_data, TRUE); pcapng->number_of_interfaces = 0; return TRUE; } /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on failure */ gboolean pcapng_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err) { wtapng_block_t wblock; pcapng_dump_t *pcapng; wblock.frame_buffer = NULL; wblock.pseudo_header = NULL; wblock.packet_header = NULL; wblock.file_encap = NULL; pcapng_debug0("pcapng_dump_open"); /* This is a pcapng file */ wdh->subtype_write = pcapng_dump; wdh->subtype_close = pcapng_dump_close; pcapng = (pcapng_dump_t *)g_malloc(sizeof(pcapng_dump_t)); wdh->priv = (void *)pcapng; pcapng->interface_data = g_array_new(FALSE, FALSE, sizeof(interface_data_t)); pcapng->number_of_interfaces = 0; /* write the section header block */ wblock.type = BLOCK_TYPE_SHB; wblock.data.section.section_length = -1; /* XXX - options unused */ wblock.data.section.opt_comment = NULL; wblock.data.section.shb_hardware = NULL; wblock.data.section.shb_os = NULL; wblock.data.section.shb_user_appl = NULL; if (!pcapng_write_block(wdh, &wblock, err)) { return FALSE; } pcapng_debug0("pcapng_dump_open: wrote section header block."); return TRUE; } /* Returns 0 if we could write the specified encapsulation type, an error indication otherwise. */ int pcapng_dump_can_write_encap(int wtap_encap) { pcapng_debug2("pcapng_dump_can_write_encap: encap = %d (%s)", wtap_encap, wtap_encap_string(wtap_encap)); /* Per-packet encapsulations is supported. */ if (wtap_encap == WTAP_ENCAP_PER_PACKET) return 0; /* Make sure we can figure out this DLT type */ if (wtap_wtap_encap_to_pcap_encap(wtap_encap) == -1) return WTAP_ERR_UNSUPPORTED_ENCAP; return 0; }