wireshark/wiretap/file_access.c
Gerald Combs 35cddec4de Associate .scap files with Stratoshark
3b4c215a5f associated .scap files with Wireshark. Make sure they're just
associated with Stratoshark.

Split our wiretap file extension lists into separate versions based on
application flavor.

Fixes #20583
2025-06-20 12:01:38 +00:00

2928 lines
87 KiB
C

/* file_access.c
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
#include "wtap-int.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <wsutil/file_util.h>
#include <wsutil/tempfile.h>
#ifdef HAVE_PLUGINS
#include <wsutil/plugins.h>
#endif
#include <wsutil/ws_assert.h>
#include "wtap_modules.h"
#include "file_wrappers.h"
#include "required_file_handlers.h"
#include <wsutil/application_flavor.h>
#include <wsutil/buffer.h>
#include <wsutil/str_util.h>
#include "lanalyzer.h"
#include "ngsniffer.h"
#include "radcom.h"
#include "ascendtext.h"
#include "nettl.h"
#include "libpcap.h"
#include "snoop.h"
#include "iptrace.h"
#include "iseries.h"
#include "netmon.h"
#include "netxray.h"
#include "toshiba.h"
#include "eyesdn.h"
#include "i4btrace.h"
#include "csids.h"
#include "pppdump.h"
#include "peekclassic.h"
#include "peektagged.h"
#include "vms.h"
#include "dbs-etherwatch.h"
#include "visual.h"
#include "cosine.h"
#include "5views.h"
#include "erf.h"
#include "hcidump.h"
#include "logcat.h"
#include "logcat_text.h"
#include "json.h"
#include "observer.h"
#include "k12.h"
#include "ber.h"
#include "catapult_dct2000.h"
#include "mp4.h"
#include "mp2t.h"
#include "mpeg.h"
#include "netscreen.h"
#include "commview.h"
#include "pcapng.h"
#include "aethra.h"
#include "btsnoop.h"
#include "tnef.h"
#include "dct3trace.h"
#include "packetlogger.h"
#include "daintree-sna.h"
#include "netscaler.h"
#include "mime_file.h"
#include "ipfix.h"
#include "vwr.h"
#include "camins.h"
#include "stanag4607.h"
#include "capsa.h"
#include "nettrace_3gpp_32_423.h"
#include "mplog.h"
#include "dpa400.h"
#include "rfc7468.h"
#include "ruby_marshal.h"
#include "systemd_journal.h"
#include "log3gpp.h"
#include "candump.h"
#include "busmaster.h"
#include "cllog.h"
#include "blf.h"
#include "eri_enb_log.h"
#include "autosar_dlt.h"
#include "rtpdump.h"
#include "ems.h"
#include "ttl.h"
#include "peak-trc.h"
/*
* Add an extension, and all compressed versions thereof if requested,
* to a GSList of extensions.
*/
static GSList *
add_extensions(GSList *extensions, const char *extension,
GSList *compression_type_extensions)
{
/*
* Add the specified extension.
*/
extensions = g_slist_prepend(extensions, g_strdup(extension));
/*
* Add whatever compressed versions we were supplied.
*/
for (GSList *compression_type_extension = compression_type_extensions;
compression_type_extension != NULL;
compression_type_extension = g_slist_next(compression_type_extension)) {
extensions = g_slist_prepend(extensions,
ws_strdup_printf("%s.%s", extension,
(const char *)compression_type_extension->data));
}
return extensions;
}
/*
* File types that can be identified by file extensions.
*
* These are used in file open dialogs to offer choices of extensions
* for which to filter. Note that the first field can list more than
* one type of file, because, for example, ".cap" is a popular
* extension used by a number of capture file types.
*
* File types that *don't* have a file extension used for them should
* *not* be placed here; if there's nothing to put in the last field
* of the structure, don't put an entry here, not even one with an
* empty string for the extensions list.
*
* All added file types, regardless of extension or lack thereof,
* must also be added open_info_base[] below.
*/
static const struct file_extension_info wireshark_file_type_extensions_base[] = {
{ "Wireshark/tcpdump/... - pcap", true, "pcap;cap;dmp" },
{ "Wireshark/... - pcapng", true, "pcapng;ntar" },
{ "Network Monitor, Surveyor, NetScaler", true, "cap" },
{ "Sun snoop", true, "snoop" },
{ "InfoVista 5View capture", true, "5vw" },
{ "Sniffer (DOS)", true, "cap;enc;trc;fdc;syc" },
{ "Cinco NetXRay, Sniffer (Windows)", true, "cap;caz" },
{ "Endace ERF capture", true, "erf" },
{ "EyeSDN USB S0/E1 ISDN trace format", true, "trc" },
{ "HP-UX nettl trace", true, "trc0;trc1" },
{ "Viavi Observer", true, "bfr" },
{ "Colasoft Capsa", true, "cscpkt" },
{ "Novell LANalyzer", true, "tr1" },
{ "Tektronix K12xx 32-bit .rf5 format", true, "rf5" },
{ "Savvius *Peek", true, "pkt;tpc;apc;wpz" },
{ "Catapult DCT2000 trace (.out format)", true, "out" },
{ "Micropross mplog", true, "mplog" },
{ "TamoSoft CommView NCF", true, "ncf" },
{ "TamoSoft CommView NCFX", true, "ncfx" },
{ "Symbian OS btsnoop", true, "log" },
{ "XML files (including Gammu DCT3 traces)", true, "xml" },
{ "macOS PacketLogger", true, "pklg" },
{ "Daintree SNA", true, "dcf" },
{ "IPFIX File Format", true, "pfx;ipfix" },
{ "Aethra .aps file", true, "aps" },
{ "MPEG2 transport stream", true, "mp2t;ts;m2ts;mpg" },
{ "Ixia IxVeriWave .vwr Raw 802.11 Capture", true, "vwr" },
{ "CAM Inspector file", true, "camins" },
{ "BLF file", true, "blf" },
{ "AUTOSAR DLT file", true, "dlt" },
{ "TTL file", true, "ttl" },
{ "MPEG files", false, "mpeg;mpg;mp3" },
{ "Transport-Neutral Encapsulation Format", false, "tnef" },
{ "JPEG/JFIF files", false, "jpg;jpeg;jfif" },
{ "JavaScript Object Notation file", false, "json" },
{ "MP4 file", false, "mp4" },
{ "RTPDump file", false, "rtp;rtpdump" },
{ "EMS file", false, "ems" },
{ "ASN.1 Basic Encoding Rules", false, "cer;crl;csr;p10;p12;p772;p7c;p7s;p7m;p8;pfx;tsq;tsr" },
{ "RFC 7468 files", false, "crt;pem" },
{ "PEAK CAN TRC log", true, "trc" },
};
#define N_WIRESHARK_FILE_TYPE_EXTENSIONS array_length(wireshark_file_type_extensions_base)
static const struct file_extension_info stratoshark_file_type_extensions_base[] = {
{"Stratoshark/... - scap", true, "scap"},
};
#define N_STRATOSHARK_FILE_TYPE_EXTENSIONS array_length(stratoshark_file_type_extensions_base)
static const struct file_extension_info* file_type_extensions;
static GArray* file_type_extensions_arr;
/* initialize the extensions array if it has not been initialized yet */
static void
init_file_type_extensions(void)
{
if (file_type_extensions_arr) return;
file_type_extensions_arr = g_array_new(false,true,sizeof(struct file_extension_info));
if (application_flavor_is_wireshark()) {
g_array_append_vals(file_type_extensions_arr, wireshark_file_type_extensions_base, N_WIRESHARK_FILE_TYPE_EXTENSIONS);
} else {
g_array_append_vals(file_type_extensions_arr, stratoshark_file_type_extensions_base, N_STRATOSHARK_FILE_TYPE_EXTENSIONS);
}
file_type_extensions = (struct file_extension_info*)(void *)file_type_extensions_arr->data;
}
void
wtap_register_file_type_extension(const struct file_extension_info *ei)
{
init_file_type_extensions();
g_array_append_val(file_type_extensions_arr,*ei);
file_type_extensions = (const struct file_extension_info*)(void *)file_type_extensions_arr->data;
}
int
wtap_get_num_file_type_extensions(void)
{
return file_type_extensions_arr->len;
}
const char *
wtap_get_file_extension_type_name(int extension_type)
{
return file_type_extensions[extension_type].name;
}
static GSList *
add_extensions_for_file_extensions_type(int extension_type, GSList *extensions,
GSList *compression_type_extensions)
{
char **extensions_set, **extensionp, *extension;
/*
* Split the extension-list string into a set of extensions.
*/
extensions_set = g_strsplit(file_type_extensions[extension_type].extensions,
";", 0);
/*
* Add each of those extensions to the list.
*/
for (extensionp = extensions_set; *extensionp != NULL; extensionp++) {
extension = *extensionp;
/*
* Add the extension, and all compressed variants
* of it.
*/
extensions = add_extensions(extensions, extension,
compression_type_extensions);
}
g_strfreev(extensions_set);
return extensions;
}
/* Return a list of file extensions that are used by the specified file
* extension type.
*
* All strings in the list are allocated with g_malloc() and must be freed
* with g_free().
*/
GSList *
wtap_get_file_extension_type_extensions(unsigned extension_type)
{
GSList *extensions, *compression_type_extensions;
if (extension_type >= file_type_extensions_arr->len)
return NULL; /* not a valid extension type */
extensions = NULL; /* empty list, to start with */
/*
* Get compression-type extensions, if any.
*/
compression_type_extensions = wtap_get_all_compression_type_extensions_list();
/*
* Add all this file extension type's extensions, with compressed
* variants.
*/
extensions = add_extensions_for_file_extensions_type(extension_type,
extensions, compression_type_extensions);
g_slist_free(compression_type_extensions);
return g_slist_reverse(extensions);
}
/*
* The open_file_* routines must return:
*
* WTAP_OPEN_ERROR on an I/O error;
*
* WTAP_OPEN_MINE if the file they're reading is one of the types
* it handles;
*
* WTAP_OPEN_NOT_MINE if the file they're reading isn't the type
* they're checking for.
*
* If the routine handles this type of file, it must set the "file_type"
* field in the "struct wtap" to the type of the file.
*
* Note that the routine does *not* have to free the private data pointer on
* error. The caller takes care of that by calling wtap_close on error.
* (See https://gitlab.com/wireshark/wireshark/-/issues/8518)
*
* However, the caller *does* have to free the private data pointer when
* returning WTAP_OPEN_NOT_MINE, since the next file type will be called
* and will likely just overwrite the pointer.
*
* The names are used in file open dialogs to select, for files that
* don't have magic numbers and that could potentially be files of
* more than one type based on the heuristics, a particular file
* type to interpret it as, if the file name has no extension, the
* extension isn't sufficient to determine the appropriate file type,
* or the extension is wrong.
*
* NOTE: when adding file formats to this list you may also want to add them
* to the following files so that the various desktop environments will
* know that Wireshark can open the file:
* 1) resources/freedesktop/org.wireshark.Wireshark-mime.xml (for freedesktop.org environments)
* 2) packaging/macosx/WiresharkInfo.plist.in (for macOS)
*
* If your file format has a commonly-used extension (e.g., ".pcap") then you
* should probably also add it to file_type_extensions_base[] (in this file),
* to the list of "<glob pattern=...>" entries for this file format in
* resources/freedesktop/org.wireshark.Wireshark-mime.xml, to the
* CFBundleTypeExtensions array for this file format in
* packaging/macosx/WiresharkInfo.plist, and to the PushFileExtensions macro
* in packaging/nsis/wireshark-common.nsh and the File Associations in
* packaging/wix/ComponentGroups.wxi (for Windows).
*/
static const struct open_info open_info_base[] = {
/* Open routines that look for magic numbers */
{ "Wireshark/tcpdump/... - pcap", OPEN_INFO_MAGIC, libpcap_open, NULL, NULL, NULL },
{ "Wireshark/... - pcapng", OPEN_INFO_MAGIC, pcapng_open, NULL, NULL, NULL },
{ "Sniffer (DOS)", OPEN_INFO_MAGIC, ngsniffer_open, NULL, NULL, NULL },
{ "Snoop, Shomiti/Finisar Surveyor", OPEN_INFO_MAGIC, snoop_open, NULL, NULL, NULL },
{ "AIX iptrace", OPEN_INFO_MAGIC, iptrace_open, NULL, NULL, NULL },
{ "Microsoft Network Monitor", OPEN_INFO_MAGIC, netmon_open, NULL, NULL, NULL },
{ "Cinco NetXray/Sniffer (Windows)", OPEN_INFO_MAGIC, netxray_open, NULL, NULL, NULL },
{ "RADCOM WAN/LAN analyzer", OPEN_INFO_MAGIC, radcom_open, NULL, NULL, NULL },
{ "HP-UX nettl trace", OPEN_INFO_MAGIC, nettl_open, NULL, NULL, NULL },
{ "Visual Networks traffic capture", OPEN_INFO_MAGIC, visual_open, NULL, NULL, NULL },
{ "InfoVista 5View capture", OPEN_INFO_MAGIC, _5views_open, NULL, NULL, NULL },
{ "Viavi Observer", OPEN_INFO_MAGIC, observer_open, NULL, NULL, NULL },
{ "Savvius tagged", OPEN_INFO_MAGIC, peektagged_open, NULL, NULL, NULL },
{ "Colasoft Capsa", OPEN_INFO_MAGIC, capsa_open, NULL, NULL, NULL },
{ "DBS Etherwatch (VMS)", OPEN_INFO_MAGIC, dbs_etherwatch_open, NULL, NULL, NULL },
{ "Tektronix K12xx 32-bit .rf5 format", OPEN_INFO_MAGIC, k12_open, NULL, NULL, NULL },
{ "Catapult DCT2000 trace (.out format)", OPEN_INFO_MAGIC, catapult_dct2000_open, NULL, NULL, NULL },
{ "Aethra .aps file", OPEN_INFO_MAGIC, aethra_open, NULL, NULL, NULL },
{ "Symbian OS btsnoop", OPEN_INFO_MAGIC, btsnoop_open, "log", NULL, NULL },
{ "EyeSDN USB S0/E1 ISDN trace format", OPEN_INFO_MAGIC, eyesdn_open, NULL, NULL, NULL },
{ "Transport-Neutral Encapsulation Format", OPEN_INFO_MAGIC, tnef_open, NULL, NULL, NULL },
/* 3GPP TS 32.423 Trace must come before MIME Files as it's XML based*/
{ "3GPP TS 32.423 Trace format", OPEN_INFO_MAGIC, nettrace_3gpp_32_423_file_open, NULL, NULL, NULL },
/* Gammu DCT3 trace must come before MIME files as it's XML based*/
{ "Gammu DCT3 trace", OPEN_INFO_MAGIC, dct3trace_open, NULL, NULL, NULL },
{ "BLF Logfile", OPEN_INFO_MAGIC, blf_open, NULL, NULL, NULL },
{ "AUTOSAR DLT Logfile", OPEN_INFO_MAGIC, autosar_dlt_open, NULL, NULL, NULL },
{ "TTL Logfile", OPEN_INFO_MAGIC, ttl_open, NULL, NULL, NULL },
{ "RTPDump files", OPEN_INFO_MAGIC, rtpdump_open, NULL, NULL, NULL },
{ "MIME Files Format", OPEN_INFO_MAGIC, mime_file_open, NULL, NULL, NULL },
{ "Micropross mplog", OPEN_INFO_MAGIC, mplog_open, NULL, NULL, NULL },
{ "Unigraf DPA-400 capture", OPEN_INFO_MAGIC, dpa400_open, NULL, NULL, NULL },
{ "RFC 7468 files", OPEN_INFO_MAGIC, rfc7468_open, NULL, NULL, NULL },
/* Open routines that have no magic numbers and require heuristics. */
{ "Novell LANalyzer", OPEN_INFO_HEURISTIC, lanalyzer_open, "tr1", NULL, NULL },
/*
* PacketLogger must come before MPEG, because its files
* are sometimes grabbed by mpeg_open.
*/
{ "macOS PacketLogger", OPEN_INFO_HEURISTIC, packetlogger_open, "pklg", NULL, NULL },
/* Some MPEG files have magic numbers, others just have heuristics. */
{ "MPEG", OPEN_INFO_HEURISTIC, mpeg_open, "mpeg;mpg;mp3", NULL, NULL },
{ "Daintree SNA", OPEN_INFO_HEURISTIC, daintree_sna_open, "dcf", NULL, NULL },
{ "STANAG 4607 Format", OPEN_INFO_HEURISTIC, stanag4607_open, NULL, NULL, NULL },
{ "ASN.1 Basic Encoding Rules", OPEN_INFO_HEURISTIC, ber_open, NULL, NULL, NULL },
/*
* I put NetScreen *before* erf, because there were some
* false positives with my test-files (Sake Blok, July 2007)
*
* I put VWR *after* ERF, because there were some cases where
* ERF files were misidentified as vwr files (Stephen
* Donnelly, August 2013; see bug 9054)
*
* I put VWR *after* Peek Classic, CommView, iSeries text,
* Toshiba text, K12 text, VMS tcpiptrace text, and NetScaler,
* because there were some cases where files of those types were
* misidentified as vwr files (Guy Harris, December 2013)
*/
{ "NetScreen snoop text file", OPEN_INFO_HEURISTIC, netscreen_open, "txt", NULL, NULL },
{ "Endace ERF capture", OPEN_INFO_HEURISTIC, erf_open, "erf", NULL, NULL },
{ "IPFIX File Format", OPEN_INFO_HEURISTIC, ipfix_open, "pfx;ipfix",NULL, NULL },
{ "K12 text file", OPEN_INFO_HEURISTIC, k12text_open, "txt", NULL, NULL },
{ "Savvius classic", OPEN_INFO_HEURISTIC, peekclassic_open, "pkt;tpc;apc;wpz", NULL, NULL },
{ "pppd log (pppdump format)", OPEN_INFO_HEURISTIC, pppdump_open, NULL, NULL, NULL },
{ "IBM iSeries comm. trace", OPEN_INFO_HEURISTIC, iseries_open, "txt", NULL, NULL },
{ "I4B ISDN trace", OPEN_INFO_HEURISTIC, i4btrace_open, NULL, NULL, NULL },
{ "MPEG2 transport stream", OPEN_INFO_HEURISTIC, mp2t_open, "mp2t;ts;mpg", NULL, NULL },
{ "CSIDS IPLog", OPEN_INFO_HEURISTIC, csids_open, NULL, NULL, NULL },
{ "TCPIPtrace (VMS)", OPEN_INFO_HEURISTIC, vms_open, "txt", NULL, NULL },
{ "CoSine IPSX L2 capture", OPEN_INFO_HEURISTIC, cosine_open, "txt", NULL, NULL },
{ "Bluetooth HCI dump", OPEN_INFO_HEURISTIC, hcidump_open, NULL, NULL, NULL },
{ "TamoSoft CommView NCF", OPEN_INFO_HEURISTIC, commview_ncf_open, "ncf", NULL, NULL },
{ "TamoSoft CommView NCFX", OPEN_INFO_HEURISTIC, commview_ncfx_open, "ncfx", NULL, NULL },
{ "NetScaler", OPEN_INFO_HEURISTIC, nstrace_open, "cap", NULL, NULL },
{ "Android Logcat Binary format", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL },
{ "Android Logcat Text formats", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL },
{ "Candump log", OPEN_INFO_HEURISTIC, candump_open, NULL, NULL, NULL },
{ "Busmaster log", OPEN_INFO_HEURISTIC, busmaster_open, NULL, NULL, NULL },
{ "CSS Electronics CLX000 CAN log", OPEN_INFO_MAGIC, cllog_open, "txt", NULL, NULL },
{ "Ericsson eNode-B raw log", OPEN_INFO_MAGIC, eri_enb_log_open, NULL, NULL, NULL },
{ "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL },
{ "PEAK CAN TRC log", OPEN_INFO_HEURISTIC, peak_trc_open, "trc", NULL, NULL },
/* ASCII trace files from Telnet sessions. */
{ "Lucent/Ascend access server trace", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL },
{ "Toshiba Compact ISDN Router snoop", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL },
{ "EGNOS Message Server (EMS) file", OPEN_INFO_HEURISTIC, ems_open, "ems", NULL, NULL },
/* Extremely weak heuristics - put them at the end. */
{ "Ixia IxVeriWave .vwr Raw Capture", OPEN_INFO_HEURISTIC, vwr_open, "vwr", NULL, NULL },
{ "CAM Inspector file", OPEN_INFO_HEURISTIC, camins_open, "camins", NULL, NULL },
{ "JavaScript Object Notation", OPEN_INFO_HEURISTIC, json_open, "json", NULL, NULL },
{ "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
{ "3gpp phone log", OPEN_INFO_MAGIC, log3gpp_open, "log", NULL, NULL },
{ "MP4 media file", OPEN_INFO_MAGIC, mp4_open, "mp4", NULL, NULL },
};
/* this is only used to build the dynamic array on load, do NOT use this
* for anything else, because the size of the actual array will change if
* Lua scripts register a new file reader.
*/
#define N_OPEN_INFO_ROUTINES array_length(open_info_base)
static GArray *open_info_arr;
/* this always points to the top of the created array */
struct open_info *open_routines;
/* this points to the first OPEN_INFO_HEURISTIC type in the array */
static unsigned heuristic_open_routine_idx;
static void
set_heuristic_routine(void)
{
unsigned i;
ws_assert(open_info_arr != NULL);
for (i = 0; i < open_info_arr->len; i++) {
if (open_routines[i].type == OPEN_INFO_HEURISTIC) {
heuristic_open_routine_idx = i;
break;
}
/* sanity check */
ws_assert(open_routines[i].type == OPEN_INFO_MAGIC);
}
ws_assert(heuristic_open_routine_idx > 0);
}
void
init_open_routines(void)
{
unsigned int i;
struct open_info *i_open;
if (open_info_arr)
return;
open_info_arr = g_array_new(true,true,sizeof(struct open_info));
g_array_append_vals(open_info_arr, open_info_base, N_OPEN_INFO_ROUTINES);
open_routines = (struct open_info *)(void*) open_info_arr->data;
/* Populate the extensions_set list now */
for (i = 0, i_open = open_routines; i < open_info_arr->len; i++, i_open++) {
if (i_open->extensions != NULL)
i_open->extensions_set = g_strsplit(i_open->extensions, ";", 0);
}
set_heuristic_routine();
}
/*
* Registers a new file reader - currently only called by wslua code for
* Lua readers and by compiled file reader plugins.
*
* If first_routine is true, the reader added before other readers of its
* type (magic or heuristic). This should be done only in cases where
* this reader's open test must be performed early, to avoid false
* positives for other readers' tests treating files for this reader
* as being for another reader.
*
* XXX - given that there is no guarantee that registration routines will
* be called in a given order, all this really does is divide readers for
* a given type (magic or heuristic) into two categories, with open routines
* for readers in the first category (first_routine true) all being called
* before readers in the second category; it does not guarantee a particular
* total order for open routines.
*
* Checks for an existing reader of the same name and errors if it finds one;
* if you want to handle that condition more gracefully, call
* wtap_has_open_info() first.
*/
void
wtap_register_open_info(struct open_info *oi, const bool first_routine)
{
if (!oi || !oi->name) {
ws_error("No open_info name given to register");
return;
}
/* verify name doesn't already exist */
if (wtap_has_open_info(oi->name)) {
ws_error("Name given to register_open_info already exists");
return;
}
if (oi->extensions != NULL)
oi->extensions_set = g_strsplit(oi->extensions, ";", 0);
/* if it's magic and first, prepend it; if it's heuristic and not first,
append it; if it's anything else, stick it in the middle */
if (first_routine && oi->type == OPEN_INFO_MAGIC) {
g_array_prepend_val(open_info_arr, *oi);
} else if (!first_routine && oi->type == OPEN_INFO_HEURISTIC) {
g_array_append_val(open_info_arr, *oi);
} else {
g_array_insert_val(open_info_arr, heuristic_open_routine_idx, *oi);
}
open_routines = (struct open_info *)(void*) open_info_arr->data;
set_heuristic_routine();
}
/* De-registers a file reader by removing it from the GArray based on its name.
* This function must NOT be called during wtap_open_offline(), since it changes the array.
* Note: this function will error if it doesn't find the given name; if you want to handle
* that condition more gracefully, call wtap_has_open_info() first.
*/
void
wtap_deregister_open_info(const char *name)
{
unsigned i;
if (!name) {
ws_error("Missing open_info name to de-register");
return;
}
for (i = 0; i < open_info_arr->len; i++) {
if (open_routines[i].name && strcmp(open_routines[i].name, name) == 0) {
g_strfreev(open_routines[i].extensions_set);
open_info_arr = g_array_remove_index(open_info_arr, i);
set_heuristic_routine();
return;
}
}
ws_error("deregister_open_info: name not found");
}
/* Determines if a open routine short name already exists
*/
bool
wtap_has_open_info(const char *name)
{
unsigned i;
if (!name) {
ws_error("No name given to wtap_has_open_info!");
return false;
}
for (i = 0; i < open_info_arr->len; i++) {
if (open_routines[i].name && strcmp(open_routines[i].name, name) == 0) {
return true;
}
}
return false;
}
bool
wtap_uses_lua_filehandler(const wtap* wth)
{
if (wth && wth->wslua_data != NULL) {
/*
* Currently, wslua_data is set if and only if using a Lua
* file handler.
*/
return true;
}
return false;
}
/*
* Visual C++ on Win32 systems doesn't define these. (Old UNIX systems don't
* define them either.)
*
* Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
*/
#ifndef S_ISREG
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#endif
#ifndef S_IFIFO
#define S_IFIFO _S_IFIFO
#endif
#ifndef S_ISFIFO
#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
#endif
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
/* returns the 'type' number to use for wtap_open_offline based on the
* passed-in name (the name in the open_info struct). It returns WTAP_TYPE_AUTO
* on failure, which is the number 0. The 'type' number is the entry's index+1,
* because that's what wtap_open_offline() expects it to be.
*/
unsigned int
open_info_name_to_type(const char *name)
{
unsigned int i;
if (!name)
return WTAP_TYPE_AUTO;
for (i = 0; i < open_info_arr->len; i++) {
if (open_routines[i].name != NULL &&
strcmp(name, open_routines[i].name) == 0)
return i+1;
}
return WTAP_TYPE_AUTO; /* no such file type */
}
static char *
get_file_extension(const char *pathname)
{
char *filename;
char **components;
size_t ncomponents;
char *extensionp;
/*
* Is the pathname empty?
*/
if (strcmp(pathname, "") == 0)
return NULL; /* no extension */
/*
* Find the last component of the pathname.
*/
filename = g_path_get_basename(pathname);
/*
* Does it have an extension?
*/
if (strchr(filename, '.') == NULL) {
g_free(filename);
return NULL; /* no extension whatsoever */
}
/*
* Yes. Fold it to lowercase, since open_routines[] has
* its extensions in lowercase.
*/
ascii_strdown_inplace(filename);
/*
* Split it into components separated by ".".
*/
components = g_strsplit(filename, ".", 0);
g_free(filename);
/*
* Count the components.
*/
for (ncomponents = 0; components[ncomponents] != NULL; ncomponents++)
;
if (ncomponents == 0) {
g_strfreev(components);
return NULL; /* no components */
}
if (ncomponents == 1) {
g_strfreev(components);
return NULL; /* only one component, with no "." */
}
/*
* Get compression-type extensions, if any.
*/
GSList *compression_type_extensions = wtap_get_all_compression_type_extensions_list();
/*
* Is the last component one of the extensions used for compressed
* files?
*/
extensionp = components[ncomponents - 1];
for (GSList *compression_type_extension = compression_type_extensions;
compression_type_extension != NULL;
compression_type_extension = g_slist_next(compression_type_extension)) {
if (strcmp(extensionp, (const char *)compression_type_extension->data) == 0) {
/*
* Yes, so it's one of the compressed-file extensions.
* Is there an extension before that?
*/
if (ncomponents == 2) {
g_slist_free(compression_type_extensions);
g_strfreev(components);
return NULL; /* no, only two components */
}
/*
* Yes, return that extension.
*/
g_slist_free(compression_type_extensions);
extensionp = g_strdup(components[ncomponents - 2]);
g_strfreev(components);
return extensionp;
}
}
g_slist_free(compression_type_extensions);
/*
* The extension isn't one of the compressed-file extensions;
* return it.
*/
extensionp = g_strdup(extensionp);
g_strfreev(components);
return extensionp;
}
/*
* Check if file extension is used in this heuristic
*/
static bool
heuristic_uses_extension(unsigned int i, const char *extension)
{
char **extensionp;
/*
* Does this file type *have* any extensions?
*/
if (open_routines[i].extensions == NULL)
return false; /* no */
/*
* Check each of them against the specified extension.
*/
for (extensionp = open_routines[i].extensions_set; *extensionp != NULL;
extensionp++) {
if (strcmp(extension, *extensionp) == 0) {
return true; /* it's one of them */
}
}
return false; /* it's not one of them */
}
/*
* Attempt to open the file corresponding to "wth" using the file format
* handler in "candidate".
*
* Returns WTAP_OPEN_MINE upon success, WTAP_OPEN_NOT_MINE if the file is not
* in a suitable format for "candidate", or WTAP_OPEN_ERROR if a failure
* occurred while reading the input.
*/
static int
try_one_open(wtap *wth, const struct open_info *candidate, int *err, char **err_info)
{
/* Seek back to the beginning of the file; the open routine for the
* previous file type may have left the file position somewhere other
* than the beginning, and the open routine for this file type will
* probably want to start reading at the beginning.
*
* Initialize the data offset while we're at it.
*/
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
/* Error - give up */
return WTAP_OPEN_ERROR;
}
/* Set wth with wslua data if any - this is how we pass the data to the
* file reader, kinda like the priv member but not free'd later.
* It's ok for this to copy a NULL.
*/
wth->wslua_data = candidate->wslua_data;
return candidate->open_routine(wth, err, err_info);
}
/*
* Attempt to open the file corresponding to "wth". If "type" is supplied
* (i.e. other than WTAP_TYPE_AUTO), that will be the only type attempted.
* Otherwise, heuristic detection of the file format will be performed,
* possibly guided by the extension part of "filename".
*
* Returns WTAP_OPEN_MINE upon success, WTAP_OPEN_NOT_MINE if it was not
* possible to determine a suitable format for the file, or WTAP_OPEN_ERROR if
* a failure occurred while reading the input.
*/
static int
try_open(wtap *wth, unsigned int type, int *err, char **err_info)
{
int result = WTAP_OPEN_NOT_MINE;
unsigned i;
char *extension;
/* 'type' is 1-based. */
if (type != WTAP_TYPE_AUTO && type <= open_info_arr->len) {
/* Try only the specified type. */
return try_one_open(wth, &open_routines[type - 1], err, err_info);
}
/* First, all file types that support magic numbers. */
for (i = 0; i < heuristic_open_routine_idx && result == WTAP_OPEN_NOT_MINE; i++) {
result = try_one_open(wth, &open_routines[i], err, err_info);
}
if (result != WTAP_OPEN_NOT_MINE) {
return result;
}
/* Does this file's name have an extension? */
extension = get_file_extension(wth->pathname);
if (extension != NULL) {
unsigned pass;
/*
* Yes, the filename has an extension.
*
* The heuristic types fall into one of three categories, which
* we attempt in order by scanning for each category in turn.
*
* First pass selects the heuristic types that list this file's
* extension, as these are most likely to be the correct choice
* for this file.
*
* Second pass selects heuristic types which have no
* extensions. We try those before the ones that have
* extensions that *don't* match this file's extension, on the
* theory that files of those types generally have one of the
* type's extensions, and, as this file *doesn't* have one of
* those extensions, it's probably *not* one of those files.
*
* Third pass selects heuristic types which support extensions
* but where none of them matches this file's extension.
*
* We need only continue searching until we find a match or an
* error occurs.
*/
for (pass = 0; pass < 3 && result == WTAP_OPEN_NOT_MINE; pass++) {
for (i = heuristic_open_routine_idx; i < open_info_arr->len && result == WTAP_OPEN_NOT_MINE; i++) {
if ( (pass == 0 && heuristic_uses_extension(i, extension))
|| (pass == 1 && open_routines[i].extensions == NULL)
|| (pass == 2 && open_routines[i].extensions != NULL
&& !heuristic_uses_extension(i, extension))) {
result = try_one_open(wth, &open_routines[i], err, err_info);
}
}
}
g_free(extension);
} else {
/* No extension. Try all the heuristic types in order. */
for (i = heuristic_open_routine_idx; i < open_info_arr->len && result == WTAP_OPEN_NOT_MINE; i++) {
result = try_one_open(wth, &open_routines[i], err, err_info);
}
}
return result;
}
/* Opens a file and prepares a wtap struct.
* If "do_random" is true, it opens the file twice; the second open
* allows the application to do random-access I/O without moving
* the seek offset for sequential I/O, which is used by Wireshark
* so that it can do sequential I/O to a capture file that's being
* written to as new packets arrive independently of random I/O done
* to display protocol trees for packets when they're selected.
*/
wtap *
wtap_open_offline(const char *filename, unsigned int type, int *err, char **err_info,
bool do_random)
{
int fd;
ws_statb64 statb;
bool ispipe = false;
wtap *wth;
bool use_stdin = false;
wtap_block_t shb;
*err = 0;
*err_info = NULL;
/* open standard input if filename is '-' */
if (strcmp(filename, "-") == 0)
use_stdin = true;
/* First, make sure the file is valid */
if (use_stdin) {
if (ws_fstat64(0, &statb) < 0) {
*err = errno;
return NULL;
}
} else {
if (ws_stat64(filename, &statb) < 0) {
*err = errno;
return NULL;
}
}
if (S_ISFIFO(statb.st_mode)) {
/*
* Opens of FIFOs are allowed only when not opening
* for random access.
*
* Currently, we do seeking when trying to find out
* the file type, but our I/O routines do some amount
* of buffering, and do backward seeks within the buffer
* if possible, so at least some file types can be
* opened from pipes, so we don't completely disallow opens
* of pipes.
*/
if (do_random) {
*err = WTAP_ERR_RANDOM_OPEN_PIPE;
return NULL;
}
ispipe = true;
} else if (S_ISDIR(statb.st_mode)) {
/*
* Return different errors for "this is a directory"
* and "this is some random special file type", so
* the user can get a potentially more helpful error.
*/
*err = EISDIR;
return NULL;
} else if (! S_ISREG(statb.st_mode)) {
*err = WTAP_ERR_NOT_REGULAR_FILE;
return NULL;
}
/*
* We need two independent descriptors for random access, so
* they have different file positions. If we're opening the
* standard input, we can only dup it to get additional
* descriptors, so we can't have two independent descriptors,
* and thus can't do random access.
*/
if (use_stdin && do_random) {
*err = WTAP_ERR_RANDOM_OPEN_STDIN;
return NULL;
}
errno = ENOMEM;
wth = g_new0(wtap, 1);
/* Open the file */
errno = WTAP_ERR_CANT_OPEN;
if (use_stdin) {
/*
* We dup FD 0, so that we don't have to worry about
* a file_close of wth->fh closing the standard
* input of the process.
*/
fd = ws_dup(0);
if (fd < 0) {
*err = errno;
g_free(wth);
return NULL;
}
#ifdef _WIN32
if (_setmode(fd, O_BINARY) == -1) {
/* "Shouldn't happen" */
*err = errno;
g_free(wth);
return NULL;
}
#endif
if (!(wth->fh = file_fdopen(fd))) {
*err = errno;
ws_close(fd);
g_free(wth);
return NULL;
}
} else {
if (!(wth->fh = file_open(filename))) {
*err = errno;
g_free(wth);
return NULL;
}
}
if (do_random) {
if (!(wth->random_fh = file_open(filename))) {
*err = errno;
file_close(wth->fh);
g_free(wth);
return NULL;
}
} else
wth->random_fh = NULL;
/* initialization */
wth->ispipe = ispipe;
wth->file_encap = WTAP_ENCAP_UNKNOWN;
wth->subtype_sequential_close = NULL;
wth->subtype_close = NULL;
wth->file_tsprec = WTAP_TSPREC_USEC;
wth->pathname = g_strdup(filename);
wth->priv = NULL;
wth->wslua_data = NULL;
wth->shb_hdrs = g_array_new(false, false, sizeof(wtap_block_t));
shb = wtap_block_create(WTAP_BLOCK_SECTION);
if (shb)
g_array_append_val(wth->shb_hdrs, shb);
/* Initialize the array containing a list of interfaces. pcapng_open and
* erf_open needs this (and libpcap_open for ERF encapsulation types).
* Always initing it here saves checking for a NULL ptr later. */
wth->interface_data = g_array_new(false, false, sizeof(wtap_block_t));
/*
* Next interface data that wtap_get_next_interface_description()
* will return.
*/
wth->next_interface_data = 0;
wth->shb_iface_to_global = g_array_new(false, false, sizeof(unsigned));
g_array_append_val(wth->shb_iface_to_global, wth->interface_data->len);
if (wth->random_fh) {
wth->fast_seek = g_ptr_array_new();
file_set_random_access(wth->fh, false, wth->fast_seek);
file_set_random_access(wth->random_fh, true, wth->fast_seek);
}
/* Find a file format handler which can read the file. */
switch (try_open(wth, type, err, err_info)) {
case WTAP_OPEN_NOT_MINE:
/* Well, it's not one of the types of file we know about. */
*err = WTAP_ERR_FILE_UNKNOWN_FORMAT;
/* FALLTHROUGH */
case WTAP_OPEN_ERROR:
wtap_close(wth);
wth = NULL;
}
return wth;
}
/*
* Given the pathname of the file we just closed with wtap_fdclose(), attempt
* to reopen that file and assign the new file descriptor(s) to the sequential
* stream and, if do_random is true, to the random stream. Used on Windows
* after the rename of a file we had open was done or if the rename of a
* file on top of a file we had open failed.
*
* This is only required by Wireshark, not TShark, and, at the point that
* Wireshark is doing this, the sequential stream is closed, and the
* random stream is open, so this refuses to open pipes, and only
* reopens the random stream.
*/
bool
wtap_fdreopen(wtap *wth, const char *filename, int *err)
{
ws_statb64 statb;
/*
* We need two independent descriptors for random access, so
* they have different file positions. If we're opening the
* standard input, we can only dup it to get additional
* descriptors, so we can't have two independent descriptors,
* and thus can't do random access.
*/
if (strcmp(filename, "-") == 0) {
*err = WTAP_ERR_RANDOM_OPEN_STDIN;
return false;
}
/* First, make sure the file is valid */
if (ws_stat64(filename, &statb) < 0) {
*err = errno;
return false;
}
if (S_ISFIFO(statb.st_mode)) {
/*
* Opens of FIFOs are not allowed; see above.
*/
*err = WTAP_ERR_RANDOM_OPEN_PIPE;
return false;
} else if (S_ISDIR(statb.st_mode)) {
/*
* Return different errors for "this is a directory"
* and "this is some random special file type", so
* the user can get a potentially more helpful error.
*/
*err = EISDIR;
return false;
} else if (! S_ISREG(statb.st_mode)) {
*err = WTAP_ERR_NOT_REGULAR_FILE;
return false;
}
/* Open the file */
errno = WTAP_ERR_CANT_OPEN;
if (!file_fdreopen(wth->random_fh, filename)) {
*err = errno;
return false;
}
if (strcmp(filename, wth->pathname) != 0) {
g_free(wth->pathname);
wth->pathname = g_strdup(filename);
}
return true;
}
/* Table of the file types and subtypes for which we have support. */
/*
* Pointer to the GArray holding the registered file types.
*/
static GArray* file_type_subtype_table_arr;
/*
* Pointer to the table of registered file types in that GArray.
*/
static const struct file_type_subtype_info* file_type_subtype_table;
/*
* Number of elements in the table for builtin file types/subtypes.
*/
static unsigned wtap_num_builtin_file_types_subtypes;
/*
* Required builtin types.
*/
int pcap_file_type_subtype = -1;
int pcap_nsec_file_type_subtype = -1;
int pcapng_file_type_subtype = -1;
/*
* Table for mapping old file type/subtype names to new ones for
* backwards compatibility.
*/
static GHashTable *type_subtype_name_map;
/*
* Initialize the table of file types/subtypes with all the builtin
* types/subtypes.
*/
void
wtap_init_file_type_subtypes(void)
{
/* Don't do this twice. */
ws_assert(file_type_subtype_table_arr == NULL);
/*
* Estimate the number of file types/subtypes as twice the
* number of modules; that's probably an overestimate, as
* the average number of file types/subtypes registered by
* a module is > 1 but probably < 2, but that shouldn't
* waste too much memory.
*
* Add on 7 more for pcapng, pcap, nanosecond pcap, and the
* extra modified flavors of pcap.
*/
file_type_subtype_table_arr = g_array_sized_new(false, true,
sizeof(struct file_type_subtype_info), wtap_module_count*2 + 7);
file_type_subtype_table = (const struct file_type_subtype_info*)(void *)file_type_subtype_table_arr->data;
/*
* Initialize the hash table for mapping old file type/subtype
* names to the corresponding new names.
*/
type_subtype_name_map = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, g_free);
/* No entries yet, so no builtin entries yet. */
wtap_num_builtin_file_types_subtypes = 0;
/*
* Register the builtin entries that aren't in the table.
* First, do the required ones; register pcapng first, then
* pcap, so, at the beginning of the table, we have pcapng,
* pcap, nanosecond pcap, and the weird modified pcaps, so
* searches for file types that can write a file format
* start with pcapng, pcap, and nanosecond pcap.
*/
register_pcapng();
register_pcap();
/* Now register the ones found by the build process */
for (unsigned i = 0; i < wtap_module_count; i++)
wtap_module_reg[i].cb_func();
/* Update the number of builtin entries. */
wtap_num_builtin_file_types_subtypes = file_type_subtype_table_arr->len;
}
/*
* Attempt to register a new file type/subtype; fails if a type/subtype
* with that name is already registered.
*/
int
wtap_register_file_type_subtype(const struct file_type_subtype_info* fi)
{
struct file_type_subtype_info* finfo;
unsigned file_type_subtype;
/*
* Check for required fields (description and name).
*/
if (!fi || !fi->description || !fi->name) {
ws_warning("no file type info");
return -1;
}
/*
* There must be at least one block type that this file
* type/subtype supports.
*/
if (fi->num_supported_blocks == 0 || fi->supported_blocks == NULL) {
ws_warning("no blocks supported by file type \"%s\"", fi->name);
return -1;
}
/*
* Is this type already registered?
*/
if (wtap_name_to_file_type_subtype(fi->name) != -1) {
/*
* Yes. You don't get to replace an existing handler.
*/
ws_warning("file type \"%s\" is already registered", fi->name);
return -1;
}
/*
* Is there a freed entry in the array, due to a file type
* being de-registered?
*
* Skip the built-in entries, as they're never deregistered.
*/
for (file_type_subtype = wtap_num_builtin_file_types_subtypes;
file_type_subtype < file_type_subtype_table_arr->len;
file_type_subtype++) {
if (file_type_subtype_table[file_type_subtype].name == NULL) {
/*
* We found such an entry.
*
* Get the pointer from the GArray, so that we get a
* non-const pointer.
*/
finfo = &g_array_index(file_type_subtype_table_arr, struct file_type_subtype_info, file_type_subtype);
/*
* Fill in the entry with the new values.
*/
*finfo = *fi;
return (int)file_type_subtype;
}
}
/*
* There aren't any free slots, so add a new entry.
* Get the number of current number of entries, which will
* be the index of the new entry, then append this entry
* to the end of the array, change file_type_subtype_table
* in case the array had to get reallocated, and return
* the index of the new entry.
*/
file_type_subtype = file_type_subtype_table_arr->len;
g_array_append_val(file_type_subtype_table_arr, *fi);
file_type_subtype_table = (const struct file_type_subtype_info*)(void *)file_type_subtype_table_arr->data;
return file_type_subtype;
}
/* De-registers a file writer - they can never be removed from the GArray, but we can "clear" an entry.
*/
void
wtap_deregister_file_type_subtype(const int subtype)
{
struct file_type_subtype_info* finfo;
if (subtype < 0 || subtype >= (int)file_type_subtype_table_arr->len) {
ws_error("invalid file type to de-register");
return;
}
if ((unsigned)subtype < wtap_num_builtin_file_types_subtypes) {
ws_error("built-in file types cannot be de-registered");
return;
}
/*
* Get the pointer from the GArray, so that we get a non-const
* pointer.
*/
finfo = &g_array_index(file_type_subtype_table_arr, struct file_type_subtype_info, subtype);
/*
* Clear out this entry.
*/
finfo->description = NULL;
finfo->name = NULL;
finfo->default_file_extension = NULL;
finfo->additional_file_extensions = NULL;
finfo->writing_must_seek = false;
finfo->num_supported_blocks = 0;
finfo->supported_blocks = NULL;
finfo->can_write_encap = NULL;
finfo->dump_open = NULL;
finfo->wslua_info = NULL;
}
/*
* Given a GArray of WTAP_ENCAP_ types, return the per-file encapsulation
* type that would be needed to write out a file with those types. If
* there's only one type, it's that type, otherwise it's
* WTAP_ENCAP_PER_PACKET.
*/
int
wtap_dump_required_file_encap_type(const GArray *file_encaps)
{
int encap;
encap = WTAP_ENCAP_PER_PACKET;
if (file_encaps->len == 1) {
/* OK, use the one-and-only encapsulation type. */
encap = g_array_index(file_encaps, int, 0);
}
return encap;
}
bool
wtap_dump_can_write_encap(int file_type_subtype, int encap)
{
int result = 0;
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len ||
file_type_subtype_table[file_type_subtype].can_write_encap == NULL)
return false;
result = (*file_type_subtype_table[file_type_subtype].can_write_encap)(encap);
if (result != 0) {
/* if the err said to check wslua's can_write_encap, try that */
if (result == WTAP_ERR_CHECK_WSLUA
&& file_type_subtype_table[file_type_subtype].wslua_info != NULL
&& file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap != NULL) {
result = (*file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap)(encap, file_type_subtype_table[file_type_subtype].wslua_info->wslua_data);
}
if (result != 0)
return false;
}
return true;
}
/*
* Return true if a capture with a given GArray of encapsulation types
* and a given bitset of comment types can be written in a specified
* format, and false if it can't.
*/
static bool
wtap_dump_can_write_format(int ft, const GArray *file_encaps,
uint32_t required_comment_types)
{
unsigned i;
/*
* Can we write in this format?
*/
if (!wtap_dump_can_open(ft)) {
/* No. */
return false;
}
/*
* Yes. Can we write out all the required comments in this
* format?
*/
if (required_comment_types & WTAP_COMMENT_PER_SECTION) {
if (wtap_file_type_subtype_supports_option(ft,
WTAP_BLOCK_SECTION, OPT_COMMENT) == OPTION_NOT_SUPPORTED) {
/* Not section comments. */
return false;
}
}
if (required_comment_types & WTAP_COMMENT_PER_INTERFACE) {
if (wtap_file_type_subtype_supports_option(ft,
WTAP_BLOCK_IF_ID_AND_INFO, OPT_COMMENT) == OPTION_NOT_SUPPORTED) {
/* Not interface comments. */
return false;
}
}
if (required_comment_types & WTAP_COMMENT_PER_PACKET) {
if (wtap_file_type_subtype_supports_option(ft,
WTAP_BLOCK_PACKET, OPT_COMMENT) == OPTION_NOT_SUPPORTED) {
/* Not packet comments. */
return false;
}
}
/*
* Yes. Is the required per-file encapsulation type supported?
* This might be WTAP_ENCAP_PER_PACKET.
*/
if (!wtap_dump_can_write_encap(ft, wtap_dump_required_file_encap_type(file_encaps))) {
/* No. */
return false;
}
/*
* Yes. Are all the individual encapsulation types supported?
*/
for (i = 0; i < file_encaps->len; i++) {
if (!wtap_dump_can_write_encap(ft,
g_array_index(file_encaps, int, i))) {
/* No - one of them isn't. */
return false;
}
}
/* Yes - we're OK. */
return true;
}
/*
* Return true if we can write a file with the given GArray of
* encapsulation types and the given bitmask of comment types.
*/
bool
wtap_dump_can_write(const GArray *file_encaps, uint32_t required_comment_types)
{
int ft;
for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
/* To save a file with Wiretap, Wiretap has to handle that format,
* and its code to handle that format must be able to write a file
* with this file's encapsulation types.
*/
if (wtap_dump_can_write_format(ft, file_encaps, required_comment_types)) {
/* OK, we can write it out in this type. */
return true;
}
}
/* No, we couldn't save it in any format. */
return false;
}
/*
* Sort by file type/subtype name.
*/
static int
compare_file_type_subtypes_by_name(const void *a, const void *b)
{
int file_type_subtype_a = *(const int *)a;
int file_type_subtype_b = *(const int *)b;
return strcmp(wtap_file_type_subtype_name(file_type_subtype_a),
wtap_file_type_subtype_name(file_type_subtype_b));
}
/*
* Sort by file type/subtype description.
*/
static int
compare_file_type_subtypes_by_description(const void *a, const void *b)
{
int file_type_subtype_a = *(const int *)a;
int file_type_subtype_b = *(const int *)b;
return strcmp(wtap_file_type_subtype_description(file_type_subtype_a),
wtap_file_type_subtype_description(file_type_subtype_b));
}
/*
* Get a GArray of file type/subtype values for file types/subtypes
* that can be used to save a file of a given type/subtype with a given
* GArray of encapsulation types and the given bitmask of comment types.
*/
GArray *
wtap_get_savable_file_types_subtypes_for_file(int file_type_subtype,
const GArray *file_encaps, uint32_t required_comment_types,
ft_sort_order sort_order)
{
GArray *savable_file_types_subtypes;
int ft;
int default_file_type_subtype = -1;
int other_file_type_subtype = -1;
/* Can we save this file in its own file type/subtype? */
if (wtap_dump_can_write_format(file_type_subtype, file_encaps,
required_comment_types)) {
/* Yes - make that the default file type/subtype. */
default_file_type_subtype = file_type_subtype;
} else if (wtap_dump_can_write_format(pcap_file_type_subtype,
file_encaps,
required_comment_types)) {
/*
* No, but we can write it as a pcap file; make that
* the default file type/subtype.
*/
default_file_type_subtype = pcap_file_type_subtype;
} else if (wtap_dump_can_write_format(pcapng_file_type_subtype,
file_encaps,
required_comment_types)) {
/*
* No, but we can write it as a pcapng file; make that
* the default file type/subtype.
*/
default_file_type_subtype = pcapng_file_type_subtype;
} else {
/* OK, find the first file type/subtype we *can* save it as. */
default_file_type_subtype = -1;
for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
if (wtap_dump_can_write_format(ft, file_encaps,
required_comment_types)) {
/* OK, got it. */
default_file_type_subtype = ft;
break;
}
}
}
if (default_file_type_subtype == -1) {
/* We don't support writing this file as any file type/subtype. */
return NULL;
}
/*
* If the default is pcap, put pcapng right after it if we can
* also write it in pcapng format; otherwise, if the default is
* pcapng, put pcap right after it if we can also write it in
* pcap format.
*/
if (default_file_type_subtype == pcap_file_type_subtype) {
if (wtap_dump_can_write_format(pcapng_file_type_subtype,
file_encaps,
required_comment_types))
other_file_type_subtype = pcapng_file_type_subtype;
} else if (default_file_type_subtype == pcapng_file_type_subtype) {
if (wtap_dump_can_write_format(pcap_file_type_subtype,
file_encaps,
required_comment_types))
other_file_type_subtype = pcap_file_type_subtype;
}
/* Allocate the array. */
savable_file_types_subtypes = g_array_new(false, false,
sizeof (int));
/*
* First, add the types we don't want to force to the
* beginning of the list.
*/
for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
if (ft == default_file_type_subtype ||
ft == other_file_type_subtype)
continue; /* we will done this one later */
if (wtap_dump_can_write_format(ft, file_encaps,
required_comment_types)) {
/* OK, we can write it out in this type. */
g_array_append_val(savable_file_types_subtypes, ft);
}
}
/* Now, sort the list. */
g_array_sort(savable_file_types_subtypes,
(sort_order == FT_SORT_BY_NAME) ? compare_file_type_subtypes_by_name :
compare_file_type_subtypes_by_description);
/*
* If we have a type/subtype to put above the default one,
* do so.
*
* We put this type at the beginning before putting the
* default there, so the default is at the top.
*/
if (other_file_type_subtype != -1)
g_array_prepend_val(savable_file_types_subtypes,
other_file_type_subtype);
/* Put the default file type/subtype first in the list. */
g_array_prepend_val(savable_file_types_subtypes,
default_file_type_subtype);
return savable_file_types_subtypes;
}
/*
* Get a GArray of all writable file type/subtype values.
*/
GArray *
wtap_get_writable_file_types_subtypes(ft_sort_order sort_order)
{
GArray *writable_file_types_subtypes;
int ft;
/*
* Allocate the array.
* Pre-allocate room enough for all types.
* XXX - that's overkill; just scan the table to find all the
* writable types and count them.
*/
writable_file_types_subtypes = g_array_sized_new(false, false,
sizeof (int), file_type_subtype_table_arr->len);
/*
* First, add the types we don't want to force to the
* beginning of the list.
*/
for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
if (ft == pcap_file_type_subtype ||
ft == pcapng_file_type_subtype)
continue; /* we've already done these two */
if (wtap_dump_can_open(ft)) {
/* OK, we can write this type. */
g_array_append_val(writable_file_types_subtypes, ft);
}
}
/* Now, sort the list. */
g_array_sort(writable_file_types_subtypes,
(sort_order == FT_SORT_BY_NAME) ? compare_file_type_subtypes_by_name :
compare_file_type_subtypes_by_description);
/*
* Now, put pcap and pcapng at the beginning, as they're
* our "native" formats. Put pcapng there first, and
* pcap before it.
*/
if (pcapng_file_type_subtype != -1 &&
wtap_dump_can_open(pcapng_file_type_subtype)) {
/*
* We can write pcapng. (If we can't, that's a huge
* mistake.)
*/
g_array_prepend_val(writable_file_types_subtypes,
pcapng_file_type_subtype);
}
if (pcap_file_type_subtype != -1 &&
wtap_dump_can_open(pcap_file_type_subtype)) {
/*
* We can write pcap. (If we can't, that's a huge
* mistake.)
*/
g_array_prepend_val(writable_file_types_subtypes,
pcap_file_type_subtype);
}
return writable_file_types_subtypes;
}
/*
* String describing the file type/subtype.
*/
const char *
wtap_file_type_subtype_description(int file_type_subtype)
{
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len)
return NULL;
else
return file_type_subtype_table[file_type_subtype].description;
}
/*
* Name to use in, say, a command-line flag specifying the type/subtype.
*/
const char *
wtap_file_type_subtype_name(int file_type_subtype)
{
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len)
return NULL;
else
return file_type_subtype_table[file_type_subtype].name;
}
/*
* Register a backwards-compatibility name.
*/
void
wtap_register_compatibility_file_subtype_name(const char *old_name,
const char *new_name)
{
g_hash_table_insert(type_subtype_name_map, g_strdup(old_name),
g_strdup(new_name));
}
/*
* Translate a name to a capture file type/subtype.
*/
int
wtap_name_to_file_type_subtype(const char *name)
{
char *new_name;
int file_type_subtype;
/*
* Is this name a backwards-compatibility name?
*/
new_name = (char *)g_hash_table_lookup(type_subtype_name_map,
(void *)name);
if (new_name != NULL) {
/*
* Yes, and new_name is the name to which it should
* be mapped.
*/
name = new_name;
}
for (file_type_subtype = 0;
file_type_subtype < (int)file_type_subtype_table_arr->len;
file_type_subtype++) {
if (file_type_subtype_table[file_type_subtype].name != NULL &&
strcmp(name, file_type_subtype_table[file_type_subtype].name) == 0)
return file_type_subtype;
}
return -1; /* no such file type, or we can't write it */
}
/*
* Provide the file type/subtype for pcap.
*/
int
wtap_pcap_file_type_subtype(void)
{
/*
* Make sure pcap was registered as a file type/subtype;
* it's one of our "native" formats.
*/
ws_assert(pcap_file_type_subtype != -1);
return pcap_file_type_subtype;
}
/*
* Provide the file type/subtype for nanosecond-resolution pcap.
*/
int
wtap_pcap_nsec_file_type_subtype(void)
{
/*
* Make sure nanosecond-resolution pcap was registered
* as a file type/subtype; it's one of our "native" formats.
*/
ws_assert(pcap_nsec_file_type_subtype != -1);
return pcap_nsec_file_type_subtype;
}
/*
* Provide the file type/subtype for pcapng.
*/
int
wtap_pcapng_file_type_subtype(void)
{
/*
* Make sure pcapng was registered as a file type/subtype;
* it's one of our "native" formats.
*/
ws_assert(pcapng_file_type_subtype != -1);
return pcapng_file_type_subtype;
}
/*
* Determine if a file type/subtype can write a block of the given type.
*/
block_support_t
wtap_file_type_subtype_supports_block(int file_type_subtype,
wtap_block_type_t type)
{
size_t num_supported_blocks;
const struct supported_block_type *supported_blocks;
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len) {
/*
* There's no such file type, so it can't support any
* blocks.
*/
return BLOCK_NOT_SUPPORTED;
}
num_supported_blocks = file_type_subtype_table[file_type_subtype].num_supported_blocks;
supported_blocks = file_type_subtype_table[file_type_subtype].supported_blocks;
for (size_t block_idx = 0; block_idx < num_supported_blocks;
block_idx++) {
if (supported_blocks[block_idx].type == type)
return supported_blocks[block_idx].support;
}
/*
* Not found, which means not supported.
*/
return BLOCK_NOT_SUPPORTED;
}
/*
* Determine if a file type/subtype, when writing a block of the given type,
* can support adding the given option to the block.
*/
option_support_t
wtap_file_type_subtype_supports_option(int file_type_subtype,
wtap_block_type_t type, unsigned option)
{
size_t num_supported_blocks;
const struct supported_block_type *supported_blocks;
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len) {
/*
* There's no such file type, so it can't support any
* blocks, and thus can't support any options.
*/
return OPTION_NOT_SUPPORTED;
}
num_supported_blocks = file_type_subtype_table[file_type_subtype].num_supported_blocks;
supported_blocks = file_type_subtype_table[file_type_subtype].supported_blocks;
for (size_t block_idx = 0; block_idx < num_supported_blocks;
block_idx++) {
if (supported_blocks[block_idx].type == type) {
/*
* OK, that block is known.
* Is it supported?
*/
if (supported_blocks[block_idx].support == BLOCK_NOT_SUPPORTED) {
/*
* No, so clearly the option isn't
* supported in that block.
*/
return OPTION_NOT_SUPPORTED;
}
/*
* Yes, so check the options.
*/
size_t num_supported_options;
const struct supported_option_type *supported_options;
num_supported_options = supported_blocks[block_idx].num_supported_options;
supported_options = supported_blocks[block_idx].supported_options;
for (size_t opt_idx = 0; opt_idx < num_supported_options;
opt_idx++) {
if (supported_options[opt_idx].opt == option)
return supported_options[opt_idx].support;
}
/*
* Not found, which means not supported.
*/
return OPTION_NOT_SUPPORTED;
}
}
/*
* The block type wasn't found, which means it's not supported,
* which means the option isn't supported in that block.
*/
return OPTION_NOT_SUPPORTED;
}
static GSList *
add_extensions_for_file_type_subtype(int file_type_subtype, GSList *extensions,
GSList *compression_type_extensions)
{
char **extensions_set, **extensionp;
char *extension;
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len) {
/*
* There's no such file type, so it has no extensions
* to add.
*/
return extensions;
}
/*
* Add the default extension, and all of the compressed variants
* from the list of compressed-file extensions, if there is a
* default extension.
*/
if (file_type_subtype_table[file_type_subtype].default_file_extension != NULL) {
extensions = add_extensions(extensions,
file_type_subtype_table[file_type_subtype].default_file_extension,
compression_type_extensions);
}
if (file_type_subtype_table[file_type_subtype].additional_file_extensions != NULL) {
/*
* We have additional extensions; add them.
*
* First, split the extension-list string into a set of
* extensions.
*/
extensions_set = g_strsplit(file_type_subtype_table[file_type_subtype].additional_file_extensions,
";", 0);
/*
* Add each of those extensions to the list.
*/
for (extensionp = extensions_set; *extensionp != NULL;
extensionp++) {
extension = *extensionp;
/*
* Add the extension, and all compressed variants
* of it if requested.
*/
extensions = add_extensions(extensions, extension,
compression_type_extensions);
}
g_strfreev(extensions_set);
}
return extensions;
}
/* Return a list of file extensions that are used by the specified file
* type/subtype.
*
* If include_compressed is true, the list will include compressed
* extensions, e.g. not just "pcap" but also "pcap.gz" if we can read
* gzipped files.
*
* All strings in the list are allocated with g_malloc() and must be freed
* with g_free().
*/
GSList *
wtap_get_file_extensions_list(int file_type_subtype, bool include_compressed)
{
GSList *extensions, *compression_type_extensions;
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len)
return NULL; /* not a valid file type */
if (file_type_subtype_table[file_type_subtype].default_file_extension == NULL)
return NULL; /* valid, but no extensions known */
extensions = NULL; /* empty list, to start with */
/*
* Add all this file type's extensions, with compressed
* variants if include_compressed is true.
*/
if (include_compressed) {
/*
* Get compression-type extensions, if any.
*/
compression_type_extensions = wtap_get_all_compression_type_extensions_list();
} else {
/*
* We don't want the compressed file extensions.
*/
compression_type_extensions = NULL;
}
extensions = add_extensions_for_file_type_subtype(file_type_subtype, extensions,
compression_type_extensions);
g_slist_free(compression_type_extensions);
return g_slist_reverse(extensions);
}
/* Return a list of all extensions that are used by all capture file
* types, including compressed extensions, e.g. not just "pcap" but
* also "pcap.gz" if we can read gzipped files.
*
* "Capture files" means "include file types that correspond to
* collections of network packets, but not file types that
* store data that just happens to be transported over protocols
* such as HTTP but that aren't collections of network packets",
* so that it could be used for "All Capture Files" without picking
* up JPEG files or files such as that - those aren't capture files,
* and we *do* have them listed in the long list of individual file
* types, so omitting them from "All Capture Files" is the right
* thing to do.
*
* All strings in the list are allocated with g_malloc() and must be freed
* with g_free().
*
* This is used to generate a list of extensions to look for if the user
* chooses "All Capture Files" in a file open dialog.
*/
GSList *
wtap_get_all_capture_file_extensions_list(void)
{
GSList *extensions, *compression_type_extensions;
unsigned int i;
init_file_type_extensions();
extensions = NULL; /* empty list, to start with */
/*
* Get compression-type extensions, if any.
*/
compression_type_extensions = wtap_get_all_compression_type_extensions_list();
for (i = 0; i < file_type_extensions_arr->len; i++) {
/*
* Is this a capture file, rather than one of the
* other random file types we can read?
*/
if (file_type_extensions[i].is_capture_file) {
/*
* Yes. Add all this file extension type's
* extensions, with compressed variants.
*/
extensions = add_extensions_for_file_extensions_type(i,
extensions, compression_type_extensions);
}
}
g_slist_free(compression_type_extensions);
return g_slist_reverse(extensions);
}
/* Return a list of all extensions that are used by all file types that
* we can read, including compressed extensions, e.g. not just "pcap" but
* also "pcap.gz" if we can read gzipped files.
*
* "File type" means "include file types that correspond to collections
* of network packets, as well as file types that store data that just
* happens to be transported over protocols such as HTTP but that aren't
* collections of network packets, and plain text files".
*
* All strings in the list are allocated with g_malloc() and must be freed
* with g_free().
*
* This is used to get the "base name" for a file, by stripping off
* compressed-file extensions and extensions that correspond to file
* types that we know about.
*/
GSList *
wtap_get_all_file_extensions_list(void)
{
GSList *extensions, *compression_type_extensions;
extensions = NULL; /* empty list, to start with */
/*
* Get compression-type extensions, if any.
*/
compression_type_extensions = wtap_get_all_compression_type_extensions_list();
for (int ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
extensions = add_extensions_for_file_type_subtype(ft, extensions,
compression_type_extensions);
}
g_slist_free(compression_type_extensions);
return g_slist_reverse(extensions);
}
/*
* Free a list returned by wtap_get_file_extension_type_extensions(),
* wtap_get_all_capture_file_extensions_list, wtap_get_file_extensions_list(),
* or wtap_get_all_file_extensions_list().
*/
void
wtap_free_extensions_list(GSList *extensions)
{
GSList *extension;
for (extension = extensions; extension != NULL;
extension = g_slist_next(extension)) {
g_free(extension->data);
}
g_slist_free(extensions);
}
/*
* Return the default file extension to use with the specified file type
* and subtype; that's just the extension, without any ".".
*/
const char *
wtap_default_file_extension(int file_type_subtype)
{
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len)
return NULL;
else
return file_type_subtype_table[file_type_subtype].default_file_extension;
}
/*
* Return whether we know how to write the specified file type.
*/
bool
wtap_dump_can_open(int file_type_subtype)
{
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len ||
file_type_subtype_table[file_type_subtype].dump_open == NULL)
return false;
return true;
}
/*
* Return whether we know how to write a compressed file of the specified
* file type.
*/
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) || defined (HAVE_LZ4FRAME_H)
bool
wtap_dump_can_compress(int file_type_subtype)
{
/*
* If this is an unknown file type, or if we have to
* seek when writing out a file with this file type,
* return false.
*/
if (file_type_subtype < 0 ||
file_type_subtype >= (int)file_type_subtype_table_arr->len ||
file_type_subtype_table[file_type_subtype].writing_must_seek)
return false;
return true;
}
#else
bool
wtap_dump_can_compress(int file_type_subtype _U_)
{
return false;
}
#endif
static bool wtap_dump_open_finish(wtap_dumper *wdh, int *err,
char **err_info);
static WFILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename);
static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd);
static int wtap_dump_file_close(wtap_dumper *wdh);
static wtap_dumper *
wtap_dump_init_dumper(int file_type_subtype, wtap_compression_type compression_type,
const wtap_dump_params *params, int *err)
{
wtap_dumper *wdh;
wtap_block_t descr, file_int_data;
wtapng_if_descr_mandatory_t *descr_mand, *file_int_data_mand;
GArray *interfaces = params->idb_inf ? params->idb_inf->interface_data : NULL;
/* Can we write files of this file type/subtype?
*
* This will fail if file_type_subtype isn't a valid
* file type/subtype value, so, if it doesn't fail,
* we know file_type_subtype is within the bounds of
* the table of file types/subtypes.
*/
if (!wtap_dump_can_open(file_type_subtype)) {
/* Invalid type, or type we don't know how to write. */
*err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
return NULL;
}
/* OK, we know how to write that file type/subtype; can we write
* the specified encapsulation type in that file type/subtype?
*/
*err = (*file_type_subtype_table[file_type_subtype].can_write_encap)(params->encap);
/* if the err said to check wslua's can_write_encap, try that */
if (*err == WTAP_ERR_CHECK_WSLUA
&& file_type_subtype_table[file_type_subtype].wslua_info != NULL
&& file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap != NULL) {
*err = (*file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap)(params->encap, file_type_subtype_table[file_type_subtype].wslua_info->wslua_data);
}
if (*err != 0) {
/* No, we can't. */
return NULL;
}
/* Check whether we can open a capture file with that file type
* and that encapsulation, and, if the compression type isn't
* "uncompressed", whether we can write a *compressed* file
* of that file type.
* If we're doing compression, can this file type/subtype be
written in compressed form?
*
* (The particular type doesn't matter - if the file can't
* be written 100% sequentially, we can't compress it,
* because we can't go back and overwrite something we've
* already written.
*/
if (compression_type != WTAP_UNCOMPRESSED &&
!wtap_dump_can_compress(file_type_subtype)) {
*err = WTAP_ERR_COMPRESSION_NOT_SUPPORTED;
return NULL;
}
/* Allocate a data structure for the output stream. */
wdh = g_new0(wtap_dumper, 1);
if (wdh == NULL) {
*err = errno;
return NULL;
}
wdh->file_type_subtype = file_type_subtype;
wdh->snaplen = params->snaplen;
wdh->file_encap = params->encap;
wdh->compression_type = compression_type;
wdh->wslua_data = NULL;
wdh->shb_iface_to_global = params->shb_iface_to_global;
wdh->interface_data = g_array_new(false, false, sizeof(wtap_block_t));
/* Set Section Header Block data */
wdh->shb_hdrs = params->shb_hdrs;
/* Set Name Resolution Block data */
wdh->nrbs_growing = params->nrbs_growing;
/* Set Interface Description Block data */
if (interfaces && interfaces->len) {
if (!params->dont_copy_idbs) { /* XXX */
unsigned itf_count;
/* Note: this memory is owned by wtap_dumper and will become
* invalid after wtap_dump_close. */
for (itf_count = 0; itf_count < interfaces->len; itf_count++) {
file_int_data = g_array_index(interfaces, wtap_block_t, itf_count);
file_int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(file_int_data);
descr = wtap_block_make_copy(file_int_data);
if ((params->encap != WTAP_ENCAP_PER_PACKET) && (params->encap != file_int_data_mand->wtap_encap)) {
descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(descr);
descr_mand->wtap_encap = params->encap;
}
g_array_append_val(wdh->interface_data, descr);
}
}
} else if (params->encap != WTAP_ENCAP_NONE && params->encap != WTAP_ENCAP_PER_PACKET) {
/* Generate a fake IDB if we don't have one, unless the
* file encapsulation is none. (WTAP_ENCAP_NONE either
* means that there are no interfaces, or they will be
* provided later when reading the file in single-pass mode.)
*
* For WTAP_ENCAP_PER_PACKET, we'll have to generate IDBs
* from packet records as they come in. (pcapng does this now.)
*
* XXX File types should provide their own IDBs (possibly
* fake ones generated by wtap_add_generated_idb()), in
* order to support being used as inputs for mergecap where
* pcapng is the output.
*/
descr = wtap_dump_params_generate_idb(params);
g_array_append_val(wdh->interface_data, descr);
}
/* Set Decryption Secrets Blocks */
wdh->dsbs_initial = params->dsbs_initial;
wdh->dsbs_growing = params->dsbs_growing;
/* Set Sysdig meta events */
wdh->mevs_growing = params->mevs_growing;
return wdh;
}
wtap_dumper *
wtap_dump_open(const char *filename, int file_type_subtype,
wtap_compression_type compression_type, const wtap_dump_params *params,
int *err, char **err_info)
{
wtap_dumper *wdh;
WFILE_T fh;
*err = 0;
*err_info = NULL;
/* Allocate and initialize a data structure for the output stream. */
wdh = wtap_dump_init_dumper(file_type_subtype, compression_type, params,
err);
if (wdh == NULL)
return NULL;
/* In case "fopen()" fails but doesn't set "errno", set "errno"
to a generic "the open failed" error. */
errno = WTAP_ERR_CANT_OPEN;
fh = wtap_dump_file_open(wdh, filename);
if (fh == NULL) {
*err = errno;
g_free(wdh);
return NULL; /* can't create file */
}
wdh->fh = fh;
if (!wtap_dump_open_finish(wdh, err, err_info)) {
/* Get rid of the file we created; we couldn't finish
opening it. */
wtap_dump_file_close(wdh);
ws_unlink(filename);
g_free(wdh);
return NULL;
}
return wdh;
}
wtap_dumper *
wtap_dump_open_tempfile(const char *tmpdir, char **filenamep, const char *pfx,
int file_type_subtype, wtap_compression_type compression_type,
const wtap_dump_params *params, int *err, char **err_info)
{
int fd;
const char *ext;
char sfx[16];
wtap_dumper *wdh;
WFILE_T fh;
/* No path name for the temporary file yet. */
*filenamep = NULL;
*err = 0;
*err_info = NULL;
/* Allocate and initialize a data structure for the output stream. */
wdh = wtap_dump_init_dumper(file_type_subtype, compression_type, params,
err);
if (wdh == NULL)
return NULL;
/* Choose an appropriate suffix for the file */
ext = wtap_default_file_extension(file_type_subtype);
if (ext == NULL)
ext = "tmp";
sfx[0] = '.';
sfx[1] = '\0';
(void) g_strlcat(sfx, ext, 16);
/* Choose a random name for the file */
fd = create_tempfile(tmpdir, filenamep, pfx, sfx, NULL);
if (fd == -1) {
*err = WTAP_ERR_CANT_OPEN;
g_free(wdh);
return NULL; /* can't create file */
}
/* In case "fopen()" fails but doesn't set "errno", set "errno"
to a generic "the open failed" error. */
errno = WTAP_ERR_CANT_OPEN;
fh = wtap_dump_file_fdopen(wdh, fd);
if (fh == NULL) {
*err = errno;
ws_close(fd);
g_free(wdh);
return NULL; /* can't create file */
}
wdh->fh = fh;
if (!wtap_dump_open_finish(wdh, err, err_info)) {
/* Get rid of the file we created; we couldn't finish
opening it. */
wtap_dump_file_close(wdh);
ws_unlink(*filenamep);
g_free(wdh);
return NULL;
}
return wdh;
}
wtap_dumper *
wtap_dump_fdopen(int fd, int file_type_subtype, wtap_compression_type compression_type,
const wtap_dump_params *params, int *err, char **err_info)
{
wtap_dumper *wdh;
WFILE_T fh;
*err = 0;
*err_info = NULL;
/* Allocate and initialize a data structure for the output stream. */
wdh = wtap_dump_init_dumper(file_type_subtype, compression_type, params,
err);
if (wdh == NULL)
return NULL;
/* In case "fopen()" fails but doesn't set "errno", set "errno"
to a generic "the open failed" error. */
errno = WTAP_ERR_CANT_OPEN;
fh = wtap_dump_file_fdopen(wdh, fd);
if (fh == NULL) {
*err = errno;
g_free(wdh);
return NULL; /* can't create standard I/O stream */
}
wdh->fh = fh;
if (!wtap_dump_open_finish(wdh, err, err_info)) {
wtap_dump_file_close(wdh);
g_free(wdh);
return NULL;
}
return wdh;
}
wtap_dumper *
wtap_dump_open_stdout(int file_type_subtype, wtap_compression_type compression_type,
const wtap_dump_params *params, int *err, char **err_info)
{
int new_fd;
wtap_dumper *wdh;
/*
* Duplicate the file descriptor, so that we can close the
* wtap_dumper handle the same way we close any other
* wtap_dumper handle, without closing the standard output.
*/
new_fd = ws_dup(1);
if (new_fd == -1) {
/* dup failed */
*err = errno;
return NULL;
}
#ifdef _WIN32
/*
* Put the new descriptor into binary mode.
*
* XXX - even if the file format we're writing is a text
* format?
*/
if (_setmode(new_fd, O_BINARY) == -1) {
/* "Should not happen" */
*err = errno;
ws_close(new_fd);
return NULL;
}
#endif
wdh = wtap_dump_fdopen(new_fd, file_type_subtype, compression_type,
params, err, err_info);
if (wdh == NULL) {
/* Failed; close the new FD */
ws_close(new_fd);
return NULL;
}
return wdh;
}
static bool
wtap_dump_open_finish(wtap_dumper *wdh, int *err, char **err_info)
{
int fd;
bool cant_seek;
/* Can we do a seek on the file descriptor?
If not, note that fact. */
if (wdh->compression_type != WTAP_UNCOMPRESSED) {
cant_seek = true;
} else {
fd = ws_fileno((FILE *)wdh->fh);
if (ws_lseek64(fd, 1, SEEK_CUR) == (off_t) -1)
cant_seek = true;
else {
/* Undo the seek. */
ws_lseek64(fd, 0, SEEK_SET);
cant_seek = false;
}
}
/* If this file type requires seeking, and we can't seek, fail. */
if (file_type_subtype_table[wdh->file_type_subtype].writing_must_seek && cant_seek) {
*err = WTAP_ERR_CANT_WRITE_TO_PIPE;
return false;
}
/* Set wdh with wslua data if any - this is how we pass the data
* to the file writer.
*/
if (file_type_subtype_table[wdh->file_type_subtype].wslua_info)
wdh->wslua_data = file_type_subtype_table[wdh->file_type_subtype].wslua_info->wslua_data;
/* Now try to open the file for writing. */
if (!(*file_type_subtype_table[wdh->file_type_subtype].dump_open)(wdh, err,
err_info)) {
return false;
}
return true; /* success! */
}
bool
wtap_dump_add_idb(wtap_dumper *wdh, wtap_block_t idb, int *err,
char **err_info)
{
if (wdh->subtype_add_idb == NULL) {
/* Not supported. */
*err = WTAP_ERR_UNWRITABLE_REC_TYPE;
*err_info = g_strdup("Adding IDBs isn't supported by this file type");
return false;
}
*err = 0;
*err_info = NULL;
return (wdh->subtype_add_idb)(wdh, idb, err, err_info);
}
bool
wtap_dump(wtap_dumper *wdh, const wtap_rec *rec, int *err, char **err_info)
{
*err = 0;
*err_info = NULL;
return (wdh->subtype_write)(wdh, rec, err, err_info);
}
bool
wtap_dump_flush(wtap_dumper *wdh, int *err)
{
switch (wdh->compression_type) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
if (gzwfile_flush((GZWFILE_T)wdh->fh) == -1) {
*err = gzwfile_geterr((GZWFILE_T)wdh->fh);
return false;
}
break;
#endif
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
if (lz4wfile_flush((LZ4WFILE_T)wdh->fh) == -1) {
*err = lz4wfile_geterr((LZ4WFILE_T)wdh->fh);
return false;
}
break;
#endif /* HAVE_LZ4FRAME_H */
default:
if (fflush((FILE *)wdh->fh) == EOF) {
*err = errno;
return false;
}
}
return true;
}
bool
wtap_dump_close(wtap_dumper *wdh, bool *needs_reload,
int *err, char **err_info)
{
bool ret = true;
*err = 0;
*err_info = NULL;
if (wdh->subtype_finish != NULL) {
/* There's a finish routine for this dump stream. */
if (!(wdh->subtype_finish)(wdh, err, err_info))
ret = false;
}
errno = WTAP_ERR_CANT_CLOSE;
if (wtap_dump_file_close(wdh) == EOF) {
if (ret) {
/* The per-format finish function succeeded,
but the stream close didn't. Save the
reason why, if our caller asked for it. */
if (err != NULL)
*err = errno;
}
ret = false;
}
if (needs_reload != NULL)
*needs_reload = wdh->needs_reload;
g_free(wdh->priv);
wtap_block_array_free(wdh->interface_data);
wtap_block_array_unref(wdh->dsbs_initial);
g_free(wdh);
return ret;
}
int
wtap_dump_file_type_subtype(wtap_dumper *wdh)
{
return wdh->file_type_subtype;
}
int64_t
wtap_get_bytes_dumped(wtap_dumper *wdh)
{
return wdh->bytes_dumped;
}
void
wtap_set_bytes_dumped(wtap_dumper *wdh, int64_t bytes_dumped)
{
wdh->bytes_dumped = bytes_dumped;
}
bool
wtap_addrinfo_list_empty(addrinfo_lists_t *addrinfo_lists)
{
return (addrinfo_lists == NULL) ||
((addrinfo_lists->ipv4_addr_list == NULL) &&
(addrinfo_lists->ipv6_addr_list == NULL));
}
bool
wtap_dump_set_addrinfo_list(wtap_dumper *wdh, addrinfo_lists_t *addrinfo_lists)
{
if (!wdh || wdh->file_type_subtype < 0 ||
wdh->file_type_subtype >= (int)file_type_subtype_table_arr->len ||
wtap_file_type_subtype_supports_block(wdh->file_type_subtype, WTAP_BLOCK_NAME_RESOLUTION) == BLOCK_NOT_SUPPORTED)
return false;
wdh->addrinfo_lists = addrinfo_lists;
return true;
}
void
wtap_dump_discard_name_resolution(wtap_dumper *wdh)
{
/* As below for DSBs. */
if (wdh->nrbs_growing) {
/*
* Pretend we've written all of them.
*/
wdh->nrbs_growing_written = wdh->nrbs_growing->len;
}
}
void
wtap_dump_discard_decryption_secrets(wtap_dumper *wdh)
{
/*
* This doesn't free the data, as it might be pointed to
* from other structures; it merely marks all of them as
* having been written to the file, so that they don't
* get written by wtap_dump().
*
* XXX - our APIs for dealing with some metadata, such as
* resolved names, decryption secrets, and interface
* statistics is not very well oriented towards one-pass
* programs; this needs to be cleaned up. See bug 15502.
*/
if (wdh->dsbs_growing) {
/*
* Pretend we've written all of them.
*/
wdh->dsbs_growing_written = wdh->dsbs_growing->len;
}
}
void
wtap_dump_discard_sysdig_meta_events(wtap_dumper *wdh)
{
/* As above for DSBs. */
if (wdh->mevs_growing) {
/*
* Pretend we've written all of them.
*/
wdh->mevs_growing_written = wdh->mevs_growing->len;
}
}
/* internally open a file for writing (compressed or not) */
static WFILE_T
wtap_dump_file_open(wtap_dumper *wdh, const char *filename)
{
switch (wdh->compression_type) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
return gzwfile_open(filename);
#endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
return lz4wfile_open(filename);
#endif /* HAVE_LZ4FRAME_H */
default:
return ws_fopen(filename, "wb");
}
}
/* internally open a file for writing (compressed or not) */
static WFILE_T
wtap_dump_file_fdopen(wtap_dumper *wdh, int fd)
{
switch (wdh->compression_type) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
return gzwfile_fdopen(fd);
#endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
return lz4wfile_fdopen(fd);
#endif /* HAVE_LZ4FRAME_H */
default:
return ws_fdopen(fd, "wb");
}
}
/* internally writing raw bytes (compressed or not). Updates wdh->bytes_dumped on success */
bool
wtap_dump_file_write(wtap_dumper *wdh, const void *buf, size_t bufsize, int *err)
{
size_t nwritten;
switch (wdh->compression_type) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
nwritten = gzwfile_write((GZWFILE_T)wdh->fh, buf, (unsigned int) bufsize);
/*
* gzwfile_write() returns 0 on error.
*/
if (nwritten == 0) {
*err = gzwfile_geterr((GZWFILE_T)wdh->fh);
return false;
}
break;
#endif
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
nwritten = lz4wfile_write((LZ4WFILE_T)wdh->fh, buf, bufsize);
/*
* lz4wfile_write() returns 0 on error.
*/
if (nwritten == 0) {
*err = lz4wfile_geterr((LZ4WFILE_T)wdh->fh);
return false;
}
break;
#endif /* HAVE_LZ4FRAME_H */
default:
errno = WTAP_ERR_CANT_WRITE;
nwritten = fwrite(buf, 1, bufsize, (FILE *)wdh->fh);
/*
* At least according to the macOS man page,
* this can return a short count on an error.
*/
if (nwritten != bufsize) {
if (ferror((FILE *)wdh->fh))
*err = errno;
else
*err = WTAP_ERR_SHORT_WRITE;
return false;
}
}
wdh->bytes_dumped += bufsize;
return true;
}
/* internally close a file for writing (compressed or not) */
static int
wtap_dump_file_close(wtap_dumper *wdh)
{
switch (wdh->compression_type) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
return gzwfile_close((GZWFILE_T)wdh->fh);
#endif
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
return lz4wfile_close((LZ4WFILE_T)wdh->fh);
#endif /* HAVE_LZ4FRAME_H */
default:
return fclose((FILE *)wdh->fh);
}
}
int64_t
wtap_dump_file_seek(wtap_dumper *wdh, int64_t offset, int whence, int *err)
{
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) || defined (HAVE_LZ4FRAME_H)
if (wdh->compression_type != WTAP_UNCOMPRESSED) {
*err = WTAP_ERR_CANT_SEEK_COMPRESSED;
return -1;
} else
#endif
{
if (-1 == ws_fseek64((FILE *)wdh->fh, offset, whence)) {
*err = errno;
return -1;
} else
{
return 0;
}
}
}
int64_t
wtap_dump_file_tell(wtap_dumper *wdh, int *err)
{
int64_t rval;
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) || defined (HAVE_LZ4FRAME_H)
if (wdh->compression_type != WTAP_UNCOMPRESSED) {
*err = WTAP_ERR_CANT_SEEK_COMPRESSED;
return -1;
} else
#endif
{
if (-1 == (rval = ws_ftell64((FILE *)wdh->fh))) {
*err = errno;
return -1;
} else
{
return rval;
}
}
}
void
cleanup_open_routines(void)
{
unsigned i;
struct open_info *i_open;
if (open_routines != NULL && open_info_arr) {
for (i = 0, i_open = open_routines; i < open_info_arr->len; i++, i_open++) {
if (i_open->extensions != NULL)
g_strfreev(i_open->extensions_set);
}
g_array_free(open_info_arr, true);
open_info_arr = NULL;
}
}
/*
* Allow built-in file handlers (but *not* plugin file handlers!) to
* register a "backwards-compatibility" name and file type value, to
* put in the Lua wtap_filetypes table.
*
* This is only to be used as long as we have that table; new Lua
* code should use wtap_name_to_file_type_subtype() to look up
* file types by their name, just as C code should.
*
* The backwards-ccmpatibility names are the old WTAP_FILE_TYPE_SUBTYPE_
* #define name, with WTAP_FILE_TYPE_SUBTYPE_ removed.
*/
static GArray *backwards_compatibility_lua_names;
void
// NOLINTNEXTLINE(misc-no-recursion)
wtap_register_backwards_compatibility_lua_name(const char *name, int ft)
{
struct backwards_compatibiliity_lua_name entry;
/*
* Create the table if it doesn't already exist.
* Use the same size as we do for the file type/subtype table.
*/
if (backwards_compatibility_lua_names == NULL) {
backwards_compatibility_lua_names = g_array_sized_new(false,
true, sizeof(struct backwards_compatibiliity_lua_name),
wtap_module_count*2);
/*
* Extra backwards compatibility hack - add entries
* for time stamp precision values(!), as well as
* for "UNKNOWN" and types that don't yet register
* themselves.
*
* If new WS_TSPREC_ value are added, don't bother
* adding them to this table; any Lua program that
* would use them should use the wtap_tsprecs type.
*
* (Recursion: see "recursion".)
*/
wtap_register_backwards_compatibility_lua_name("TSPREC_SEC",
WTAP_TSPREC_SEC);
wtap_register_backwards_compatibility_lua_name("TSPREC_DSEC",
WTAP_TSPREC_100_MSEC);
wtap_register_backwards_compatibility_lua_name("TSPREC_CSEC",
WTAP_TSPREC_10_MSEC);
wtap_register_backwards_compatibility_lua_name("TSPREC_MSEC",
WTAP_TSPREC_MSEC);
wtap_register_backwards_compatibility_lua_name("TSPREC_USEC",
WTAP_TSPREC_USEC);
wtap_register_backwards_compatibility_lua_name("TSPREC_NSEC",
WTAP_TSPREC_NSEC);
wtap_register_backwards_compatibility_lua_name("UNKNOWN",
WTAP_FILE_TYPE_SUBTYPE_UNKNOWN);
}
entry.name = name;
entry.ft = ft;
g_array_append_val(backwards_compatibility_lua_names, entry);
}
const GArray *
get_backwards_compatibility_lua_table(void)
{
return backwards_compatibility_lua_names;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/