aboutsummaryrefslogtreecommitdiffstats
path: root/extcap
diff options
context:
space:
mode:
authorOdysseus Yang <wiresharkyyh@outlook.com>2020-12-02 09:05:11 +0000
committerAndersBroman <a.broman58@gmail.com>2020-12-02 09:05:11 +0000
commit2a5b34d8b02b1663237490eec5dfd8652a9bdb51 (patch)
tree5acf1640ab708e32a172a00763145e41e19c5089 /extcap
parentfe1f9475409f252b6ca2dccc71187868e168c74b (diff)
MBIM: Update dissector to support DLT_ETW
New link type DLT_ETW is added for write and read Event Trace on Windows. This change updates MBIM dissector to decode a MBIM message from a DLT_ETW packet.
Diffstat (limited to 'extcap')
-rw-r--r--extcap/CMakeLists.txt27
-rw-r--r--extcap/etl.c438
-rw-r--r--extcap/etl.h42
-rw-r--r--extcap/etw_message.c416
-rw-r--r--extcap/etw_message.h60
-rw-r--r--extcap/etwdump.c262
6 files changed, 1245 insertions, 0 deletions
diff --git a/extcap/CMakeLists.txt b/extcap/CMakeLists.txt
index 39389aced1..f59fd55838 100644
--- a/extcap/CMakeLists.txt
+++ b/extcap/CMakeLists.txt
@@ -246,6 +246,33 @@ if(BUILD_randpktdump)
add_dependencies(extcaps randpktdump)
endif()
+
+if(BUILD_etwdump AND WIN32)
+ set(etwdump_LIBS
+ wiretap
+ wsutil
+ tdh
+ ${GLIB2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${WIN_WS2_32_LIBRARY}
+ )
+ set(etwdump_FILES
+ $<TARGET_OBJECTS:cli_main>
+ $<TARGET_OBJECTS:extcap-base>
+ etwdump.c
+ etl.c
+ etw_message.c
+ )
+
+ set_executable_resources(etwdump "etwdump")
+ add_executable(etwdump ${etwdump_FILES})
+ set_extcap_executable_properties(etwdump)
+ target_link_libraries(etwdump ${etwdump_LIBS})
+ install(TARGETS etwdump RUNTIME DESTINATION ${EXTCAP_INSTALL_LIBDIR})
+ add_dependencies(extcaps etwdump)
+endif()
+
if(BUILD_sdjournal AND SYSTEMD_FOUND)
set(sdjournal_LIBS
wiretap
diff --git a/extcap/etl.c b/extcap/etl.c
new file mode 100644
index 0000000000..0aac40751b
--- /dev/null
+++ b/extcap/etl.c
@@ -0,0 +1,438 @@
+/* etl.c
+ *
+ * Copyright 2020, Odysseus Yang
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ /*
+ https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
+ */
+
+#include "config.h"
+#include "etl.h"
+
+#include "etw_message.h"
+
+#define MAX_PACKET_SIZE 0xFFFF
+#define G_NSEC_PER_SEC 1000000000
+#define ADD_OFFSET_TO_POINTER(buffer, offset) (((PBYTE)buffer) + offset)
+#define ROUND_UP_COUNT(Count,Pow2) \
+ ( ((Count)+(Pow2)-1) & (~(((int)(Pow2))-1)) )
+
+extern int g_include_undecidable_event;
+
+const GUID mbb_provider = { 0xA42FE227, 0xA7BF, 0x4483, {0xA5, 0x02, 0x6B, 0xCD, 0xA4, 0x28, 0xCD, 0x96} };
+
+EXTERN_C const GUID DECLSPEC_SELECTANY EventTraceGuid = { 0x68fdd900, 0x4a3e, 0x11d1, {0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3} };
+EXTERN_C const GUID DECLSPEC_SELECTANY ImageIdGuid = { 0xb3e675d7, 0x2554, 0x4f18, { 0x83, 0xb, 0x27, 0x62, 0x73, 0x25, 0x60, 0xde } };
+EXTERN_C const GUID DECLSPEC_SELECTANY SystemConfigExGuid = { 0x9b79ee91, 0xb5fd, 0x41c0, { 0xa2, 0x43, 0x42, 0x48, 0xe2, 0x66, 0xe9, 0xd0 } };
+EXTERN_C const GUID DECLSPEC_SELECTANY EventMetadataGuid = { 0xbbccf6c1, 0x6cd1, 0x48c4, {0x80, 0xff, 0x83, 0x94, 0x82, 0xe3, 0x76, 0x71 } };
+
+typedef struct _WTAP_ETL_RECORD {
+ EVENT_HEADER EventHeader; // Event header
+ ETW_BUFFER_CONTEXT BufferContext; // Buffer context
+ ULONG UserDataLength;
+ ULONG MessageLength;
+ ULONG ProviderLength;
+} WTAP_ETL_RECORD;
+
+static gchar g_err_info[FILENAME_MAX] = { 0 };
+static int g_err = ERROR_SUCCESS;
+static wtap_dumper* g_pdh = NULL;
+extern ULONGLONG g_num_events;
+
+static void WINAPI event_callback(PEVENT_RECORD ev);
+void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
+void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
+void etw_dump_write_event_head_only(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
+void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound);
+wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info);
+
+wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filename, int* err, gchar** err_info)
+{
+ EVENT_TRACE_LOGFILE log_file;
+ TRACEHANDLE trace_handle = INVALID_PROCESSTRACE_HANDLE;
+ WCHAR w_etl_filename[FILENAME_MAX] = { 0 };
+ WCHAR w_pcapng_filename[FILENAME_MAX] = { 0 };
+ ULONG trace_error;
+ wtap_open_return_val returnVal = WTAP_OPEN_MINE;
+
+ SecureZeroMemory(g_err_info, FILENAME_MAX);
+ g_err = ERROR_SUCCESS;
+ g_num_events = 0;
+
+ /* do/while(FALSE) is used to jump out of loop so no complex nested if/else is needed */
+ do
+ {
+ mbstowcs(w_etl_filename, etl_filename, FILENAME_MAX);
+ mbstowcs(w_pcapng_filename, pcapng_filename, FILENAME_MAX);
+
+ SecureZeroMemory(&log_file, sizeof(log_file));
+ log_file.LogFileName = w_etl_filename;
+ log_file.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD;
+ log_file.EventRecordCallback = event_callback;
+ log_file.Context = NULL;
+
+ trace_handle = OpenTrace(&log_file);
+ if (trace_handle == INVALID_PROCESSTRACE_HANDLE) {
+ *err_info = g_strdup_printf("OpenTrace failed with %u", err);
+ returnVal = WTAP_OPEN_NOT_MINE;
+ break;
+ }
+
+ g_pdh = etw_dump_open(pcapng_filename, err, err_info);
+ if (g_pdh == NULL)
+ {
+ returnVal = WTAP_OPEN_ERROR;
+ break;
+ }
+
+ trace_error = ProcessTrace(&trace_handle, 1, 0, 0);
+ if (trace_error != ERROR_SUCCESS) {
+ returnVal = WTAP_OPEN_ERROR;
+ break;
+ }
+
+ if (g_err != ERROR_SUCCESS)
+ {
+ *err = g_err;
+ *err_info = g_strdup(g_err_info);
+ returnVal = WTAP_OPEN_ERROR;
+ break;
+ }
+
+ if (!g_num_events) {
+ *err_info = g_strdup_printf("Didn't find any etw event");
+ returnVal = WTAP_OPEN_NOT_MINE;
+ break;
+ }
+ } while (FALSE);
+
+ if (trace_handle != INVALID_PROCESSTRACE_HANDLE)
+ {
+ CloseTrace(trace_handle);
+ }
+ if (g_pdh != NULL)
+ {
+ if (!wtap_dump_close(g_pdh, err, err_info))
+ {
+ returnVal = WTAP_OPEN_ERROR;
+ }
+ }
+ return returnVal;
+}
+
+static void WINAPI event_callback(PEVENT_RECORD ev)
+{
+ ULARGE_INTEGER timestamp;
+ g_num_events++;
+ /*
+ * 100ns since 1/1/1601 -> usec since 1/1/1970.
+ * The offset of 11644473600 seconds can be calculated with a couple of calls to SystemTimeToFileTime.
+ */
+ timestamp.QuadPart = (ev->EventHeader.TimeStamp.QuadPart / 10) - 11644473600000000ll;
+
+ /* Write OPN events that needs mbim sub dissector */
+ if (IsEqualGUID(&ev->EventHeader.ProviderId, &mbb_provider))
+ {
+ etw_dump_write_opn_event(ev, timestamp);
+ }
+ /* TODO:: You can write events from other providers that needs specific sub dissector */
+#if 0
+ else if (IsEqualGUID(&ev->EventHeader.ProviderId, &ndis_packcapture_provider))
+ {
+ etw_dump_write_packet_event(ev, timestamp);
+ }
+#endif
+ /* Write any event form other providers other than above */
+ else
+ {
+ etw_dump_write_general_event(ev, timestamp);
+ }
+}
+
+wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info)
+{
+ wtap_dump_params params = { 0 };
+ GArray* shb_hdrs = NULL;
+ wtap_block_t shb_hdr;
+ wtapng_iface_descriptions_t* idb_info;
+ GArray* idb_datas;
+ wtap_block_t idb_data;
+ wtapng_if_descr_mandatory_t* descr_mand;
+
+ wtap_dumper* pdh = NULL;
+
+ shb_hdrs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ shb_hdr = wtap_block_create(WTAP_BLOCK_NG_SECTION);
+ g_array_append_val(shb_hdrs, shb_hdr);
+
+ /* In the future, may create multiple WTAP_BLOCK_IF_DESCR separately for IP packet */
+ idb_info = g_new(wtapng_iface_descriptions_t, 1);
+ idb_datas = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ idb_data = wtap_block_create(WTAP_BLOCK_IF_DESCR);
+ descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb_data);
+ descr_mand->tsprecision = WTAP_TSPREC_USEC;
+ descr_mand->wtap_encap = WTAP_ENCAP_ETL;
+ /* Timestamp for each pcapng packet is usec units, so time_units_per_second need be set to 10^6 */
+ descr_mand->time_units_per_second = G_USEC_PER_SEC;
+ g_array_append_val(idb_datas, idb_data);
+ idb_info->interface_data = idb_datas;
+
+ params.encap = WTAP_ENCAP_ETL;
+ params.snaplen = 0;
+ params.tsprec = WTAP_TSPREC_USEC;
+ params.shb_hdrs = shb_hdrs;
+ params.idb_inf = idb_info;
+
+ pdh = wtap_dump_open(pcapng_filename, WTAP_FILE_TYPE_SUBTYPE_PCAPNG, WTAP_UNCOMPRESSED, &params, err, err_info);
+
+ if (shb_hdrs)
+ {
+ wtap_block_array_free(shb_hdrs);
+ }
+ if (params.idb_inf)
+ {
+ if (params.idb_inf->interface_data)
+ {
+ wtap_block_array_free(params.idb_inf->interface_data);
+ }
+ g_free(params.idb_inf);
+ params.idb_inf = NULL;
+ }
+
+ return pdh;
+}
+
+ULONG wtap_etl_record_buffer_init(WTAP_ETL_RECORD** out_etl_record, PEVENT_RECORD ev, BOOLEAN include_user_data, WCHAR* message, WCHAR* provider_name)
+{
+ ULONG total_packet_length = sizeof(WTAP_ETL_RECORD);
+ WTAP_ETL_RECORD* etl_record = NULL;
+ ULONG user_data_length = 0;
+ ULONG user_data_offset = 0;
+ ULONG message_offset = 0;
+ ULONG provider_name_offset = 0;
+ ULONG message_length = 0;
+ ULONG provider_name_length = 0;
+
+ if (include_user_data)
+ {
+ if (ev->UserDataLength < MAX_PACKET_SIZE)
+ {
+ user_data_length = ev->UserDataLength;
+ }
+ else
+ {
+ user_data_length = MAX_PACKET_SIZE;
+ }
+ user_data_offset = sizeof(WTAP_ETL_RECORD);
+ total_packet_length += ROUND_UP_COUNT(user_data_length, sizeof(LONG));
+ }
+ if (message && message[0] != L'\0')
+ {
+ message_offset = total_packet_length;
+ message_length = (ULONG)((wcslen(message) + 1) * sizeof(WCHAR));
+ total_packet_length += ROUND_UP_COUNT(message_length, sizeof(LONG));
+ }
+ if (provider_name && provider_name[0] != L'\0')
+ {
+ provider_name_offset = total_packet_length;
+ provider_name_length = (ULONG)((wcslen(provider_name) + 1) * sizeof(WCHAR));
+ total_packet_length += ROUND_UP_COUNT(provider_name_length, sizeof(LONG));
+ }
+
+ etl_record = g_malloc(total_packet_length);
+ SecureZeroMemory(etl_record, total_packet_length);
+ etl_record->EventHeader = ev->EventHeader;
+ etl_record->BufferContext = ev->BufferContext;
+ etl_record->UserDataLength = user_data_length;
+ etl_record->MessageLength = message_length;
+ etl_record->ProviderLength = provider_name_length;
+
+ if (user_data_offset)
+ {
+ memcpy(ADD_OFFSET_TO_POINTER(etl_record, user_data_offset), ev->UserData, user_data_length);
+ }
+ if (message_offset)
+ {
+ memcpy(ADD_OFFSET_TO_POINTER(etl_record, message_offset), message, message_length);
+ }
+ if (provider_name_offset)
+ {
+ memcpy(ADD_OFFSET_TO_POINTER(etl_record, provider_name_offset), provider_name, provider_name_length);
+ }
+
+ *out_etl_record = etl_record;
+ return total_packet_length;
+}
+
+void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound)
+{
+ gchar* err_info;
+ int err;
+ wtap_rec rec = { 0 };
+
+ wtap_rec_init(&rec);
+ rec.rec_header.packet_header.caplen = total_packet_length;
+ rec.rec_header.packet_header.len = total_packet_length;
+ rec.rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETL;
+ rec.presence_flags = rec.presence_flags | WTAP_HAS_PACK_FLAGS;
+ rec.rec_header.packet_header.pack_flags = is_inbound ? 1 : 2;
+ /* Convert usec of the timestamp into nstime_t */
+ rec.ts.secs = (time_t)(timestamp.QuadPart / G_USEC_PER_SEC);
+ rec.ts.nsecs = (int)(((timestamp.QuadPart % G_USEC_PER_SEC) * G_NSEC_PER_SEC) / G_USEC_PER_SEC);
+
+ /* and save the packet */
+ if (!wtap_dump(g_pdh, &rec, (guint8*)etl_record, &err, &err_info)) {
+ g_err = err;
+ sprintf_s(g_err_info, sizeof(g_err_info), "wtap_dump failed, %s", err_info);
+ g_free(err_info);
+ }
+ wtap_rec_cleanup(&rec);
+}
+
+void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
+{
+ WTAP_ETL_RECORD* etl_record = NULL;
+ ULONG total_packet_length = 0;
+ BOOLEAN is_inbound = FALSE;
+ /* 0x80000000 mask the function to host message */
+ is_inbound = ((*(INT32*)(ev->UserData)) & 0x80000000) ? TRUE : FALSE;
+ total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, TRUE, NULL, NULL);
+ wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, is_inbound);
+ g_free(etl_record);
+}
+
+void etw_dump_write_event_head_only(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
+{
+ WTAP_ETL_RECORD* etl_record = NULL;
+ ULONG total_packet_length = 0;
+ total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, FALSE, NULL, NULL);
+ wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, FALSE);
+ g_free(etl_record);
+}
+
+void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
+{
+ PTRACE_EVENT_INFO pInfo = NULL;
+ PBYTE pUserData = NULL;
+ PBYTE pEndOfUserData = NULL;
+ DWORD PointerSize = 0;
+ PROPERTY_KEY_VALUE* prop_arr = NULL;
+ DWORD dwTopLevelPropertyCount = 0;
+ DWORD dwSizeofArray = 0;
+ WCHAR wszMessageBuffer[MAX_LOG_LINE_LENGTH] = { 0 };
+ WCHAR formatMessage[MAX_LOG_LINE_LENGTH] = { 0 };
+
+ WTAP_ETL_RECORD* etl_record = NULL;
+ ULONG total_packet_length = 0;
+ BOOLEAN is_message_dumped = FALSE;
+
+ do
+ {
+ /* Skip EventTrace events */
+ if (ev->EventHeader.Flags & EVENT_HEADER_FLAG_CLASSIC_HEADER &&
+ IsEqualGUID(&ev->EventHeader.ProviderId, &EventTraceGuid))
+ {
+ /*
+ * The first event in every ETL file contains the data from the file header.
+ * This is the same data as was returned in the EVENT_TRACE_LOGFILEW by
+ * OpenTrace. Since we've already seen this information, we'll skip this
+ * event.
+ */
+ break;
+ }
+
+ /* Skip events injected by the XPerf tracemerger - they will never be decodable */
+ if (IsEqualGUID(&ev->EventHeader.ProviderId, &ImageIdGuid) ||
+ IsEqualGUID(&ev->EventHeader.ProviderId, &SystemConfigExGuid) ||
+ IsEqualGUID(&ev->EventHeader.ProviderId, &EventMetadataGuid))
+ {
+ break;
+ }
+
+ if (!get_event_information(ev, &pInfo))
+ {
+ break;
+ }
+
+ /* Skip those events without format message since most of them need special logic to decode like NDIS-PackCapture */
+ if (pInfo->EventMessageOffset <= 0)
+ {
+ break;
+ }
+
+ if (EVENT_HEADER_FLAG_32_BIT_HEADER == (ev->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER))
+ {
+ PointerSize = 4;
+ }
+ else
+ {
+ PointerSize = 8;
+ }
+
+ pUserData = (PBYTE)ev->UserData;
+ pEndOfUserData = (PBYTE)ev->UserData + ev->UserDataLength;
+
+ dwTopLevelPropertyCount = pInfo->TopLevelPropertyCount;
+ if (dwTopLevelPropertyCount > 0)
+ {
+ prop_arr = g_malloc(sizeof(PROPERTY_KEY_VALUE) * dwTopLevelPropertyCount);
+ dwSizeofArray = dwTopLevelPropertyCount * sizeof(PROPERTY_KEY_VALUE);
+ SecureZeroMemory(prop_arr, dwSizeofArray);
+ }
+
+ StringCbCopy(formatMessage, MAX_LOG_LINE_LENGTH, (LPWSTR)ADD_OFFSET_TO_POINTER(pInfo, pInfo->EventMessageOffset));
+
+ for (USHORT i = 0; i < dwTopLevelPropertyCount; i++)
+ {
+ pUserData = extract_properties(ev, pInfo, PointerSize, i, pUserData, pEndOfUserData, &prop_arr[i]);
+ if (NULL == pUserData)
+ {
+ break;
+ }
+ }
+
+ format_message(formatMessage, prop_arr, dwTopLevelPropertyCount, wszMessageBuffer, sizeof(wszMessageBuffer));
+
+ total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, FALSE, wszMessageBuffer, (WCHAR*)ADD_OFFSET_TO_POINTER(pInfo, pInfo->ProviderNameOffset));
+ wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, FALSE);
+ g_free(etl_record);
+
+ is_message_dumped = TRUE;
+ } while (FALSE);
+
+ if (NULL != prop_arr)
+ {
+ g_free(prop_arr);
+ prop_arr = NULL;
+ }
+ if (NULL != pInfo)
+ {
+ g_free(pInfo);
+ pInfo = NULL;
+ }
+
+ if (!is_message_dumped && g_include_undecidable_event)
+ {
+ etw_dump_write_event_head_only(ev, timestamp);
+ }
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/extcap/etl.h b/extcap/etl.h
new file mode 100644
index 0000000000..7410de3a85
--- /dev/null
+++ b/extcap/etl.h
@@ -0,0 +1,42 @@
+/* etl.h
+*
+ * Copyright 2020, Odysseus Yang
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_ETL_H__
+#define __W_ETL_H__
+
+#include "wiretap/wtap.h"
+#include "ws_symbol_export.h"
+#include "wiretap/wtap-int.h"
+
+#include <glib.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <strsafe.h>
+#include <tdh.h>
+#include <guiddef.h>
+
+wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filename, int* err, gchar** err_info);
+
+#endif
+
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/extcap/etw_message.c b/extcap/etw_message.c
new file mode 100644
index 0000000000..fa74d8b637
--- /dev/null
+++ b/extcap/etw_message.c
@@ -0,0 +1,416 @@
+/* etw_message.h
+ *
+ * Copyright 2020, Odysseus Yang
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "etw_message.h"
+ULONGLONG g_num_events = 0;
+
+VOID format_message(WCHAR* lpszMessage, PROPERTY_KEY_VALUE* propArray, DWORD dwPropertyCount, WCHAR* lpszOutBuffer, DWORD dwOutBufferCount)
+{
+ DWORD startLoc = 0;
+ int percent_loc = 0;
+
+ for (int i = 0; lpszMessage[i] != L'\0';)
+ {
+ if (lpszMessage[i] != L'%')
+ {
+ i++;
+ continue;
+ }
+ if (lpszMessage[i + 1] == '%')
+ {
+ i += 2;
+ continue;
+ }
+
+ percent_loc = i;
+ i++;
+
+ if (iswdigit(lpszMessage[i]))
+ {
+ DWORD dwDigitalCount = 0;
+ WCHAR smallBuffer[MAX_SMALL_BUFFER] = { 0 };
+ while (iswdigit(lpszMessage[i]))
+ {
+ if (dwDigitalCount < (MAX_SMALL_BUFFER - 1))
+ {
+ smallBuffer[dwDigitalCount] = lpszMessage[i];
+ }
+ dwDigitalCount++;
+ i++;
+ }
+
+ /* We are not parsing this */
+ if (dwDigitalCount >= (MAX_SMALL_BUFFER - 1))
+ {
+ continue;
+ }
+ DWORD num = _wtoi(smallBuffer);
+ /* We are not parsing this */
+ if (num == 0 || num > dwPropertyCount || propArray[num - 1].value[0] == L'\0')
+ {
+ continue;
+ }
+
+ if (lpszMessage[i] == L'!' && lpszMessage[i + 1] == L'S' && lpszMessage[i + 2] == L'!')
+ {
+ i += 3;
+ }
+
+ /* We have everything */
+ lpszMessage[percent_loc] = L'\0';
+ StringCbCat(lpszOutBuffer, dwOutBufferCount, lpszMessage + startLoc);
+ StringCbCat(lpszOutBuffer, dwOutBufferCount, propArray[num - 1].value);
+ startLoc = i;
+ continue; // for
+ }
+ }
+ StringCbCat(lpszOutBuffer, dwOutBufferCount, lpszMessage + startLoc);
+}
+
+/*
+* Get the length of the property data. For MOF-based events, the size is inferred from the data type
+* of the property. For manifest-based events, the property can specify the size of the property value
+* using the length attribute. The length attribue can specify the size directly or specify the name
+* of another property in the event data that contains the size. If the property does not include the
+* length attribute, the size is inferred from the data type. The length will be zero for variable
+* length, null-terminated strings and structures.
+*/
+DWORD GetPropertyLength(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT PropertyLength)
+{
+ DWORD status = ERROR_SUCCESS;
+ PROPERTY_DATA_DESCRIPTOR DataDescriptor = { 0 };
+ DWORD PropertySize = 0;
+
+ /*
+ * If the property is a binary blob and is defined in a manifest, the property can
+ * specify the blob's size or it can point to another property that defines the
+ * blob's size. The PropertyParamLength flag tells you where the blob's size is defined.
+ */
+ if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamLength) == PropertyParamLength)
+ {
+ DWORD Length = 0; // Expects the length to be defined by a UINT16 or UINT32
+ DWORD j = pInfo->EventPropertyInfoArray[i].lengthPropertyIndex;
+ DataDescriptor.PropertyName = ((ULONGLONG)(pInfo)+(ULONGLONG)pInfo->EventPropertyInfoArray[j].NameOffset);
+ DataDescriptor.ArrayIndex = ULONG_MAX;
+ status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize);
+ status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Length);
+ *PropertyLength = (USHORT)Length;
+ }
+ else
+ {
+ if (pInfo->EventPropertyInfoArray[i].length > 0)
+ {
+ *PropertyLength = pInfo->EventPropertyInfoArray[i].length;
+ }
+ else
+ {
+ /*
+ * If the property is a binary blob and is defined in a MOF class, the extension
+ * qualifier is used to determine the size of the blob. However, if the extension
+ * is IPAddrV6, you must set the PropertyLength variable yourself because the
+ * EVENT_PROPERTY_INFO.length field will be zero.
+ */
+ if (TDH_INTYPE_BINARY == pInfo->EventPropertyInfoArray[i].nonStructType.InType &&
+ TDH_OUTTYPE_IPV6 == pInfo->EventPropertyInfoArray[i].nonStructType.OutType)
+ {
+ *PropertyLength = (USHORT)sizeof(IN6_ADDR);
+ }
+ else if (TDH_INTYPE_UNICODESTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType ||
+ TDH_INTYPE_ANSISTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType ||
+ (pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct)
+ {
+ *PropertyLength = pInfo->EventPropertyInfoArray[i].length;
+ }
+ else
+ {
+ g_debug("Event %d Unexpected length of 0 for intype %d and outtype %d", g_num_events,
+ pInfo->EventPropertyInfoArray[i].nonStructType.InType,
+ pInfo->EventPropertyInfoArray[i].nonStructType.OutType);
+
+ status = ERROR_EVT_INVALID_EVENT_DATA;
+ goto cleanup;
+ }
+ }
+ }
+cleanup:
+ return status;
+}
+
+DWORD GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT ArraySize)
+{
+ DWORD status = ERROR_SUCCESS;
+ PROPERTY_DATA_DESCRIPTOR DataDescriptor = { 0 };
+ DWORD PropertySize = 0;
+
+ if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == PropertyParamCount)
+ {
+ /* Expects the count to be defined by a UINT16 or UINT32 */
+ DWORD Count = 0;
+ DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex;
+ DataDescriptor.PropertyName = ((ULONGLONG)(pInfo)+(ULONGLONG)(pInfo->EventPropertyInfoArray[j].NameOffset));
+ DataDescriptor.ArrayIndex = ULONG_MAX;
+ status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize);
+ status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Count);
+ *ArraySize = (USHORT)Count;
+ }
+ else
+ {
+ *ArraySize = pInfo->EventPropertyInfoArray[i].count;
+ }
+ return status;
+}
+
+DWORD GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, PEVENT_MAP_INFO* pMapInfo)
+{
+ DWORD status = ERROR_SUCCESS;
+ DWORD MapSize = 0;
+
+ /* Retrieve the required buffer size for the map info. */
+ status = TdhGetEventMapInformation(pEvent, pMapName, *pMapInfo, &MapSize);
+ if (ERROR_INSUFFICIENT_BUFFER == status)
+ {
+ *pMapInfo = (PEVENT_MAP_INFO)g_malloc(MapSize);
+ if (*pMapInfo == NULL)
+ {
+ status = ERROR_OUTOFMEMORY;
+ goto cleanup;
+ }
+ /* Retrieve the map info. */
+ status = TdhGetEventMapInformation(pEvent, pMapName, *pMapInfo, &MapSize);
+ }
+
+ if (ERROR_NOT_FOUND == status)
+ {
+ /* This case is okay. */
+ status = ERROR_SUCCESS;
+ }
+
+cleanup:
+
+ return status;
+}
+
+
+PBYTE extract_properties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData, PROPERTY_KEY_VALUE* pExtract)
+{
+ TDHSTATUS status = ERROR_SUCCESS;
+ USHORT PropertyLength = 0;
+ USHORT UserDataConsumed = 0;
+ /* Last member of a structure */
+ DWORD LastMember = 0;
+ USHORT ArraySize = 0;
+ PEVENT_MAP_INFO pMapInfo = NULL;
+ WCHAR formatted_data[MAX_LOG_LINE_LENGTH];
+ DWORD formatted_data_size = sizeof(formatted_data);
+ LPWSTR oversize_formatted_data = NULL;
+
+ do
+ {
+ StringCbCopy(pExtract->key, sizeof(pExtract->key), (PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset));
+ /* Get the length of the property. */
+ status = GetPropertyLength(pEvent, pInfo, i, &PropertyLength);
+ if (ERROR_SUCCESS != status)
+ {
+ StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetPropertyLength failed 0x%x", pExtract->key, status);
+ break;
+ }
+
+ /* Get the size of the array if the property is an array. */
+ status = GetArraySize(pEvent, pInfo, i, &ArraySize);
+ if (ERROR_SUCCESS != status)
+ {
+ StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetArraySize failed 0x%x", pExtract->key, status);
+ break;
+ }
+
+ /* Add [] for an array property */
+ if (ArraySize > 1)
+ {
+ StringCbCat(pExtract->value, sizeof(pExtract->value), L"[");
+ }
+
+ for (USHORT k = 0; k < ArraySize; k++)
+ {
+ /* Add array item separator "," */
+ if (k > 0)
+ {
+ StringCbCat(pExtract->value, sizeof(pExtract->value), L",");
+ }
+ /* If the property is a structure, print the members of the structure. */
+ if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct)
+ {
+ /* Add {} for an array property */
+ StringCbCat(pExtract->value, sizeof(pExtract->value), L"{");
+ /* Add struct member separator ";" */
+ if (k > 0)
+ {
+ StringCbCat(pExtract->value, sizeof(pExtract->value), L";");
+ }
+ LastMember = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex +
+ pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers;
+
+ for (USHORT j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++)
+ {
+ pUserData = extract_properties(pEvent, pInfo, PointerSize, j, pUserData, pEndOfUserData, pExtract);
+ if (NULL == pUserData)
+ {
+ StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: extract_properties of member %d failed 0x%x", pExtract->key, j, status);
+ break;
+ }
+ }
+ StringCbCat(pExtract->value, sizeof(pExtract->value), L"}");
+ }
+ else
+ {
+ /* Get the name/value mapping only at the first time if the property specifies a value map. */
+ if (pMapInfo == NULL)
+ {
+ status = GetMapInfo(pEvent,
+ (PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset),
+ &pMapInfo);
+
+ if (ERROR_SUCCESS != status)
+ {
+ StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetMapInfo failed 0x%x", pExtract->key, status);
+ break;
+ }
+ }
+
+ /* Get the size of the buffer required for the formatted data. */
+
+ status = TdhFormatProperty(
+ pInfo,
+ pMapInfo,
+ PointerSize,
+ pInfo->EventPropertyInfoArray[i].nonStructType.InType,
+ pInfo->EventPropertyInfoArray[i].nonStructType.OutType,
+ PropertyLength,
+ (USHORT)(pEndOfUserData - pUserData),
+ pUserData,
+ &formatted_data_size,
+ formatted_data,
+ &UserDataConsumed);
+
+ if (ERROR_INSUFFICIENT_BUFFER == status)
+ {
+ if (oversize_formatted_data)
+ {
+ g_free(oversize_formatted_data);
+ oversize_formatted_data = NULL;
+ }
+
+ oversize_formatted_data = (LPWSTR)g_malloc(formatted_data_size);
+ if (oversize_formatted_data == NULL)
+ {
+ status = ERROR_OUTOFMEMORY;
+ StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: Allocate FormattedData memory (size %d) for array item %d failed 0x%x", pExtract->key, formatted_data_size, k, status);
+ break;
+ }
+
+ /* Retrieve the formatted data. */
+ status = TdhFormatProperty(
+ pInfo,
+ pMapInfo,
+ PointerSize,
+ pInfo->EventPropertyInfoArray[i].nonStructType.InType,
+ pInfo->EventPropertyInfoArray[i].nonStructType.OutType,
+ PropertyLength,
+ (USHORT)(pEndOfUserData - pUserData),
+ pUserData,
+ &formatted_data_size,
+ oversize_formatted_data,
+ &UserDataConsumed);
+ }
+
+ if (ERROR_SUCCESS == status)
+ {
+ if (formatted_data_size > sizeof(formatted_data) && oversize_formatted_data != NULL)
+ {
+ /* Any oversize FormattedData will be truncated */
+ StringCbCat(pExtract->value, sizeof(pExtract->value), oversize_formatted_data);
+ }
+ else
+ {
+ StringCbCat(pExtract->value, sizeof(pExtract->value), formatted_data);
+ }
+ pUserData += UserDataConsumed;
+ }
+ else
+ {
+ StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: TdhFormatProperty for array item %d failed 0x%x", pExtract->key, k, status);
+ break;
+ }
+ }
+ }
+ /* Add [] for an array property */
+ if (ArraySize > 1)
+ {
+ StringCbCat(pExtract->value, sizeof(pExtract->value), L"]");
+ }
+ } while (FALSE);
+
+ if (oversize_formatted_data)
+ {
+ g_free(oversize_formatted_data);
+ oversize_formatted_data = NULL;
+ }
+ if (pMapInfo)
+ {
+ g_free(pMapInfo);
+ pMapInfo = NULL;
+ }
+
+ return (ERROR_SUCCESS == status) ? pUserData : NULL;
+}
+
+
+BOOL get_event_information(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO* pInfo)
+{
+ BOOL bReturn = FALSE;
+ DWORD status;
+ DWORD BufferSize = 0;
+
+ /* Retrieve the required buffer size for the event metadata. */
+ status = TdhGetEventInformation(pEvent, 0, NULL, *pInfo, &BufferSize);
+ if (ERROR_INSUFFICIENT_BUFFER == status)
+ {
+ *pInfo = (TRACE_EVENT_INFO*)g_malloc(BufferSize);
+ if (*pInfo == NULL)
+ {
+ g_debug("Event %d GetEventInformation Failed to allocate memory for event info (size=%lu).", g_num_events, BufferSize);
+ goto Exit;
+ }
+ /* Retrieve the event metadata. */
+ status = TdhGetEventInformation(pEvent, 0, NULL, *pInfo, &BufferSize);
+ }
+
+ if (ERROR_SUCCESS != status)
+ {
+ goto Exit;
+ }
+ bReturn = TRUE;
+Exit:
+
+ return bReturn;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/extcap/etw_message.h b/extcap/etw_message.h
new file mode 100644
index 0000000000..b88e320233
--- /dev/null
+++ b/extcap/etw_message.h
@@ -0,0 +1,60 @@
+/* etl.h
+*
+ * Copyright 2020, Odysseus Yang
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_ETW_MESSAGE_H__
+#define __W_ETW_MESSAGE_H__
+
+#include <glib.h>
+#include "config.h"
+
+#include <windows.h>
+#include <SDKDDKVer.h>
+#include <strsafe.h>
+#include <evntcons.h>
+#include <tdh.h>
+#include <stdlib.h>
+
+#define MAX_SMALL_BUFFER 4
+#define MAX_LOG_LINE_LENGTH 1024
+#define MAX_KEY_LENGTH 64
+
+typedef struct Property_Key_Value
+{
+ WCHAR key[MAX_KEY_LENGTH];
+ WCHAR value[MAX_LOG_LINE_LENGTH];
+} PROPERTY_KEY_VALUE;
+
+typedef struct in6_addr {
+ union {
+ UCHAR Byte[16];
+ USHORT Word[8];
+ } u;
+} IN6_ADDR, * PIN6_ADDR, FAR* LPIN6_ADDR;
+
+VOID format_message(WCHAR* lpszMessage, PROPERTY_KEY_VALUE* propArray, DWORD dwPropertyCount, WCHAR* lpszOutBuffer, DWORD dwOutBufferCount);
+BOOL get_event_information(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO* pInfo);
+PBYTE extract_properties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData, PROPERTY_KEY_VALUE* pExtract);
+
+#endif
+
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/extcap/etwdump.c b/extcap/etwdump.c
new file mode 100644
index 0000000000..dcc0e293cf
--- /dev/null
+++ b/extcap/etwdump.c
@@ -0,0 +1,262 @@
+/* etwdump.c
+ * etwdump is an extcap tool used to dump etw to pcapng
+ *
+ * Copyright 2020, Odysseus Yang
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "extcap-base.h"
+
+#include <wsutil/strtoi.h>
+#include <wsutil/filesystem.h>
+#include <wsutil/privileges.h>
+#include <wsutil/please_report_bug.h>
+
+#include <cli_main.h>
+#include <ui/cmdarg_err.h>
+#include "etl.h"
+
+/* extcap-interface has to be unique, or it may use wrong option output by a different extcapbin */
+#define ETW_EXTCAP_INTERFACE "etwdump"
+#define ETWDUMP_VERSION_MAJOR "1"
+#define ETWDUMP_VERSION_MINOR "0"
+#define ETWDUMP_VERSION_RELEASE "0"
+
+enum {
+ EXTCAP_BASE_OPTIONS_ENUM,
+ OPT_HELP,
+ OPT_VERSION,
+ OPT_INCLUDE_UNDECIDABLE_EVENT,
+ OPT_ETLFILE
+};
+
+static struct option longopts[] = {
+ EXTCAP_BASE_OPTIONS,
+ { "help", no_argument, NULL, OPT_HELP},
+ { "version", no_argument, NULL, OPT_VERSION},
+ { "iue", optional_argument, NULL, OPT_INCLUDE_UNDECIDABLE_EVENT},
+ { "etlfile", required_argument, NULL, OPT_ETLFILE},
+ { 0, 0, 0, 0 }
+};
+
+int g_include_undecidable_event = FALSE;
+
+static void help(extcap_parameters* extcap_conf)
+{
+ extcap_help_print(extcap_conf);
+}
+
+static int list_config(char* interface)
+{
+ unsigned inc = 0;
+
+ if (!interface) {
+ g_warning("No interface specified.");
+ return EXIT_FAILURE;
+ }
+
+ if (g_strcmp0(interface, ETW_EXTCAP_INTERFACE)) {
+ g_warning("Interface must be %s", ETW_EXTCAP_INTERFACE);
+ return EXIT_FAILURE;
+ }
+ /* Saved for later live capture support */
+#if 0
+ printf("arg {number=%u}{call=--type}{display=Capture type}"
+ "{type=selector}{tooltip=Choose the type of capture}{group=Capture}\n",
+ inc);
+ printf("value {arg=%u}{value=etl}{display=From a etl file}\n", inc);
+ printf("value {arg=%u}{value=live}{display=From a live session}\n", inc);
+ inc++;
+#endif
+ /*
+ * The undecidable events are those that either don't have sub-dissector or don't have anthing meaningful to display except for the EVENT_HEADER.
+ */
+ printf("arg {number=%u}{call=--iue}{display=Should undecidable events be included}"
+ "{type=boolflag}{default=false}{tooltip=Choose if the undecidable event is included}{group=Capture}\n",
+ inc++);
+ printf("arg {number=%u}{call=--etlfile}{display=etl file}"
+ "{type=fileselect}{tooltip=Select etl file to display in Wireshark}{required=true}{group=Capture}\n",
+ inc++);
+ /* Saved for later live capture support */
+#if 0
+ printf("arg {number=%u}{call=--session-params}{display=Live session parameters}"
+ "{type=string}{tooltip=providers, keyword and level}{group=Capture}\n",
+ inc++);
+#endif
+
+ extcap_config_debug(&inc);
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char* argv[])
+{
+ char* err_msg;
+ int option_idx = 0;
+ int result;
+ int ret = EXIT_FAILURE;
+
+ char* etlfile = NULL;
+
+ extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1);
+ char* help_url;
+ char* help_header = NULL;
+
+ /*
+ * Get credential information for later use.
+ */
+ init_process_policies();
+
+ /*
+ * Attempt to get the pathname of the directory containing the
+ * executable file.
+ */
+ err_msg = init_progfile_dir(argv[0]);
+ if (err_msg != NULL) {
+ g_warning("Can't get pathname of directory containing the captype program: %s.",
+ err_msg);
+ g_free(err_msg);
+ }
+
+ help_url = data_file_url("etwdump.html");
+ extcap_base_set_util_info(extcap_conf, argv[0], ETWDUMP_VERSION_MAJOR, ETWDUMP_VERSION_MINOR,
+ ETWDUMP_VERSION_RELEASE, help_url);
+ g_free(help_url);
+ extcap_base_register_interface(extcap_conf, ETW_EXTCAP_INTERFACE, "ETW reader", 290, "DLT_ETW");
+
+ help_header = g_strdup_printf(
+ " %s --extcap-interfaces\n"
+ " %s --extcap-interface=%s --extcap-dlts\n"
+ " %s --extcap-interface=%s --extcap-config\n"
+ " %s --extcap-interface=%s --etlfile c:\\wwansvc.etl \n"
+ "--fifo=FILENAME --capture\n", argv[0], argv[0], ETW_EXTCAP_INTERFACE, argv[0], ETW_EXTCAP_INTERFACE,
+ argv[0], ETW_EXTCAP_INTERFACE);
+ extcap_help_add_header(extcap_conf, help_header);
+ g_free(help_header);
+
+ extcap_help_add_option(extcap_conf, "--help", "print this help");
+ extcap_help_add_option(extcap_conf, "--version", "print the version");
+ extcap_help_add_option(extcap_conf, "--etlfile <filename>", "A etl filename");
+ extcap_help_add_option(extcap_conf, "--iue", "Choose if undecidable event is included");
+
+ if (argc == 1) {
+ help(extcap_conf);
+ goto end;
+ }
+
+ while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
+ switch (result) {
+ case OPT_VERSION:
+ extcap_version_print(extcap_conf);
+ ret = EXIT_SUCCESS;
+ goto end;
+
+ case OPT_HELP:
+ help(extcap_conf);
+ ret = EXIT_SUCCESS;
+ goto end;
+
+ case OPT_ETLFILE:
+ etlfile = g_strdup(optarg);
+ break;
+
+ case OPT_INCLUDE_UNDECIDABLE_EVENT:
+ g_include_undecidable_event = TRUE;
+ break;
+
+ case ':':
+ /* missing option argument */
+ g_warning("Option '%s' requires an argument", argv[optind - 1]);
+ break;
+
+ default:
+ /* Handle extcap specific options */
+ if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg))
+ {
+ g_warning("Invalid option: %s", argv[optind - 1]);
+ goto end;
+ }
+ }
+ }
+
+ extcap_cmdline_debug(argv, argc);
+
+ if (extcap_base_handle_interface(extcap_conf)) {
+ ret = EXIT_SUCCESS;
+ goto end;
+ }
+
+ if (extcap_conf->show_config) {
+ ret = list_config(extcap_conf->interface);
+ goto end;
+ }
+
+ if (extcap_conf->capture) {
+
+ if (g_strcmp0(extcap_conf->interface, ETW_EXTCAP_INTERFACE)) {
+ g_warning("ERROR: invalid interface");
+ goto end;
+ }
+
+ wtap_init(FALSE);
+
+ switch(etw_dump(etlfile, extcap_conf->fifo, &ret, &err_msg))
+ {
+ case WTAP_OPEN_ERROR:
+ if (err_msg != NULL) {
+ g_warning("etw_dump failed: %s.",
+ err_msg);
+ g_free(err_msg);
+ }
+ else
+ {
+ g_warning("etw_dump failed");
+ }
+ break;
+ case WTAP_OPEN_NOT_MINE:
+ if (err_msg != NULL) {
+ g_warning("The file %s is not etl format. Error message: %s.",
+ etlfile, err_msg);
+ g_free(err_msg);
+ }
+ else
+ {
+ g_warning("The file %s is not etl format");
+ }
+ break;
+ case WTAP_OPEN_MINE:
+ ret = EXIT_SUCCESS;
+ break;
+ }
+ }
+
+end:
+ /* clean up stuff */
+ extcap_base_cleanup(&extcap_conf);
+
+ if (etlfile != NULL)
+ {
+ g_free(etlfile);
+ }
+
+ return ret;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */